summaryrefslogtreecommitdiff
path: root/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket')
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/close03.rb14
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/close05.rb14
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/close06.rb19
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/close75.rb11
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/connection.rb349
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/debugger.rb17
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing03.rb162
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing04.rb15
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing05.rb163
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing07.rb185
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing76.rb105
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler.rb97
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler03.rb9
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler05.rb9
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler06.rb9
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler07.rb9
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler08.rb9
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler13.rb9
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler75.rb9
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler76.rb14
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handshake.rb156
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handshake04.rb37
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handshake75.rb28
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handshake76.rb72
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/masking04.rb37
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/message_processor_03.rb47
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/message_processor_06.rb78
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/version.rb5
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/websocket.rb56
29 files changed, 1744 insertions, 0 deletions
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/close03.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/close03.rb
new file mode 100644
index 0000000..8981776
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/close03.rb
@@ -0,0 +1,14 @@
+module EventMachine
+ module WebSocket
+ module Close03
+ def close_websocket(code, body)
+ # TODO: Ideally send body data and check that it matches in ack
+ send_frame(:close, '')
+ @state = :closing
+ start_close_timeout
+ end
+
+ def supports_close_codes?; false; end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/close05.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/close05.rb
new file mode 100644
index 0000000..8f8c150
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/close05.rb
@@ -0,0 +1,14 @@
+module EventMachine
+ module WebSocket
+ module Close05
+ def close_websocket(code, body)
+ # TODO: Ideally send body data and check that it matches in ack
+ send_frame(:close, "\x53")
+ @state = :closing
+ start_close_timeout
+ end
+
+ def supports_close_codes?; false; end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/close06.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/close06.rb
new file mode 100644
index 0000000..14cc80e
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/close06.rb
@@ -0,0 +1,19 @@
+module EventMachine
+ module WebSocket
+ module Close06
+ def close_websocket(code, body)
+ if code
+ close_data = [code].pack('n')
+ close_data << body if body
+ send_frame(:close, close_data)
+ else
+ send_frame(:close, '')
+ end
+ @state = :closing
+ start_close_timeout
+ end
+
+ def supports_close_codes?; true; end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/close75.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/close75.rb
new file mode 100644
index 0000000..9d6ba02
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/close75.rb
@@ -0,0 +1,11 @@
+module EventMachine
+ module WebSocket
+ module Close75
+ def close_websocket(code, body)
+ @connection.close_connection_after_writing
+ end
+
+ def supports_close_codes?; false; end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/connection.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/connection.rb
new file mode 100644
index 0000000..a55881d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/connection.rb
@@ -0,0 +1,349 @@
+module EventMachine
+ module WebSocket
+ class Connection < EventMachine::Connection
+ include Debugger
+
+ attr_writer :max_frame_size
+
+ # define WebSocket callbacks
+ def onopen(&blk); @onopen = blk; end
+ def onclose(&blk); @onclose = blk; end
+ def onerror(&blk); @onerror = blk; end
+ def onmessage(&blk); @onmessage = blk; end
+ def onbinary(&blk); @onbinary = blk; end
+ def onping(&blk); @onping = blk; end
+ def onpong(&blk); @onpong = blk; end
+
+ def trigger_on_message(msg)
+ @onmessage.call(msg) if defined? @onmessage
+ end
+ def trigger_on_binary(msg)
+ @onbinary.call(msg) if defined? @onbinary
+ end
+ def trigger_on_open(handshake)
+ @onopen.call(handshake) if defined? @onopen
+ end
+ def trigger_on_close(event = {})
+ @onclose.call(event) if defined? @onclose
+ end
+ def trigger_on_ping(data)
+ @onping.call(data) if defined? @onping
+ end
+ def trigger_on_pong(data)
+ @onpong.call(data) if defined? @onpong
+ end
+ def trigger_on_error(reason)
+ return false unless defined? @onerror
+ @onerror.call(reason)
+ true
+ end
+
+ def initialize(options)
+ @options = options
+ @debug = options[:debug] || false
+ @secure = options[:secure] || false
+ @secure_proxy = options[:secure_proxy] || false
+ @tls_options = options[:tls_options] || {}
+ @close_timeout = options[:close_timeout]
+ @outbound_limit = options[:outbound_limit] || 0
+
+ @handler = nil
+
+ debug [:initialize]
+ end
+
+ # Use this method to close the websocket connection cleanly
+ # This sends a close frame and waits for acknowlegement before closing
+ # the connection
+ def close(code = nil, body = nil)
+ if code && !acceptable_close_code?(code)
+ raise "Application code may only use codes from 1000, 3000-4999"
+ end
+
+ close_websocket_private(code, body)
+ end
+
+ # Deprecated, to be removed in version 0.6
+ alias :close_websocket :close
+
+ def post_init
+ start_tls(@tls_options) if @secure
+ end
+
+ def receive_data(data)
+ debug [:receive_data, data]
+
+ if @handler
+ @handler.receive_data(data)
+ else
+ dispatch(data)
+ end
+ rescue => e
+ debug [:error, e]
+
+ # There is no code defined for application errors, so use 3000
+ # (which is reserved for frameworks)
+ close_websocket_private(3000, "Application error")
+
+ # These are application errors - raise unless onerror defined
+ trigger_on_error(e) || raise(e)
+ end
+
+ def send_data(data)
+ if @outbound_limit > 0 &&
+ get_outbound_data_size + data.bytesize > @outbound_limit
+ abort(:outbound_limit_reached)
+ return 0
+ end
+
+ super(data)
+ end
+
+ def unbind
+ debug [:unbind, :connection]
+
+ @handler.unbind if @handler
+ rescue => e
+ debug [:error, e]
+ # These are application errors - raise unless onerror defined
+ trigger_on_error(e) || raise(e)
+ end
+
+ def dispatch(data)
+ if data.match(%r|^GET /healthcheck|)
+ send_healthcheck_response
+ elsif data.match(/\A<policy-file-request\s*\/>/)
+ send_flash_cross_domain_file
+ else
+ @handshake ||= begin
+ handshake = Handshake.new(@secure || @secure_proxy)
+
+ handshake.callback { |upgrade_response, handler_klass|
+ debug [:accepting_ws_version, handshake.protocol_version]
+ debug [:upgrade_response, upgrade_response]
+ self.send_data(upgrade_response)
+ @handler = handler_klass.new(self, @debug)
+ @handshake = nil
+ trigger_on_open(handshake)
+ }
+
+ handshake.errback { |e|
+ debug [:error, e]
+ trigger_on_error(e)
+ # Handshake errors require the connection to be aborted
+ abort(:handshake_error)
+ }
+
+ handshake
+ end
+
+ @handshake.receive_data(data)
+ end
+ end
+
+ def send_healthcheck_response
+ debug [:healthcheck, 'OK']
+
+ healthcheck_res = ["HTTP/1.1 200 OK"]
+ healthcheck_res << "Content-Type: text/plain"
+ healthcheck_res << "Content-Length: 2"
+
+ healthcheck_res = healthcheck_res.join("\r\n") + "\r\n\r\nOK"
+
+ send_data healthcheck_res
+
+ # handle the healthcheck request transparently
+ # no need to notify the user about this connection
+ @onclose = nil
+ close_connection_after_writing
+ end
+
+ def send_flash_cross_domain_file
+ file = '<?xml version="1.0"?><cross-domain-policy><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>'
+ debug [:cross_domain, file]
+ send_data file
+
+ # handle the cross-domain request transparently
+ # no need to notify the user about this connection
+ @onclose = nil
+ close_connection_after_writing
+ end
+
+ # Cache encodings since it's moderately expensive to look them up each time
+ ENCODING_SUPPORTED = "string".respond_to?(:force_encoding)
+ UTF8 = Encoding.find("UTF-8") if ENCODING_SUPPORTED
+ BINARY = Encoding.find("BINARY") if ENCODING_SUPPORTED
+
+ # Send a WebSocket text frame.
+ #
+ # A WebSocketError may be raised if the connection is in an opening or a
+ # closing state, or if the passed in data is not valid UTF-8
+ #
+ def send_text(data)
+ # If we're using Ruby 1.9, be pedantic about encodings
+ if ENCODING_SUPPORTED
+ # Also accept ascii only data in other encodings for convenience
+ unless (data.encoding == UTF8 && data.valid_encoding?) || data.ascii_only?
+ raise WebSocketError, "Data sent to WebSocket must be valid UTF-8 but was #{data.encoding} (valid: #{data.valid_encoding?})"
+ end
+ # This labels the encoding as binary so that it can be combined with
+ # the BINARY framing
+ data.force_encoding(BINARY)
+ else
+ # TODO: Check that data is valid UTF-8
+ end
+
+ if @handler
+ @handler.send_text_frame(data)
+ else
+ raise WebSocketError, "Cannot send data before onopen callback"
+ end
+
+ # Revert data back to the original encoding (which we assume is UTF-8)
+ # Doing this to avoid duping the string - there may be a better way
+ data.force_encoding(UTF8) if ENCODING_SUPPORTED
+ return nil
+ end
+
+ alias :send :send_text
+
+ # Send a WebSocket binary frame.
+ #
+ def send_binary(data)
+ if @handler
+ @handler.send_frame(:binary, data)
+ else
+ raise WebSocketError, "Cannot send binary before onopen callback"
+ end
+ end
+
+ # Send a ping to the client. The client must respond with a pong.
+ #
+ # In the case that the client is running a WebSocket draft < 01, false
+ # is returned since ping & pong are not supported
+ #
+ def ping(body = '')
+ if @handler
+ @handler.pingable? ? @handler.send_frame(:ping, body) && true : false
+ else
+ raise WebSocketError, "Cannot ping before onopen callback"
+ end
+ end
+
+ # Send an unsolicited pong message, as allowed by the protocol. The
+ # client is not expected to respond to this message.
+ #
+ # em-websocket automatically takes care of sending pong replies to
+ # incoming ping messages, as the protocol demands.
+ #
+ def pong(body = '')
+ if @handler
+ @handler.pingable? ? @handler.send_frame(:pong, body) && true : false
+ else
+ raise WebSocketError, "Cannot ping before onopen callback"
+ end
+ end
+
+ # Test whether the connection is pingable (i.e. the WebSocket draft in
+ # use is >= 01)
+ def pingable?
+ if @handler
+ @handler.pingable?
+ else
+ raise WebSocketError, "Cannot test whether pingable before onopen callback"
+ end
+ end
+
+ def supports_close_codes?
+ if @handler
+ @handler.supports_close_codes?
+ else
+ raise WebSocketError, "Cannot test before onopen callback"
+ end
+ end
+
+ def state
+ @handler ? @handler.state : :handshake
+ end
+
+ # Returns the IP address for the remote peer
+ def remote_ip
+ get_peername[2,6].unpack('nC4')[1..4].join('.')
+ end
+
+ # Returns the maximum frame size which this connection is configured to
+ # accept. This can be set globally or on a per connection basis, and
+ # defaults to a value of 10MB if not set.
+ #
+ # The behaviour when a too large frame is received varies by protocol,
+ # but in the newest protocols the connection will be closed with the
+ # correct close code (1009) immediately after receiving the frame header
+ #
+ def max_frame_size
+ defined?(@max_frame_size) ? @max_frame_size : WebSocket.max_frame_size
+ end
+
+ def close_timeout
+ @close_timeout || WebSocket.close_timeout
+ end
+
+ private
+
+ # As definited in draft 06 7.2.2, some failures require that the server
+ # abort the websocket connection rather than close cleanly
+ def abort(reason)
+ debug [:abort, reason]
+ close_connection
+ end
+
+ def close_websocket_private(code, body)
+ if @handler
+ debug [:closing, code]
+ @handler.close_websocket(code, body)
+ else
+ # The handshake hasn't completed - should be safe to terminate
+ abort(:handshake_incomplete)
+ end
+ end
+
+ # Allow applications to close with 1000, 1003, 1008, 1011, 3xxx or 4xxx.
+ #
+ # em-websocket uses a few other codes internally which should not be
+ # used by applications
+ #
+ # Browsers generally allow connections to be closed with code 1000,
+ # 3xxx, and 4xxx. em-websocket allows closing with a few other codes
+ # which seem reasonable (for discussion see
+ # https://github.com/igrigorik/em-websocket/issues/98)
+ #
+ # Usage from the rfc:
+ #
+ # 1000 indicates a normal closure
+ #
+ # 1003 indicates that an endpoint is terminating the connection
+ # because it has received a type of data it cannot accept
+ #
+ # 1008 indicates that an endpoint is terminating the connection because
+ # it has received a message that violates its policy
+ #
+ # 1011 indicates that a server is terminating the connection because it
+ # encountered an unexpected condition that prevented it from fulfilling
+ # the request
+ #
+ # Status codes in the range 3000-3999 are reserved for use by libraries,
+ # frameworks, and applications
+ #
+ # Status codes in the range 4000-4999 are reserved for private use and
+ # thus can't be registered
+ #
+ def acceptable_close_code?(code)
+ case code
+ when 1000, 1003, 1008, 1011, (3000..4999)
+ true
+ else
+ false
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/debugger.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/debugger.rb
new file mode 100644
index 0000000..a5b3266
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/debugger.rb
@@ -0,0 +1,17 @@
+module EventMachine
+ module WebSocket
+ module Debugger
+
+ private
+
+ def debug(*data)
+ if @debug
+ require 'pp'
+ pp data
+ puts
+ end
+ end
+
+ end
+ end
+end \ No newline at end of file
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing03.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing03.rb
new file mode 100644
index 0000000..61e6ab8
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing03.rb
@@ -0,0 +1,162 @@
+# encoding: BINARY
+
+module EventMachine
+ module WebSocket
+ module Framing03
+ def initialize_framing
+ @data = ''
+ @application_data_buffer = '' # Used for MORE frames
+ @frame_type = nil
+ end
+
+ def process_data
+ error = false
+
+ while !error && @data.size > 1
+ pointer = 0
+
+ more = ((@data.getbyte(pointer) & 0b10000000) == 0b10000000) ^ fin
+ # Ignoring rsv1-3 for now
+ opcode = @data.getbyte(0) & 0b00001111
+ pointer += 1
+
+ # Ignoring rsv4
+ length = @data.getbyte(pointer) & 0b01111111
+ pointer += 1
+
+ payload_length = case length
+ when 127 # Length defined by 8 bytes
+ # Check buffer size
+ if @data.getbyte(pointer+8-1) == nil
+ debug [:buffer_incomplete, @data]
+ error = true
+ next
+ end
+
+ # Only using the last 4 bytes for now, till I work out how to
+ # unpack 8 bytes. I'm sure 4GB frames will do for now :)
+ l = @data[(pointer+4)..(pointer+7)].unpack('N').first
+ pointer += 8
+ l
+ when 126 # Length defined by 2 bytes
+ # Check buffer size
+ if @data.getbyte(pointer+2-1) == nil
+ debug [:buffer_incomplete, @data]
+ error = true
+ next
+ end
+
+ l = @data[pointer..(pointer+1)].unpack('n').first
+ pointer += 2
+ l
+ else
+ length
+ end
+
+ if payload_length > @connection.max_frame_size
+ raise WSMessageTooBigError, "Frame length too long (#{payload_length} bytes)"
+ end
+
+ # Check buffer size
+ if @data.getbyte(pointer+payload_length-1) == nil
+ debug [:buffer_incomplete, @data]
+ error = true
+ next
+ end
+
+ # Throw away data up to pointer
+ @data.slice!(0...pointer)
+
+ # Read application data
+ application_data = @data.slice!(0...payload_length)
+
+ frame_type = opcode_to_type(opcode)
+
+ if frame_type == :continuation && !@frame_type
+ raise WSProtocolError, 'Continuation frame not expected'
+ end
+
+ if more
+ debug [:moreframe, frame_type, application_data]
+ @application_data_buffer << application_data
+ # The message type is passed in the first frame
+ @frame_type ||= frame_type
+ else
+ # Message is complete
+ if frame_type == :continuation
+ @application_data_buffer << application_data
+ message(@frame_type, '', @application_data_buffer)
+ @application_data_buffer = ''
+ @frame_type = nil
+ else
+ message(frame_type, '', application_data)
+ end
+ end
+ end # end while
+ end
+
+ def send_frame(frame_type, application_data)
+ debug [:sending_frame, frame_type, application_data]
+
+ if @state == :closing && data_frame?(frame_type)
+ raise WebSocketError, "Cannot send data frame since connection is closing"
+ end
+
+ frame = ''
+
+ opcode = type_to_opcode(frame_type)
+ byte1 = opcode # since more, rsv1-3 are 0
+ frame << byte1
+
+ length = application_data.size
+ if length <= 125
+ byte2 = length # since rsv4 is 0
+ frame << byte2
+ elsif length < 65536 # write 2 byte length
+ frame << 126
+ frame << [length].pack('n')
+ else # write 8 byte length
+ frame << 127
+ frame << [length >> 32, length & 0xFFFFFFFF].pack("NN")
+ end
+
+ frame << application_data
+
+ @connection.send_data(frame)
+ end
+
+ def send_text_frame(data)
+ send_frame(:text, data)
+ end
+
+ private
+
+ # This allows flipping the more bit to fin for draft 04
+ def fin; false; end
+
+ FRAME_TYPES = {
+ :continuation => 0,
+ :close => 1,
+ :ping => 2,
+ :pong => 3,
+ :text => 4,
+ :binary => 5
+ }
+ FRAME_TYPES_INVERSE = FRAME_TYPES.invert
+ # Frames are either data frames or control frames
+ DATA_FRAMES = [:text, :binary, :continuation]
+
+ def type_to_opcode(frame_type)
+ FRAME_TYPES[frame_type] || raise("Unknown frame type")
+ end
+
+ def opcode_to_type(opcode)
+ FRAME_TYPES_INVERSE[opcode] || raise(WSProtocolError, "Unknown opcode #{opcode}")
+ end
+
+ def data_frame?(type)
+ DATA_FRAMES.include?(type)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing04.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing04.rb
new file mode 100644
index 0000000..41352e6
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing04.rb
@@ -0,0 +1,15 @@
+# encoding: BINARY
+
+module EventMachine
+ module WebSocket
+ # The only difference between draft 03 framing and draft 04 framing is
+ # that the MORE bit has been changed to a FIN bit
+ module Framing04
+ include Framing03
+
+ private
+
+ def fin; true; end
+ end
+ end
+end \ No newline at end of file
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing05.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing05.rb
new file mode 100644
index 0000000..636336a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing05.rb
@@ -0,0 +1,163 @@
+# encoding: BINARY
+
+module EventMachine
+ module WebSocket
+ module Framing05
+ def initialize_framing
+ @data = MaskedString.new
+ @application_data_buffer = '' # Used for MORE frames
+ @frame_type = nil
+ end
+
+ def process_data
+ error = false
+
+ while !error && @data.size > 5 # mask plus first byte present
+ pointer = 0
+
+ @data.read_mask
+ pointer += 4
+
+ fin = (@data.getbyte(pointer) & 0b10000000) == 0b10000000
+ # Ignoring rsv1-3 for now
+ opcode = @data.getbyte(pointer) & 0b00001111
+ pointer += 1
+
+ # Ignoring rsv4
+ length = @data.getbyte(pointer) & 0b01111111
+ pointer += 1
+
+ payload_length = case length
+ when 127 # Length defined by 8 bytes
+ # Check buffer size
+ if @data.getbyte(pointer+8-1) == nil
+ debug [:buffer_incomplete, @data]
+ error = true
+ next
+ end
+
+ # Only using the last 4 bytes for now, till I work out how to
+ # unpack 8 bytes. I'm sure 4GB frames will do for now :)
+ l = @data.getbytes(pointer+4, 4).unpack('N').first
+ pointer += 8
+ l
+ when 126 # Length defined by 2 bytes
+ # Check buffer size
+ if @data.getbyte(pointer+2-1) == nil
+ debug [:buffer_incomplete, @data]
+ error = true
+ next
+ end
+
+ l = @data.getbytes(pointer, 2).unpack('n').first
+ pointer += 2
+ l
+ else
+ length
+ end
+
+ if payload_length > @connection.max_frame_size
+ raise WSMessageTooBigError, "Frame length too long (#{payload_length} bytes)"
+ end
+
+ # Check buffer size
+ if @data.getbyte(pointer+payload_length-1) == nil
+ debug [:buffer_incomplete, @data]
+ error = true
+ next
+ end
+
+ # Read application data
+ application_data = @data.getbytes(pointer, payload_length)
+ pointer += payload_length
+
+ # Throw away data up to pointer
+ @data.unset_mask
+ @data.slice!(0...pointer)
+
+ frame_type = opcode_to_type(opcode)
+
+ if frame_type == :continuation && !@frame_type
+ raise WSProtocolError, 'Continuation frame not expected'
+ end
+
+ if !fin
+ debug [:moreframe, frame_type, application_data]
+ @application_data_buffer << application_data
+ @frame_type = frame_type
+ else
+ # Message is complete
+ if frame_type == :continuation
+ @application_data_buffer << application_data
+ message(@frame_type, '', @application_data_buffer)
+ @application_data_buffer = ''
+ @frame_type = nil
+ else
+ message(frame_type, '', application_data)
+ end
+ end
+ end # end while
+ end
+
+ def send_frame(frame_type, application_data)
+ debug [:sending_frame, frame_type, application_data]
+
+ if @state == :closing && data_frame?(frame_type)
+ raise WebSocketError, "Cannot send data frame since connection is closing"
+ end
+
+ frame = ''
+
+ opcode = type_to_opcode(frame_type)
+ byte1 = opcode | 0b10000000 # fin bit set, rsv1-3 are 0
+ frame << byte1
+
+ length = application_data.size
+ if length <= 125
+ byte2 = length # since rsv4 is 0
+ frame << byte2
+ elsif length < 65536 # write 2 byte length
+ frame << 126
+ frame << [length].pack('n')
+ else # write 8 byte length
+ frame << 127
+ frame << [length >> 32, length & 0xFFFFFFFF].pack("NN")
+ end
+
+ frame << application_data
+
+ @connection.send_data(frame)
+ end
+
+ def send_text_frame(data)
+ send_frame(:text, data)
+ end
+
+ private
+
+ FRAME_TYPES = {
+ :continuation => 0,
+ :close => 1,
+ :ping => 2,
+ :pong => 3,
+ :text => 4,
+ :binary => 5
+ }
+ FRAME_TYPES_INVERSE = FRAME_TYPES.invert
+ # Frames are either data frames or control frames
+ DATA_FRAMES = [:text, :binary, :continuation]
+
+ def type_to_opcode(frame_type)
+ FRAME_TYPES[frame_type] || raise("Unknown frame type")
+ end
+
+ def opcode_to_type(opcode)
+ FRAME_TYPES_INVERSE[opcode] || raise(WSProtocolError, "Unknown opcode #{opcode}")
+ end
+
+ def data_frame?(type)
+ DATA_FRAMES.include?(type)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing07.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing07.rb
new file mode 100644
index 0000000..58318e4
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing07.rb
@@ -0,0 +1,185 @@
+# encoding: BINARY
+
+module EventMachine
+ module WebSocket
+ module Framing07
+
+ def initialize_framing
+ @data = MaskedString.new
+ @application_data_buffer = '' # Used for MORE frames
+ @frame_type = nil
+ end
+
+ def process_data
+ error = false
+
+ while !error && @data.size >= 2
+ pointer = 0
+
+ fin = (@data.getbyte(pointer) & 0b10000000) == 0b10000000
+ # Ignoring rsv1-3 for now
+ opcode = @data.getbyte(pointer) & 0b00001111
+ pointer += 1
+
+ mask = (@data.getbyte(pointer) & 0b10000000) == 0b10000000
+ length = @data.getbyte(pointer) & 0b01111111
+ pointer += 1
+
+ # raise WebSocketError, 'Data from client must be masked' unless mask
+
+ payload_length = case length
+ when 127 # Length defined by 8 bytes
+ # Check buffer size
+ if @data.getbyte(pointer+8-1) == nil
+ debug [:buffer_incomplete, @data]
+ error = true
+ next
+ end
+
+ # Only using the last 4 bytes for now, till I work out how to
+ # unpack 8 bytes. I'm sure 4GB frames will do for now :)
+ l = @data.getbytes(pointer+4, 4).unpack('N').first
+ pointer += 8
+ l
+ when 126 # Length defined by 2 bytes
+ # Check buffer size
+ if @data.getbyte(pointer+2-1) == nil
+ debug [:buffer_incomplete, @data]
+ error = true
+ next
+ end
+
+ l = @data.getbytes(pointer, 2).unpack('n').first
+ pointer += 2
+ l
+ else
+ length
+ end
+
+ # Compute the expected frame length
+ frame_length = pointer + payload_length
+ frame_length += 4 if mask
+
+ if frame_length > @connection.max_frame_size
+ raise WSMessageTooBigError, "Frame length too long (#{frame_length} bytes)"
+ end
+
+ # Check buffer size
+ if @data.getbyte(frame_length - 1) == nil
+ debug [:buffer_incomplete, @data]
+ error = true
+ next
+ end
+
+ # Remove frame header
+ @data.slice!(0...pointer)
+ pointer = 0
+
+ # Read application data (unmasked if required)
+ @data.read_mask if mask
+ pointer += 4 if mask
+ application_data = @data.getbytes(pointer, payload_length)
+ pointer += payload_length
+ @data.unset_mask if mask
+
+ # Throw away data up to pointer
+ @data.slice!(0...pointer)
+
+ frame_type = opcode_to_type(opcode)
+
+ if frame_type == :continuation
+ if !@frame_type
+ raise WSProtocolError, 'Continuation frame not expected'
+ end
+ else # Not a continuation frame
+ if @frame_type && data_frame?(frame_type)
+ raise WSProtocolError, "Continuation frame expected"
+ end
+ end
+
+ # Validate that control frames are not fragmented
+ if !fin && !data_frame?(frame_type)
+ raise WSProtocolError, 'Control frames must not be fragmented'
+ end
+
+ if !fin
+ debug [:moreframe, frame_type, application_data]
+ @application_data_buffer << application_data
+ # The message type is passed in the first frame
+ @frame_type ||= frame_type
+ else
+ # Message is complete
+ if frame_type == :continuation
+ @application_data_buffer << application_data
+ message(@frame_type, '', @application_data_buffer)
+ @application_data_buffer = ''
+ @frame_type = nil
+ else
+ message(frame_type, '', application_data)
+ end
+ end
+ end # end while
+ end
+
+ def send_frame(frame_type, application_data)
+ debug [:sending_frame, frame_type, application_data]
+
+ if @state == :closing && data_frame?(frame_type)
+ raise WebSocketError, "Cannot send data frame since connection is closing"
+ end
+
+ frame = ''
+
+ opcode = type_to_opcode(frame_type)
+ byte1 = opcode | 0b10000000 # fin bit set, rsv1-3 are 0
+ frame << byte1
+
+ length = application_data.size
+ if length <= 125
+ byte2 = length # since rsv4 is 0
+ frame << byte2
+ elsif length < 65536 # write 2 byte length
+ frame << 126
+ frame << [length].pack('n')
+ else # write 8 byte length
+ frame << 127
+ frame << [length >> 32, length & 0xFFFFFFFF].pack("NN")
+ end
+
+ frame << application_data
+
+ @connection.send_data(frame)
+ end
+
+ def send_text_frame(data)
+ send_frame(:text, data)
+ end
+
+ private
+
+ FRAME_TYPES = {
+ :continuation => 0,
+ :text => 1,
+ :binary => 2,
+ :close => 8,
+ :ping => 9,
+ :pong => 10,
+ }
+ FRAME_TYPES_INVERSE = FRAME_TYPES.invert
+ # Frames are either data frames or control frames
+ DATA_FRAMES = [:text, :binary, :continuation]
+
+ def type_to_opcode(frame_type)
+ FRAME_TYPES[frame_type] || raise("Unknown frame type")
+ end
+
+ def opcode_to_type(opcode)
+ FRAME_TYPES_INVERSE[opcode] || raise(WSProtocolError, "Unknown opcode #{opcode}")
+ end
+
+ def data_frame?(type)
+ DATA_FRAMES.include?(type)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing76.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing76.rb
new file mode 100644
index 0000000..55982ff
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/framing76.rb
@@ -0,0 +1,105 @@
+# encoding: BINARY
+
+module EventMachine
+ module WebSocket
+ module Framing76
+ def initialize_framing
+ @data = ''
+ end
+
+ def process_data
+ debug [:message, @data]
+
+ # This algorithm comes straight from the spec
+ # http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-5.3
+
+ error = false
+
+ while !error
+ return if @data.size == 0
+
+ pointer = 0
+ frame_type = @data.getbyte(pointer)
+ pointer += 1
+
+ if (frame_type & 0x80) == 0x80
+ # If the high-order bit of the /frame type/ byte is set
+ length = 0
+
+ loop do
+ return false if !@data.getbyte(pointer)
+ b = @data.getbyte(pointer)
+ pointer += 1
+ b_v = b & 0x7F
+ length = length * 128 + b_v
+ break unless (b & 0x80) == 0x80
+ end
+
+ if length > @connection.max_frame_size
+ raise WSMessageTooBigError, "Frame length too long (#{length} bytes)"
+ end
+
+ if @data.getbyte(pointer+length-1) == nil
+ debug [:buffer_incomplete, @data]
+ # Incomplete data - leave @data to accumulate
+ error = true
+ else
+ # Straight from spec - I'm sure this isn't crazy...
+ # 6. Read /length/ bytes.
+ # 7. Discard the read bytes.
+ @data = @data[(pointer+length)..-1]
+
+ # If the /frame type/ is 0xFF and the /length/ was 0, then close
+ if length == 0
+ @connection.send_data("\xff\x00")
+ @state = :closing
+ @connection.close_connection_after_writing
+ else
+ error = true
+ end
+ end
+ else
+ # If the high-order bit of the /frame type/ byte is _not_ set
+
+ if @data.getbyte(0) != 0x00
+ # Close the connection since this buffer can never match
+ raise WSProtocolError, "Invalid frame received"
+ end
+
+ # Addition to the spec to protect against malicious requests
+ if @data.size > @connection.max_frame_size
+ raise WSMessageTooBigError, "Frame length too long (#{@data.size} bytes)"
+ end
+
+ msg = @data.slice!(/\A\x00[^\xff]*\xff/)
+ if msg
+ msg.gsub!(/\A\x00|\xff\z/, '')
+ if @state == :closing
+ debug [:ignored_message, msg]
+ else
+ msg.force_encoding('UTF-8') if msg.respond_to?(:force_encoding)
+ @connection.trigger_on_message(msg)
+ end
+ else
+ error = true
+ end
+ end
+ end
+
+ false
+ end
+
+ # frames need to start with 0x00-0x7f byte and end with
+ # an 0xFF byte. Per spec, we can also set the first
+ # byte to a value betweent 0x80 and 0xFF, followed by
+ # a leading length indicator
+ def send_text_frame(data)
+ debug [:sending_text_frame, data]
+ ary = ["\x00", data, "\xff"]
+ ary.collect{ |s| s.force_encoding('UTF-8') if s.respond_to?(:force_encoding) }
+ @connection.send_data(ary.join)
+ end
+
+ end
+ end
+end \ No newline at end of file
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler.rb
new file mode 100644
index 0000000..98cdd4e
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler.rb
@@ -0,0 +1,97 @@
+module EventMachine
+ module WebSocket
+ class Handler
+ def self.klass_factory(version)
+ case version
+ when 75
+ Handler75
+ when 76
+ Handler76
+ when 1..3
+ # We'll use handler03 - I believe they're all compatible
+ Handler03
+ when 5
+ Handler05
+ when 6
+ Handler06
+ when 7
+ Handler07
+ when 8
+ # drafts 9, 10, 11 and 12 should never change the version
+ # number as they are all the same as version 08.
+ Handler08
+ when 13
+ # drafts 13 to 17 all identify as version 13 as they are
+ # only minor changes or text changes.
+ Handler13
+ else
+ # According to spec should abort the connection
+ raise HandshakeError, "Protocol version #{version} not supported"
+ end
+ end
+
+ include Debugger
+
+ attr_reader :request, :state
+
+ def initialize(connection, debug = false)
+ @connection = connection
+ @debug = debug
+ @state = :connected
+ @close_timer = nil
+ initialize_framing
+ end
+
+ def receive_data(data)
+ @data << data
+ process_data
+ rescue WSProtocolError => e
+ fail_websocket(e)
+ end
+
+ def close_websocket(code, body)
+ # Implemented in subclass
+ end
+
+ # Used to avoid un-acked and unclosed remaining open indefinitely
+ def start_close_timeout
+ @close_timer = EM::Timer.new(@connection.close_timeout) {
+ @connection.close_connection
+ e = WSProtocolError.new("Close handshake un-acked after #{@connection.close_timeout}s, closing tcp connection")
+ @connection.trigger_on_error(e)
+ }
+ end
+
+ # This corresponds to "Fail the WebSocket Connection" in the spec.
+ def fail_websocket(e)
+ debug [:error, e]
+ close_websocket(e.code, e.message)
+ @connection.close_connection_after_writing
+ @connection.trigger_on_error(e)
+ end
+
+ def unbind
+ @state = :closed
+
+ @close_timer.cancel if @close_timer
+
+ @close_info = defined?(@close_info) ? @close_info : {
+ :code => 1006,
+ :was_clean => false,
+ }
+
+ @connection.trigger_on_close(@close_info)
+ end
+
+ def ping
+ # Overridden in subclass
+ false
+ end
+
+ def pingable?
+ # Also Overridden
+ false
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler03.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler03.rb
new file mode 100644
index 0000000..ac4b3ac
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler03.rb
@@ -0,0 +1,9 @@
+module EventMachine
+ module WebSocket
+ class Handler03 < Handler
+ include Framing03
+ include MessageProcessor03
+ include Close03
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler05.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler05.rb
new file mode 100644
index 0000000..4e48e15
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler05.rb
@@ -0,0 +1,9 @@
+module EventMachine
+ module WebSocket
+ class Handler05 < Handler
+ include Framing05
+ include MessageProcessor03
+ include Close05
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler06.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler06.rb
new file mode 100644
index 0000000..ad55ef7
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler06.rb
@@ -0,0 +1,9 @@
+module EventMachine
+ module WebSocket
+ class Handler06 < Handler
+ include Framing05
+ include MessageProcessor06
+ include Close06
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler07.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler07.rb
new file mode 100644
index 0000000..d080cf5
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler07.rb
@@ -0,0 +1,9 @@
+module EventMachine
+ module WebSocket
+ class Handler07 < Handler
+ include Framing07
+ include MessageProcessor06
+ include Close06
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler08.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler08.rb
new file mode 100644
index 0000000..632057f
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler08.rb
@@ -0,0 +1,9 @@
+module EventMachine
+ module WebSocket
+ class Handler08 < Handler
+ include Framing07
+ include MessageProcessor06
+ include Close06
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler13.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler13.rb
new file mode 100644
index 0000000..1dc465d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler13.rb
@@ -0,0 +1,9 @@
+module EventMachine
+ module WebSocket
+ class Handler13 < Handler
+ include Framing07
+ include MessageProcessor06
+ include Close06
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler75.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler75.rb
new file mode 100644
index 0000000..d9a90d3
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler75.rb
@@ -0,0 +1,9 @@
+module EventMachine
+ module WebSocket
+ class Handler75 < Handler
+ include Handshake75
+ include Framing76
+ include Close75
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler76.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler76.rb
new file mode 100644
index 0000000..c4aaa37
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handler76.rb
@@ -0,0 +1,14 @@
+# encoding: BINARY
+
+module EventMachine
+ module WebSocket
+ class Handler76 < Handler
+ include Handshake76
+ include Framing76
+ include Close75
+
+ # "\377\000" is octet version and "\xff\x00" is hex version
+ TERMINATE_STRING = "\xff\x00"
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handshake.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handshake.rb
new file mode 100644
index 0000000..12b7904
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handshake.rb
@@ -0,0 +1,156 @@
+require "http/parser"
+require "uri"
+
+module EventMachine
+ module WebSocket
+
+ # Resposible for creating the server handshake response
+ class Handshake
+ include EM::Deferrable
+
+ attr_reader :parser, :protocol_version
+
+ # Unfortunately drafts 75 & 76 require knowledge of whether the
+ # connection is being terminated as ws/wss in order to generate the
+ # correct handshake response
+ def initialize(secure)
+ @parser = Http::Parser.new
+ @secure = secure
+
+ @parser.on_headers_complete = proc { |headers|
+ @headers = Hash[headers.map { |k,v| [k.downcase, v] }]
+ }
+ end
+
+ def receive_data(data)
+ @parser << data
+
+ if defined? @headers
+ process(@headers, @parser.upgrade_data)
+ end
+ rescue HTTP::Parser::Error => e
+ fail(HandshakeError.new("Invalid HTTP header: #{e.message}"))
+ end
+
+ # Returns the WebSocket upgrade headers as a hash.
+ #
+ # Keys are strings, unmodified from the request.
+ #
+ def headers
+ @parser.headers
+ end
+
+ # The same as headers, except that the hash keys are downcased
+ #
+ def headers_downcased
+ @headers
+ end
+
+ # Returns the request path (excluding any query params)
+ #
+ def path
+ @path
+ end
+
+ # Returns the query params as a string foo=bar&baz=...
+ def query_string
+ @query_string
+ end
+
+ def query
+ Hash[query_string.split('&').map { |c| c.split('=', 2) }]
+ end
+
+ # Returns the WebSocket origin header if provided
+ #
+ def origin
+ @headers["origin"] || @headers["sec-websocket-origin"] || nil
+ end
+
+ def secure?
+ @secure
+ end
+
+ private
+
+ def process(headers, remains)
+ unless @parser.http_method == "GET"
+ raise HandshakeError, "Must be GET request"
+ end
+
+ # Validate request path
+ #
+ # According to http://tools.ietf.org/search/rfc2616#section-5.1.2, an
+ # invalid Request-URI should result in a 400 status code, but
+ # HandshakeError's currently result in a WebSocket abort. It's not
+ # clear which should take precedence, but an abort will do just fine.
+ begin
+ uri = URI.parse(@parser.request_url)
+ @path = uri.path
+ @query_string = uri.query || ""
+ rescue URI::InvalidURIError
+ raise HandshakeError, "Invalid request URI: #{@parser.request_url}"
+ end
+
+ # Validate Upgrade
+ unless @parser.upgrade?
+ raise HandshakeError, "Not an upgrade request"
+ end
+ upgrade = @headers['upgrade']
+ unless upgrade.kind_of?(String) && upgrade.downcase == 'websocket'
+ raise HandshakeError, "Invalid upgrade header: #{upgrade.inspect}"
+ end
+
+ # Determine version heuristically
+ version = if @headers['sec-websocket-version']
+ # Used from drafts 04 onwards
+ @headers['sec-websocket-version'].to_i
+ elsif @headers['sec-websocket-draft']
+ # Used in drafts 01 - 03
+ @headers['sec-websocket-draft'].to_i
+ elsif @headers['sec-websocket-key1']
+ 76
+ else
+ 75
+ end
+
+ # Additional handling of bytes after the header if required
+ case version
+ when 75
+ if !remains.empty?
+ raise HandshakeError, "Extra bytes after header"
+ end
+ when 76, 1..3
+ if remains.length < 8
+ # The whole third-key has not been received yet.
+ return nil
+ elsif remains.length > 8
+ raise HandshakeError, "Extra bytes after third key"
+ end
+ @headers['third-key'] = remains
+ end
+
+ handshake_klass = case version
+ when 75
+ Handshake75
+ when 76, 1..3
+ Handshake76
+ when 5, 6, 7, 8, 13
+ Handshake04
+ else
+ # According to spec should abort the connection
+ raise HandshakeError, "Protocol version #{version} not supported"
+ end
+
+ upgrade_response = handshake_klass.handshake(@headers, @parser.request_url, @secure)
+
+ handler_klass = Handler.klass_factory(version)
+
+ @protocol_version = version
+ succeed(upgrade_response, handler_klass)
+ rescue HandshakeError => e
+ fail(e)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handshake04.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handshake04.rb
new file mode 100644
index 0000000..7600787
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handshake04.rb
@@ -0,0 +1,37 @@
+require 'digest/sha1'
+require 'base64'
+
+module EventMachine
+ module WebSocket
+ module Handshake04
+ def self.handshake(headers, _, __)
+ # Required
+ unless key = headers['sec-websocket-key']
+ raise HandshakeError, "sec-websocket-key header is required"
+ end
+
+ string_to_sign = "#{key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+ signature = Base64.encode64(Digest::SHA1.digest(string_to_sign)).chomp
+
+ upgrade = ["HTTP/1.1 101 Switching Protocols"]
+ upgrade << "Upgrade: websocket"
+ upgrade << "Connection: Upgrade"
+ upgrade << "Sec-WebSocket-Accept: #{signature}"
+ if protocol = headers['sec-websocket-protocol']
+ validate_protocol!(protocol)
+ upgrade << "Sec-WebSocket-Protocol: #{protocol}"
+ end
+
+ # TODO: Support sec-websocket-protocol selection
+ # TODO: sec-websocket-extensions
+
+ return upgrade.join("\r\n") + "\r\n\r\n"
+ end
+
+ def self.validate_protocol!(protocol)
+ raise HandshakeError, "Invalid WebSocket-Protocol: empty" if protocol.empty?
+ # TODO: Validate characters
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handshake75.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handshake75.rb
new file mode 100644
index 0000000..65b744f
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handshake75.rb
@@ -0,0 +1,28 @@
+module EventMachine
+ module WebSocket
+ module Handshake75
+ def self.handshake(headers, path, secure)
+ scheme = (secure ? "wss" : "ws")
+ location = "#{scheme}://#{headers['host']}#{path}"
+
+ upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
+ upgrade << "Upgrade: WebSocket\r\n"
+ upgrade << "Connection: Upgrade\r\n"
+ upgrade << "WebSocket-Origin: #{headers['origin']}\r\n"
+ upgrade << "WebSocket-Location: #{location}\r\n"
+ if protocol = headers['sec-websocket-protocol']
+ validate_protocol!(protocol)
+ upgrade << "Sec-WebSocket-Protocol: #{protocol}\r\n"
+ end
+ upgrade << "\r\n"
+
+ return upgrade
+ end
+
+ def self.validate_protocol!(protocol)
+ raise HandshakeError, "Invalid WebSocket-Protocol: empty" if protocol.empty?
+ # TODO: Validate characters
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handshake76.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handshake76.rb
new file mode 100644
index 0000000..c18260d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/handshake76.rb
@@ -0,0 +1,72 @@
+require 'digest/md5'
+
+module EventMachine::WebSocket
+ module Handshake76
+ class << self
+ def handshake(headers, path, secure)
+ challenge_response = solve_challenge(
+ headers['sec-websocket-key1'],
+ headers['sec-websocket-key2'],
+ headers['third-key']
+ )
+
+ scheme = (secure ? "wss" : "ws")
+ location = "#{scheme}://#{headers['host']}#{path}"
+
+ upgrade = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+ upgrade << "Upgrade: WebSocket\r\n"
+ upgrade << "Connection: Upgrade\r\n"
+ upgrade << "Sec-WebSocket-Location: #{location}\r\n"
+ upgrade << "Sec-WebSocket-Origin: #{headers['origin']}\r\n"
+ if protocol = headers['sec-websocket-protocol']
+ validate_protocol!(protocol)
+ upgrade << "Sec-WebSocket-Protocol: #{protocol}\r\n"
+ end
+ upgrade << "\r\n"
+ upgrade << challenge_response
+
+ return upgrade
+ end
+
+ private
+
+ def solve_challenge(first, second, third)
+ # Refer to 5.2 4-9 of the draft 76
+ sum = [numbers_over_spaces(first)].pack("N*") +
+ [numbers_over_spaces(second)].pack("N*") +
+ third
+ Digest::MD5.digest(sum)
+ end
+
+ def numbers_over_spaces(string)
+ unless string
+ raise HandshakeError, "WebSocket key1 or key2 is missing"
+ end
+
+ numbers = string.scan(/[0-9]/).join.to_i
+
+ spaces = string.scan(/ /).size
+ # As per 5.2.5, abort the connection if spaces are zero.
+ raise HandshakeError, "Websocket Key1 or Key2 does not contain spaces - this is a symptom of a cross-protocol attack" if spaces == 0
+
+ # As per 5.2.6, abort if numbers is not an integral multiple of spaces
+ if numbers % spaces != 0
+ raise HandshakeError, "Invalid Key #{string.inspect}"
+ end
+
+ quotient = numbers / spaces
+
+ if quotient > 2**32-1
+ raise HandshakeError, "Challenge computation out of range for key #{string.inspect}"
+ end
+
+ return quotient
+ end
+
+ def validate_protocol!(protocol)
+ raise HandshakeError, "Invalid WebSocket-Protocol: empty" if protocol.empty?
+ # TODO: Validate characters
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/masking04.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/masking04.rb
new file mode 100644
index 0000000..ad050e9
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/masking04.rb
@@ -0,0 +1,37 @@
+module EventMachine
+ module WebSocket
+ class MaskedString < String
+ # Read a 4 bit XOR mask - further requested bytes will be unmasked
+ def read_mask
+ if respond_to?(:encoding) && encoding.name != "ASCII-8BIT"
+ raise "MaskedString only operates on BINARY strings"
+ end
+ raise "Too short" if bytesize < 4 # TODO - change
+ @masking_key = String.new(self[0..3])
+ end
+
+ # Removes the mask, behaves like a normal string again
+ def unset_mask
+ @masking_key = nil
+ end
+
+ def getbyte(index)
+ if defined?(@masking_key) && @masking_key
+ masked_char = super
+ masked_char ? masked_char ^ @masking_key.getbyte(index % 4) : nil
+ else
+ super
+ end
+ end
+
+ def getbytes(start_index, count)
+ data = ''
+ data.force_encoding('ASCII-8BIT') if data.respond_to?(:force_encoding)
+ count.times do |i|
+ data << getbyte(start_index + i)
+ end
+ data
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/message_processor_03.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/message_processor_03.rb
new file mode 100644
index 0000000..c5dec70
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/message_processor_03.rb
@@ -0,0 +1,47 @@
+# encoding: BINARY
+
+module EventMachine
+ module WebSocket
+ module MessageProcessor03
+ def message(message_type, extension_data, application_data)
+ case message_type
+ when :close
+ @close_info = {
+ :code => 1005,
+ :reason => "",
+ :was_clean => true,
+ }
+ if @state == :closing
+ # TODO: Check that message body matches sent data
+ # We can close connection immediately since there is no more data
+ # is allowed to be sent or received on this connection
+ @connection.close_connection
+ else
+ # Acknowlege close
+ # The connection is considered closed
+ send_frame(:close, application_data)
+ @connection.close_connection_after_writing
+ end
+ when :ping
+ # Pong back the same data
+ send_frame(:pong, application_data)
+ @connection.trigger_on_ping(application_data)
+ when :pong
+ @connection.trigger_on_pong(application_data)
+ when :text
+ if application_data.respond_to?(:force_encoding)
+ application_data.force_encoding("UTF-8")
+ end
+ @connection.trigger_on_message(application_data)
+ when :binary
+ @connection.trigger_on_binary(application_data)
+ end
+ end
+
+ # Ping & Pong supported
+ def pingable?
+ true
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/message_processor_06.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/message_processor_06.rb
new file mode 100644
index 0000000..25ca5d2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/message_processor_06.rb
@@ -0,0 +1,78 @@
+module EventMachine
+ module WebSocket
+ module MessageProcessor06
+ def message(message_type, extension_data, application_data)
+ debug [:message_received, message_type, application_data]
+
+ case message_type
+ when :close
+ status_code = case application_data.length
+ when 0
+ # close messages MAY contain a body
+ nil
+ when 1
+ # Illegal close frame
+ raise WSProtocolError, "Close frames with a body must contain a 2 byte status code"
+ else
+ application_data.slice!(0, 2).unpack('n').first
+ end
+
+ debug [:close_frame_received, status_code, application_data]
+
+ @close_info = {
+ :code => status_code || 1005,
+ :reason => application_data,
+ :was_clean => true,
+ }
+
+ if @state == :closing
+ # We can close connection immediately since no more data may be
+ # sent or received on this connection
+ @connection.close_connection
+ elsif @state == :connected
+ # Acknowlege close & echo status back to client
+ # The connection is considered closed
+ close_data = [status_code || 1000].pack('n')
+ send_frame(:close, close_data)
+ @connection.close_connection_after_writing
+ end
+ when :ping
+ # There are a couple of protections here against malicious/broken WebSocket abusing ping frames.
+ #
+ # 1. Delay 200ms before replying. This reduces the number of pings from WebSocket clients behaving as
+ # `for (;;) { send_ping(conn); rcv_pong(conn); }`. The spec says we "SHOULD respond with Pong frame as soon
+ # as is practical".
+ # 2. Reply at most every 200ms. This reduces the number of pong frames sent to WebSocket clients behaving as
+ # `for (;;) { send_ping(conn); }`. The spec says "If an endpoint receives a Ping frame and has not yet sent
+ # Pong frame(s) in response to previous Ping frame(s), the endpoint MAY elect to send a Pong frame for only
+ # the most recently processed Ping frame."
+ @most_recent_pong_application_data = application_data
+ if @pong_timer == nil then
+ @pong_timer = EventMachine.add_timer(0.2) do
+ @pong_timer = nil
+ send_frame(:pong, @most_recent_pong_application_data)
+ end
+ end
+ @connection.trigger_on_ping(application_data)
+ when :pong
+ @connection.trigger_on_pong(application_data)
+ when :text
+ if application_data.respond_to?(:force_encoding)
+ application_data.force_encoding("UTF-8")
+ unless application_data.valid_encoding?
+ raise InvalidDataError, "Invalid UTF8 data"
+ end
+ end
+ @connection.trigger_on_message(application_data)
+ when :binary
+ @connection.trigger_on_binary(application_data)
+ end
+ end
+
+ # Ping & Pong supported
+ def pingable?
+ true
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/version.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/version.rb
new file mode 100644
index 0000000..ecdedbe
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/version.rb
@@ -0,0 +1,5 @@
+module EventMachine
+ module Websocket
+ VERSION = "0.5.3"
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/websocket.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/websocket.rb
new file mode 100644
index 0000000..02e4d86
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/lib/em-websocket/websocket.rb
@@ -0,0 +1,56 @@
+module EventMachine
+ module WebSocket
+ class << self
+ attr_accessor :max_frame_size
+ attr_accessor :close_timeout
+ end
+ @max_frame_size = 10 * 1024 * 1024 # 10MB
+ # Connections are given 60s to close after being sent a close handshake
+ @close_timeout = 60
+
+ # All errors raised by em-websocket should descend from this class
+ class WebSocketError < RuntimeError; end
+
+ # Used for errors that occur during WebSocket handshake
+ class HandshakeError < WebSocketError; end
+
+ # Used for errors which should cause the connection to close.
+ # See RFC6455 ยง7.4.1 for a full description of meanings
+ class WSProtocolError < WebSocketError
+ def code; 1002; end
+ end
+
+ class InvalidDataError < WSProtocolError
+ def code; 1007; end
+ end
+
+ # 1009: Message too big to process
+ class WSMessageTooBigError < WSProtocolError
+ def code; 1009; end
+ end
+
+ # Start WebSocket server, including starting eventmachine run loop
+ def self.start(options, &blk)
+ EM.epoll
+ EM.run {
+ trap("TERM") { stop }
+ trap("INT") { stop }
+
+ run(options, &blk)
+ }
+ end
+
+ # Start WebSocket server inside eventmachine run loop
+ def self.run(options)
+ host, port = options.values_at(:host, :port)
+ EM.start_server(host, port, Connection, options) do |c|
+ yield c
+ end
+ end
+
+ def self.stop
+ puts "Terminating WebSocket Server"
+ EM.stop
+ end
+ end
+end