ruflet_server 0.0.6 → 0.0.8

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: 2f889edb097984732cc8ccc1927674aa4f4b6fbd0b218b17cf352365100f4e9c
4
- data.tar.gz: 37707b6486c6e6fb50ac40011ab3c80a6d667150957f3d07daf6aaeb649236e5
3
+ metadata.gz: f0ec355d06e9fb730a851fa6b085bc97b74bd1231aa682bfc03a93ed3078f762
4
+ data.tar.gz: 2c3af4192affd015f59bfc0a49dc4fe3af264f1da38f5914c78af3d8602ace57
5
5
  SHA512:
6
- metadata.gz: ccb7dcf7dcbc3ab808b8bc300ffbc1da83b687256c9f892b664ce7b5e815b434c5b6a022c968b10fba3dc88ee5ee18ee7f1aed54ba55b9ea939e8f4804b04f69
7
- data.tar.gz: 1d4cf5d940ab17a9681da6aa853303df9fbf4ee83d90264e7bbfd654c65757dd6144bc9aa46c91ffc1422b7981904048351f7f211c9d7d8501d4a91bf0595aa1
6
+ metadata.gz: 250363621bf1bfaefb04a2a2a89f30336590272250239c21061d96c60cac1ae5464f5a5442a42fab0f415bea3085a5921be8bb073e59d7d968b5bc537635eac9
7
+ data.tar.gz: 14d25116e02035b24646f73577aa3780b5f990473191725636324d26c53cccd3a65da53d84db3d2534a4a60450aac5ce7c79799f70b44703db271819c8f8f687
@@ -2,6 +2,9 @@
2
2
 
3
3
  module Ruflet
4
4
  class WebSocketConnection
5
+ # Ruflet control messages are small; anything much larger is invalid or hostile.
6
+ MAX_FRAME_PAYLOAD_BYTES = 16 * 1024 * 1024
7
+
5
8
  def initialize(socket)
6
9
  @socket = socket
7
10
  @write_mutex = Mutex.new
@@ -70,10 +73,20 @@ module Ruflet
70
73
  masked = (b2 & 0x80) != 0
71
74
  payload_len = b2 & 0x7f
72
75
 
73
- payload_len = read_exact(2).unpack1("n") if payload_len == 126
74
- payload_len = read_exact(8).unpack1("Q>") if payload_len == 127
76
+ if payload_len == 126
77
+ ext = read_exact(2)
78
+ return nil if ext.nil?
79
+ payload_len = ext.unpack1("n")
80
+ elsif payload_len == 127
81
+ ext = read_exact(8)
82
+ return nil if ext.nil?
83
+ payload_len = ext.unpack1("Q>")
84
+ end
85
+
86
+ return nil if payload_len.negative? || payload_len > MAX_FRAME_PAYLOAD_BYTES
75
87
 
76
88
  masking_key = masked ? read_exact(4) : nil
89
+ return nil if masked && masking_key.nil?
77
90
  payload = payload_len.zero? ? "".b : read_exact(payload_len)
78
91
  return nil if payload.nil?
79
92
 
@@ -112,6 +125,9 @@ module Ruflet
112
125
  end
113
126
 
114
127
  def read_exact(length)
128
+ return nil unless length.is_a?(Integer)
129
+ return nil if length.negative? || length > MAX_FRAME_PAYLOAD_BYTES
130
+
115
131
  chunk = +""
116
132
  chunk.force_encoding(Encoding::BINARY)
117
133
 
@@ -123,6 +139,8 @@ module Ruflet
123
139
  end
124
140
 
125
141
  chunk
142
+ rescue IOError, SystemCallError
143
+ nil
126
144
  end
127
145
  end
128
146
  end
@@ -128,10 +128,23 @@ module Ruflet
128
128
  when 0xd9 then reader.read_string(reader.read_u8)
129
129
  when 0xda then reader.read_string(reader.read_u16)
130
130
  when 0xdb then reader.read_string(reader.read_u32)
131
+ when 0xc4 then reader.read_binary(reader.read_u8)
132
+ when 0xc5 then reader.read_binary(reader.read_u16)
133
+ when 0xc6 then reader.read_binary(reader.read_u32)
131
134
  when 0xdc then read_array(reader, reader.read_u16)
132
135
  when 0xdd then read_array(reader, reader.read_u32)
133
136
  when 0xde then read_map(reader, reader.read_u16)
134
137
  when 0xdf then read_map(reader, reader.read_u32)
138
+ when 0xd4
139
+ read_ext(reader, 1)
140
+ when 0xd5
141
+ read_ext(reader, 2)
142
+ when 0xd6
143
+ read_ext(reader, 4)
144
+ when 0xd7
145
+ read_ext(reader, 8)
146
+ when 0xd8
147
+ read_ext(reader, 16)
135
148
  when 0xc7
136
149
  read_ext(reader, reader.read_u8)
137
150
  when 0xc8
@@ -231,6 +244,10 @@ module Ruflet
231
244
  def read_string(size)
232
245
  read_exact(size).force_encoding("UTF-8")
233
246
  end
247
+
248
+ def read_binary(size)
249
+ read_exact(size)
250
+ end
234
251
  end
235
252
  end
236
253
  end
data/lib/ruflet/server.rb CHANGED
@@ -96,6 +96,33 @@ module Ruflet
96
96
  end
97
97
  end
98
98
 
99
+ def reload_app!
100
+ snapshots = @sessions_mutex.synchronize { @sessions.to_a }
101
+
102
+ snapshots.each do |session_key, current_page|
103
+ ws = @connections_mutex.synchronize { @connections[session_key] }
104
+ next unless ws
105
+
106
+ refreshed_page = Page.new(
107
+ session_id: current_page.session_id,
108
+ client_details: current_page.client_details,
109
+ sender: lambda do |action, payload|
110
+ send_message(ws, action, payload)
111
+ end
112
+ )
113
+ refreshed_page.title = "Ruflet App"
114
+
115
+ @sessions_mutex.synchronize do
116
+ @sessions[session_key] = refreshed_page
117
+ end
118
+
119
+ @app_block.call(refreshed_page)
120
+ refreshed_page.update
121
+ rescue StandardError => e
122
+ warn "reload error: #{e.class}: #{e.message}"
123
+ end
124
+ end
125
+
99
126
  private
100
127
 
101
128
  def trap_stop_signals
@@ -159,6 +186,8 @@ module Ruflet
159
186
  ws = nil
160
187
  begin
161
188
  path, headers = read_http_upgrade_request(socket)
189
+ return if path.nil?
190
+
162
191
  if websocket_upgrade_request?(path, headers)
163
192
  send_handshake_response(socket, headers["sec-websocket-key"])
164
193
  ws = Ruflet::WebSocketConnection.new(socket)
@@ -167,6 +196,8 @@ module Ruflet
167
196
  handle_http_request(socket, path)
168
197
  end
169
198
  rescue StandardError => e
199
+ return if disconnect_error?(e)
200
+
170
201
  warn "server error: #{e.class}: #{e.message}"
171
202
  warn e.backtrace.join("\n") if e.backtrace
172
203
  send_message(ws, Protocol::ACTIONS[:session_crashed], { "message" => e.message.to_s.dup.force_encoding("UTF-8") }) if ws
@@ -182,6 +213,8 @@ module Ruflet
182
213
  handle_message(ws, raw)
183
214
  end
184
215
  rescue StandardError => e
216
+ return if disconnect_error?(e)
217
+
185
218
  warn "server error: #{e.class}: #{e.message}"
186
219
  warn e.backtrace.join("\n") if e.backtrace
187
220
  send_message(ws, Protocol::ACTIONS[:session_crashed], { "message" => e.message.to_s.dup.force_encoding("UTF-8") })
@@ -197,10 +230,12 @@ module Ruflet
197
230
 
198
231
  def read_http_upgrade_request(socket)
199
232
  request_line = socket.gets("\r\n")
200
- raise "Invalid HTTP request" if request_line.nil?
233
+ return [nil, {}] if request_line.nil?
234
+ return [nil, {}] unless request_line.include?(" ")
201
235
 
202
236
  method, path, _version = request_line.strip.split(" ", 3)
203
- raise "Unsupported HTTP method: #{method}" unless method == "GET"
237
+ return [nil, {}] unless method == "GET"
238
+ return [nil, {}] if path.to_s.empty?
204
239
 
205
240
  headers = {}
206
241
  loop do
@@ -438,9 +473,9 @@ module Ruflet
438
473
  raise
439
474
  end
440
475
 
441
- def on_invoke_control_method(_ws, _payload)
442
- # Client response to invoke_control_method; no server-side handling yet.
443
- nil
476
+ def on_invoke_control_method(ws, payload)
477
+ page = fetch_page(ws)
478
+ page.handle_invoke_method_result(payload)
444
479
  end
445
480
 
446
481
  def on_control_event(ws, payload)
@@ -482,10 +517,30 @@ module Ruflet
482
517
  end
483
518
 
484
519
  def send_message(ws, action, payload)
520
+ return if ws.nil? || ws.closed?
521
+
485
522
  message = [action, payload]
486
523
  ws.send_binary(Ruflet::WireCodec.pack(message))
487
524
  rescue StandardError => e
488
- warn "send error: #{e.class}: #{e.message}"
525
+ unless disconnect_error?(e)
526
+ warn "send error: #{e.class}: #{e.message}"
527
+ end
528
+ remove_session(ws)
529
+ unregister_connection(ws)
530
+ ws&.close
531
+ end
532
+
533
+ def disconnect_error?(error)
534
+ return true if error.is_a?(IOError)
535
+ return true if error.is_a?(Errno::EPIPE)
536
+ return true if error.is_a?(Errno::ECONNRESET)
537
+ return true if error.is_a?(Errno::ECONNABORTED)
538
+ return true if error.is_a?(Errno::ENOTCONN)
539
+ return true if error.is_a?(Errno::ESHUTDOWN)
540
+ return true if error.is_a?(Errno::EBADF)
541
+ return true if error.is_a?(Errno::EINVAL)
542
+
543
+ false
489
544
  end
490
545
 
491
546
  def pseudo_uuid
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ruflet
4
- VERSION = "0.0.6" unless const_defined?(:VERSION)
4
+ VERSION = "0.0.8" unless const_defined?(:VERSION)
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruflet_server
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - AdamMusa
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.0.6
18
+ version: 0.0.8
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 0.0.6
25
+ version: 0.0.8
26
26
  description: Ruflet WebSocket server runtime compatible with Flet protocol.
27
27
  email:
28
28
  - adammusa2222@gmail.com