whoosh 1.4.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf87392b23cb5b6a3cdc3fc27c06df4d62d7dd3b780e8695f9479cf869a0922a
4
- data.tar.gz: 249803c4606645c7032deae70836954c3f1255ed5c9bf44342d07cf50afb2214
3
+ metadata.gz: 5baff744454d485e7f9788aca18285ece0c8901a5abea6f9c2f3fd4a22a7de75
4
+ data.tar.gz: 6c2062d73b6bec5e60c0adfd897e7e8aec5d10fa366439471592fc196bfe5bbd
5
5
  SHA512:
6
- metadata.gz: c871107c868e72d7f27596a48719dd752155d91ff114937b7448a7474e1a0ac6bdfa39dec8db23fe857e9728e3e2a0db3517013a70f820590386ebc7876b2707
7
- data.tar.gz: 6ebad6447841c6587c0b3853fdb4f2666680db1e6e1eaf01a1fb1edb854af0a13ebd5e5a205b5866686346caf747a0aa49cbba30b2587afec681dc409829de23
6
+ metadata.gz: 7ef32b36e7405fe117eefc60908baf71a9553d328ccd2fd7823c715b7e37d4f01aeedc29a7cb1709e865410d407a32bc511f49353b90339702a06bb7f9a31425
7
+ data.tar.gz: 75188ded90b9205cb7c779d18252ef842989ec84d76ad78e1167d4ca267e544918b39c5d2a3596abe0b2d6001620e98e4272a75bdd62491b4130127bba78487c
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
- require "faye/websocket"
5
4
 
6
5
  module Whoosh
7
6
  module Streaming
@@ -19,7 +18,8 @@ module Whoosh
19
18
 
20
19
  # Check if the request is a WebSocket upgrade
21
20
  def self.websocket?(env)
22
- Faye::WebSocket.websocket?(env)
21
+ upgrade = env["HTTP_UPGRADE"]
22
+ upgrade && upgrade.downcase == "websocket"
23
23
  end
24
24
 
25
25
  # Register callbacks
@@ -46,18 +46,47 @@ module Whoosh
46
46
  return if @closed
47
47
  @closed = true
48
48
  @ws&.close(code || 1000, reason || "")
49
+ rescue
50
+ # Already closed
49
51
  end
50
52
 
51
53
  def closed?
52
54
  @closed
53
55
  end
54
56
 
55
- # Returns a Rack response — hijacks the connection via faye-websocket
57
+ # Returns a Rack response — auto-detects Faye (Puma) or Async (Falcon)
56
58
  def rack_response
57
59
  unless self.class.websocket?(@env)
58
60
  return [400, { "content-type" => "text/plain" }, ["Not a WebSocket request"]]
59
61
  end
60
62
 
63
+ if async_websocket_available? && falcon_env?
64
+ rack_response_async
65
+ else
66
+ rack_response_faye
67
+ end
68
+ end
69
+
70
+ # For testing without real socket
71
+ def trigger_message(msg)
72
+ @on_message&.call(msg)
73
+ end
74
+
75
+ def trigger_close(code = 1000, reason = "")
76
+ @on_close&.call(code, reason)
77
+ @closed = true
78
+ end
79
+
80
+ def trigger_open
81
+ @on_open&.call
82
+ end
83
+
84
+ private
85
+
86
+ # Faye WebSocket — works with Puma (threads + EventMachine)
87
+ def rack_response_faye
88
+ require "faye/websocket"
89
+
61
90
  @ws = Faye::WebSocket.new(@env)
62
91
 
63
92
  @ws.on :open do |_event|
@@ -77,18 +106,52 @@ module Whoosh
77
106
  @ws.rack_response
78
107
  end
79
108
 
80
- # For testing without real socket
81
- def trigger_message(msg)
82
- @on_message&.call(msg)
109
+ # Async WebSocket works with Falcon (fibers + async)
110
+ def rack_response_async
111
+ require "async/websocket/adapters/rack"
112
+
113
+ Async::WebSocket::Adapters::Rack.open(@env, protocols: ["ws"]) do |connection|
114
+ @ws = AsyncWSWrapper.new(connection)
115
+ @on_open&.call
116
+
117
+ while (message = connection.read)
118
+ @on_message&.call(message.to_str)
119
+ end
120
+ rescue EOFError, Protocol::WebSocket::ClosedError
121
+ # Client disconnected
122
+ ensure
123
+ @closed = true
124
+ @on_close&.call(1000, "")
125
+ @ws = nil
126
+ end
83
127
  end
84
128
 
85
- def trigger_close(code = 1000, reason = "")
86
- @on_close&.call(code, reason)
87
- @closed = true
129
+ def falcon_env?
130
+ # Falcon sets async.* keys in the env
131
+ @env.key?("async.reactor") || @env.key?("protocol.http.request")
88
132
  end
89
133
 
90
- def trigger_open
91
- @on_open&.call
134
+ def async_websocket_available?
135
+ require "async/websocket/adapters/rack"
136
+ true
137
+ rescue LoadError
138
+ false
139
+ end
140
+
141
+ # Wrapper to give async-websocket the same #send interface
142
+ class AsyncWSWrapper
143
+ def initialize(connection)
144
+ @connection = connection
145
+ end
146
+
147
+ def send(data)
148
+ @connection.write(Protocol::WebSocket::TextMessage.generate(data))
149
+ @connection.flush
150
+ end
151
+
152
+ def close(code = 1000, reason = "")
153
+ @connection.close
154
+ end
92
155
  end
93
156
  end
94
157
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Whoosh
4
- VERSION = "1.4.0"
4
+ VERSION = "1.4.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: whoosh
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Johannes Dwi Cahyo