From 5571dc766e2143e39762ff0b47c41d1c2e154f4c Mon Sep 17 00:00:00 2001 From: Benjamin Sanders Date: Sat, 11 Oct 2025 10:34:24 -0400 Subject: Vendor bundled gems for deployment --- .../3.4.0/gems/em-websocket-0.5.3/spec/helper.rb | 173 ++++++++++++ .../spec/integration/common_spec.rb | 138 ++++++++++ .../spec/integration/draft03_spec.rb | 298 +++++++++++++++++++++ .../spec/integration/draft05_spec.rb | 50 ++++ .../spec/integration/draft06_spec.rb | 145 ++++++++++ .../spec/integration/draft13_spec.rb | 105 ++++++++ .../spec/integration/draft75_spec.rb | 123 +++++++++ .../spec/integration/draft76_spec.rb | 234 ++++++++++++++++ .../spec/integration/gte_03_examples.rb | 42 +++ .../spec/integration/shared_examples.rb | 265 ++++++++++++++++++ .../em-websocket-0.5.3/spec/unit/framing_spec.rb | 298 +++++++++++++++++++++ .../em-websocket-0.5.3/spec/unit/handshake_spec.rb | 216 +++++++++++++++ .../em-websocket-0.5.3/spec/unit/masking_spec.rb | 29 ++ 13 files changed, 2116 insertions(+) create mode 100644 vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/helper.rb create mode 100644 vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/common_spec.rb create mode 100644 vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft03_spec.rb create mode 100644 vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft05_spec.rb create mode 100644 vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft06_spec.rb create mode 100644 vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft13_spec.rb create mode 100644 vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft75_spec.rb create mode 100644 vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft76_spec.rb create mode 100644 vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/gte_03_examples.rb create mode 100644 vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/shared_examples.rb create mode 100644 vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/unit/framing_spec.rb create mode 100644 vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/unit/handshake_spec.rb create mode 100644 vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/unit/masking_spec.rb (limited to 'vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec') diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/helper.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/helper.rb new file mode 100644 index 0000000..76e45b5 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/helper.rb @@ -0,0 +1,173 @@ +# encoding: BINARY + +require 'rubygems' +require 'rspec' +require 'em-spec/rspec' +require 'em-http' + +require 'em-websocket' +require 'em-websocket-client' + +require 'integration/shared_examples' +require 'integration/gte_03_examples' + +RSpec.configure do |c| + c.mock_with :rspec +end + +class FakeWebSocketClient < EM::Connection + attr_reader :handshake_response, :packets + + 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 initialize + @state = :new + @packets = [] + end + + def receive_data(data) + # puts "RECEIVE DATA #{data}" + if @state == :new + @handshake_response = data + @onopen.call if defined? @onopen + @state = :open + else + @onmessage.call(data) if defined? @onmessage + @packets << data + end + end + + def send(application_data) + send_frame(:text, application_data) + end + + def send_frame(type, application_data) + send_data construct_frame(type, application_data) + end + + def unbind + @onclose.call if defined? @onclose + end + + private + + def construct_frame(type, data) + "\x00#{data}\xff" + end +end + +class Draft03FakeWebSocketClient < FakeWebSocketClient + private + + def construct_frame(type, data) + frame = "" + frame << EM::WebSocket::Framing03::FRAME_TYPES[type] + frame << encoded_length(data.size) + frame << data + end + + def encoded_length(length) + if length <= 125 + [length].pack('C') # since rsv4 is 0 + elsif length < 65536 # write 2 byte length + "\126#{[length].pack('n')}" + else # write 8 byte length + "\127#{[length >> 32, length & 0xFFFFFFFF].pack("NN")}" + end + end +end + +class Draft05FakeWebSocketClient < Draft03FakeWebSocketClient + private + + def construct_frame(type, data) + frame = "" + frame << "\x00\x00\x00\x00" # Mask with nothing for simplicity + frame << (EM::WebSocket::Framing05::FRAME_TYPES[type] | 0b10000000) + frame << encoded_length(data.size) + frame << data + end +end + +class Draft07FakeWebSocketClient < Draft05FakeWebSocketClient + private + + def construct_frame(type, data) + frame = "" + frame << (EM::WebSocket::Framing07::FRAME_TYPES[type] | 0b10000000) + # Should probably mask the data, but I get away without bothering since + # the server doesn't enforce that incoming frames are masked + frame << encoded_length(data.size) + frame << data + end +end + +# Wrapper around em-websocket-client +class Draft75WebSocketClient + 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 initialize + @ws = EventMachine::WebSocketClient.connect('ws://127.0.0.1:12345/', + :version => 75, + :origin => 'http://example.com') + @ws.errback { |err| @onerror.call if defined? @onerror } + @ws.callback { @onopen.call if defined? @onopen } + @ws.stream { |msg| @onmessage.call(msg) if defined? @onmessage } + @ws.disconnect { @onclose.call if defined? @onclose } + end + + def send(message) + @ws.send_msg(message) + end + + def close_connection + @ws.close_connection + end +end + +def start_server(opts = {}) + EM::WebSocket.run({:host => "0.0.0.0", :port => 12345}.merge(opts)) { |ws| + yield ws if block_given? + } +end + +def format_request(r) + data = "#{r[:method]} #{r[:path]} HTTP/1.1\r\n" + header_lines = r[:headers].map { |k,v| "#{k}: #{v}" } + data << [header_lines, '', r[:body]].join("\r\n") + data +end + +def format_response(r) + data = r[:protocol] || "HTTP/1.1 101 WebSocket Protocol Handshake\r\n" + header_lines = r[:headers].map { |k,v| "#{k}: #{v}" } + data << [header_lines, '', r[:body]].join("\r\n") + data +end + +RSpec::Matchers.define :succeed_with_upgrade do |response| + match do |actual| + success = nil + actual.callback { |upgrade_response, handler_klass| + success = (upgrade_response.lines.sort == format_response(response).lines.sort) + } + success + end +end + +RSpec::Matchers.define :fail_with_error do |error_klass, error_message| + match do |actual| + success = nil + actual.errback { |e| + success = (e.class == error_klass) + success &= (e.message == error_message) if error_message + } + success + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/common_spec.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/common_spec.rb new file mode 100644 index 0000000..c89f871 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/common_spec.rb @@ -0,0 +1,138 @@ +require 'helper' + +# These tests are not specific to any particular draft of the specification +# +describe "WebSocket server" do + include EM::SpecHelper + default_timeout 1 + + it "should fail on non WebSocket requests" do + em { + EM.add_timer(0.1) do + http = EM::HttpRequest.new('http://127.0.0.1:12345/').get :timeout => 0 + http.errback { done } + http.callback { fail } + end + + start_server + } + end + + it "should expose the WebSocket request headers, path and query params" do + em { + EM.add_timer(0.1) do + ws = EventMachine::WebSocketClient.connect('ws://127.0.0.1:12345/', + :origin => 'http://example.com') + ws.errback { fail } + ws.callback { ws.close_connection } + ws.stream { |msg| } + end + + start_server do |ws| + ws.onopen { |handshake| + headers = handshake.headers + headers["Connection"].should == "Upgrade" + headers["Upgrade"].should == "websocket" + headers["Host"].to_s.should == "127.0.0.1:12345" + handshake.path.should == "/" + handshake.query.should == {} + handshake.origin.should == 'http://example.com' + } + ws.onclose { + ws.state.should == :closed + done + } + end + } + end + + it "should expose the WebSocket path and query params when nonempty" do + em { + EM.add_timer(0.1) do + ws = EventMachine::WebSocketClient.connect('ws://127.0.0.1:12345/hello?foo=bar&baz=qux') + ws.errback { fail } + ws.callback { + ws.close_connection + } + ws.stream { |msg| } + end + + start_server do |ws| + ws.onopen { |handshake| + handshake.path.should == '/hello' + handshake.query_string.split('&').sort. + should == ["baz=qux", "foo=bar"] + handshake.query.should == {"foo"=>"bar", "baz"=>"qux"} + } + ws.onclose { + ws.state.should == :closed + done + } + end + } + end + + it "should raise an exception if frame sent before handshake complete" do + em { + # 1. Start WebSocket server + start_server { |ws| + # 3. Try to send a message to the socket + lambda { + ws.send('early message') + }.should raise_error('Cannot send data before onopen callback') + done + } + + # 2. Connect a dumb TCP connection (will not send handshake) + EM.connect('0.0.0.0', 12345, EM::Connection) + } + end + + it "should allow the server to be started inside an existing EM" do + em { + EM.add_timer(0.1) do + http = EM::HttpRequest.new('http://127.0.0.1:12345/').get :timeout => 0 + http.errback { |e| done } + http.callback { fail } + end + + start_server do |ws| + ws.onopen { |handshake| + headers = handshake.headers + headers["Host"].to_s.should == "127.0.0.1:12345" + } + ws.onclose { + ws.state.should == :closed + done + } + end + } + end + + context "outbound limit set" do + it "should close the connection if the limit is reached" do + em { + start_server(:outbound_limit => 150) do |ws| + # Increase the message size by one on each loop + ws.onmessage{|msg| ws.send(msg + "x") } + ws.onclose{|status| + status[:code].should == 1006 # Unclean + status[:was_clean].should be false + } + end + + EM.add_timer(0.1) do + ws = EventMachine::WebSocketClient.connect('ws://127.0.0.1:12345/') + ws.callback { ws.send_msg "hello" } + ws.disconnect { done } # Server closed the connection + ws.stream { |msg| + # minus frame size ? (getting 146 max here) + msg.data.size.should <= 150 + # Return back the message + ws.send_msg(msg.data) + } + end + } + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft03_spec.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft03_spec.rb new file mode 100644 index 0000000..f299ab6 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft03_spec.rb @@ -0,0 +1,298 @@ +require 'helper' + +describe "draft03" do + include EM::SpecHelper + default_timeout 1 + + before :each do + @request = { + :port => 80, + :method => "GET", + :path => "/demo", + :headers => { + 'Host' => 'example.com', + 'Connection' => 'Upgrade', + 'Sec-WebSocket-Key2' => '12998 5 Y3 1 .P00', + 'Sec-WebSocket-Protocol' => 'sample', + 'Upgrade' => 'WebSocket', + 'Sec-WebSocket-Key1' => '4 @1 46546xW%0l 1 5', + 'Origin' => 'http://example.com', + 'Sec-WebSocket-Draft' => '3' + }, + :body => '^n:ds[4U' + } + + @response = { + :headers => { + "Upgrade" => "WebSocket", + "Connection" => "Upgrade", + "Sec-WebSocket-Location" => "ws://example.com/demo", + "Sec-WebSocket-Origin" => "http://example.com", + "Sec-WebSocket-Protocol" => "sample" + }, + :body => "8jKS\'y:G*Co,Wxa-" + } + end + + def start_client + client = EM.connect('0.0.0.0', 12345, Draft03FakeWebSocketClient) + client.send_data(format_request(@request)) + yield client if block_given? + return client + end + + it_behaves_like "a websocket server" do + let(:version) { 3 } + end + + it_behaves_like "a WebSocket server drafts 3 and above" do + let(:version) { 3 } + end + + # These examples are straight from the spec + # http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-03#section-4.6 + describe "examples from the spec" do + it "should accept a single-frame text message" do + em { + start_server { |ws| + ws.onmessage { |msg| + msg.should == 'Hello' + done + } + } + start_client { |client| + client.onopen { + client.send_data("\x04\x05Hello") + } + } + } + end + + it "should accept a fragmented text message" do + em { + start_server { |ws| + ws.onmessage { |msg| + msg.should == 'Hello' + done + } + } + + connection = start_client + + # Send frame + connection.onopen { + connection.send_data("\x84\x03Hel") + connection.send_data("\x00\x02lo") + } + } + end + + it "should accept a ping request and respond with the same body" do + em { + start_server + + connection = start_client + + # Send frame + connection.onopen { + connection.send_data("\x02\x05Hello") + } + + connection.onmessage { |frame| + next if frame.nil? + frame.should == "\x03\x05Hello" + done + } + } + end + + it "should accept a 256 bytes binary message in a single frame" do + em { + data = "a" * 256 + + start_server { |ws| + ws.onbinary { |msg| + msg.encoding.should == Encoding.find("BINARY") if defined?(Encoding) + msg.should == data + done + } + } + + connection = start_client + + # Send frame + connection.onopen { + connection.send_data("\x05\x7E\x01\x00" + data) + } + } + end + + it "should accept a 64KiB binary message in a single frame" do + em { + data = "a" * 65536 + + start_server { |ws| + ws.onbinary { |msg| + msg.encoding.should == Encoding.find("BINARY") if defined?(Encoding) + msg.should == data + done + } + } + + connection = start_client + + # Send frame + connection.onopen { + connection.send_data("\x05\x7F\x00\x00\x00\x00\x00\x01\x00\x00" + data) + } + } + end + end + + describe "close handling" do + it "should respond to a new close frame with a close frame" do + em { + start_server + + connection = start_client + + # Send close frame + connection.onopen { + connection.send_data("\x01\x00") + } + + # Check that close ack received + connection.onmessage { |frame| + frame.should == "\x01\x00" + done + } + } + end + + it "should close the connection on receiving a close acknowlegement and call onclose with close code 1005 and was_clean=true (initiated by server)" do + em { + ack_received = false + + start_server { |ws| + ws.onopen { + # 2. Send a close frame + EM.next_tick { + ws.close + } + } + + # 5. Onclose event on server + ws.onclose { |event| + event.should == { + :code => 1005, + :reason => "", + :was_clean => true, + } + done + } + } + + # 1. Create a fake client which sends draft 76 handshake + connection = start_client + + # 3. Check that close frame recieved and acknowlege it + connection.onmessage { |frame| + frame.should == "\x01\x00" + ack_received = true + connection.send_data("\x01\x00") + } + + # 4. Check that connection is closed _after_ the ack + connection.onclose { + ack_received.should == true + } + } + end + + # it "should repur" + # + it "should return close code 1005 and was_clean=true after closing handshake (initiated by client)" do + em { + start_server { |ws| + ws.onclose { |event| + event.should == { + :code => 1005, + :reason => "", + :was_clean => true, + } + done + } + } + start_client { |client| + client.onopen { + client.send_data("\x01\x00") + } + } + } + end + + it "should not allow data frame to be sent after close frame sent" do + em { + start_server { |ws| + ws.onopen { + # 2. Send a close frame + EM.next_tick { + ws.close + } + + # 3. Check that exception raised if I attempt to send more data + EM.add_timer(0.1) { + lambda { + ws.send('hello world') + }.should raise_error(EM::WebSocket::WebSocketError, 'Cannot send data frame since connection is closing') + done + } + } + } + + # 1. Create a fake client which sends draft 76 handshake + start_client + } + end + + it "should still respond to control frames after close frame sent" do + em { + start_server { |ws| + ws.onopen { + # 2. Send a close frame + EM.next_tick { + ws.close + } + } + } + + # 1. Create a fake client which sends draft 76 handshake + connection = start_client + + connection.onmessage { |frame| + if frame == "\x01\x00" + # 3. After the close frame is received send a ping frame, but + # don't respond with a close ack + connection.send_data("\x02\x05Hello") + else + # 4. Check that the pong is received + frame.should == "\x03\x05Hello" + done + end + } + } + end + + it "should report that close codes are not supported" do + em { + start_server { |ws| + ws.onopen { + ws.supports_close_codes?.should == false + done + } + } + start_client + } + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft05_spec.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft05_spec.rb new file mode 100644 index 0000000..e07ea28 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft05_spec.rb @@ -0,0 +1,50 @@ +require 'helper' + +describe "draft05" do + include EM::SpecHelper + default_timeout 1 + + before :each do + @request = { + :port => 80, + :method => "GET", + :path => "/demo", + :headers => { + 'Host' => 'example.com', + 'Upgrade' => 'websocket', + 'Connection' => 'Upgrade', + 'Sec-WebSocket-Key' => 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Protocol' => 'sample', + 'Sec-WebSocket-Origin' => 'http://example.com', + 'Sec-WebSocket-Version' => '5' + } + } + end + + def start_client + client = EM.connect('0.0.0.0', 12345, Draft05FakeWebSocketClient) + client.send_data(format_request(@request)) + yield client if block_given? + return client + end + + it_behaves_like "a websocket server" do + let(:version) { 5 } + end + + it_behaves_like "a WebSocket server drafts 3 and above" do + let(:version) { 5 } + end + + it "should report that close codes are not supported" do + em { + start_server { |ws| + ws.onopen { + ws.supports_close_codes?.should == false + done + } + } + start_client + } + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft06_spec.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft06_spec.rb new file mode 100644 index 0000000..ae213cb --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft06_spec.rb @@ -0,0 +1,145 @@ +require 'helper' + +describe "draft06" do + include EM::SpecHelper + default_timeout 1 + + before :each do + @request = { + :port => 80, + :method => "GET", + :path => "/demo", + :headers => { + 'Host' => 'example.com', + 'Upgrade' => 'websocket', + 'Connection' => 'Upgrade', + 'Sec-WebSocket-Key' => 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Protocol' => 'sample', + 'Sec-WebSocket-Origin' => 'http://example.com', + 'Sec-WebSocket-Version' => '6' + } + } + + @response = { + :protocol => "HTTP/1.1 101 Switching Protocols\r\n", + :headers => { + "Upgrade" => "websocket", + "Connection" => "Upgrade", + "Sec-WebSocket-Accept" => "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", + "Sec-WebSocket-Protocol" => "sample", + } + } + end + + def start_client + client = EM.connect('0.0.0.0', 12345, Draft05FakeWebSocketClient) + client.send_data(format_request(@request)) + yield client if block_given? + return client + end + + it_behaves_like "a websocket server" do + let(:version) { 6 } + end + + it_behaves_like "a WebSocket server drafts 3 and above" do + let(:version) { 6 } + end + + it "should open connection" do + em { + start_server { |server| + server.onopen { + server.instance_variable_get(:@handler).class.should == EventMachine::WebSocket::Handler06 + } + } + + start_client { |client| + client.onopen { + client.handshake_response.lines.sort. + should == format_response(@response).lines.sort + done + } + } + } + end + + it "should accept a single-frame text message (masked)" do + em { + start_server { |server| + server.onmessage { |msg| + msg.should == 'Hello' + if msg.respond_to?(:encoding) + msg.encoding.should == Encoding.find("UTF-8") + end + done + } + server.onerror { + fail + } + } + + start_client { |client| + client.onopen { + client.send_data("\x00\x00\x01\x00\x84\x05Ielln") + } + } + } + end + + it "should return close code and reason if closed via handshake" do + em { + start_server { |ws| + ws.onclose { |event| + # 2. Receive close event in server + event.should == { + :code => 4004, + :reason => "close reason", + :was_clean => true, + } + done + } + } + start_client { |client| + client.onopen { + # 1: Send close handshake + close_data = [4004].pack('n') + close_data << "close reason" + client.send_frame(:close, close_data) + } + } + } + end + + it "should return close code 1005 if no code was specified" do + em { + start_server { |ws| + ws.onclose { |event| + event.should == { + :code => 1005, + :reason => "", + :was_clean => true, + } + done + } + } + start_client { |client| + client.onopen { + client.send_frame(:close, '') + } + } + } + end + + it "should report that close codes are supported" do + em { + start_server { |ws| + ws.onopen { + ws.supports_close_codes?.should == true + done + } + } + start_client + } + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft13_spec.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft13_spec.rb new file mode 100644 index 0000000..10dc2a3 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft13_spec.rb @@ -0,0 +1,105 @@ +# encoding: BINARY + +require 'helper' + +describe "draft13" do + include EM::SpecHelper + default_timeout 1 + + before :each do + @request = { + :port => 80, + :method => "GET", + :path => "/demo", + :headers => { + 'Host' => 'example.com', + 'Upgrade' => 'websocket', + 'Connection' => 'Upgrade', + 'Sec-WebSocket-Key' => 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Protocol' => 'sample', + 'Sec-WebSocket-Origin' => 'http://example.com', + 'Sec-WebSocket-Version' => '13' + } + } + + @response = { + :protocol => "HTTP/1.1 101 Switching Protocols\r\n", + :headers => { + "Upgrade" => "websocket", + "Connection" => "Upgrade", + "Sec-WebSocket-Accept" => "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", + "Sec-WebSocket-Protocol" => "sample", + } + } + end + + def start_client + client = EM.connect('0.0.0.0', 12345, Draft07FakeWebSocketClient) + client.send_data(format_request(@request)) + yield client if block_given? + return client + end + + it_behaves_like "a websocket server" do + let(:version) { 13 } + end + + it_behaves_like "a WebSocket server drafts 3 and above" do + let(:version) { 13 } + end + + it "should send back the correct handshake response" do + em { + start_server + + connection = start_client + + connection.onopen { + connection.handshake_response.lines.sort. + should == format_response(@response).lines.sort + done + } + } + end + + # TODO: This test would be much nicer with a real websocket client... + it "should support sending pings and binding to onpong" do + em { + start_server { |ws| + ws.onopen { + ws.should be_pingable + EM.next_tick { + ws.ping('hello').should == true + } + + } + ws.onpong { |data| + data.should == 'hello' + done + } + } + + connection = start_client + + # Confusing, fake onmessage means any data after the handshake + connection.onmessage { |data| + # This is what a ping looks like + data.should == "\x89\x05hello" + # This is what a pong looks like + connection.send_data("\x8a\x05hello") + } + } + end + + it "should report that close codes are supported" do + em { + start_server { |ws| + ws.onopen { + ws.supports_close_codes?.should == true + done + } + } + start_client + } + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft75_spec.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft75_spec.rb new file mode 100644 index 0000000..8d9faec --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft75_spec.rb @@ -0,0 +1,123 @@ +require 'helper' + +# These integration tests are older and use a different testing style to the +# integration tests for newer drafts. They use EM::HttpRequest which happens +# to currently estabish a websocket connection using the draft75 protocol. +# +describe "WebSocket server draft75" do + include EM::SpecHelper + default_timeout 1 + + def start_client + client = Draft75WebSocketClient.new + yield client if block_given? + return client + end + + it_behaves_like "a websocket server" do + let(:version) { 75 } + end + + it "should automatically complete WebSocket handshake" do + em { + MSG = "Hello World!" + EventMachine.add_timer(0.1) do + ws = EventMachine::WebSocketClient.connect('ws://127.0.0.1:12345/') + ws.errback { fail } + ws.callback { } + + ws.stream { |msg| + msg.data.should == MSG + EventMachine.stop + } + end + + start_server { |ws| + ws.onopen { + ws.send MSG + } + } + } + end + + it "should split multiple messages into separate callbacks" do + em { + messages = %w[1 2] + received = [] + + EventMachine.add_timer(0.1) do + ws = EventMachine::WebSocketClient.connect('ws://127.0.0.1:12345/') + ws.errback { fail } + ws.stream {|msg|} + ws.callback { + ws.send_msg messages[0] + ws.send_msg messages[1] + } + end + + start_server { |ws| + ws.onopen {} + ws.onclose {} + ws.onmessage {|msg| + msg.should == messages[received.size] + received.push msg + + EventMachine.stop if received.size == messages.size + } + } + } + end + + it "should call onclose callback when client closes connection" do + em { + EventMachine.add_timer(0.1) do + ws = EventMachine::WebSocketClient.connect('ws://127.0.0.1:12345/') + ws.errback { fail } + ws.callback { + ws.close_connection + } + ws.stream{|msg|} + end + + start_server { |ws| + ws.onopen {} + ws.onclose { + ws.state.should == :closed + EventMachine.stop + } + } + } + end + + it "should call onerror callback with raised exception and close connection on bad handshake" do + em { + EventMachine.add_timer(0.1) do + http = EM::HttpRequest.new('http://127.0.0.1:12345/').get + http.errback { } + http.callback { fail } + end + + start_server { |ws| + ws.onopen { fail } + ws.onclose { EventMachine.stop } + ws.onerror {|e| + e.should be_an_instance_of EventMachine::WebSocket::HandshakeError + e.message.should match('Not an upgrade request') + EventMachine.stop + } + } + } + end + + it "should report that close codes are not supported" do + em { + start_server { |ws| + ws.onopen { + ws.supports_close_codes?.should == false + done + } + } + start_client + } + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft76_spec.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft76_spec.rb new file mode 100644 index 0000000..f1a14e6 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/draft76_spec.rb @@ -0,0 +1,234 @@ +# encoding: BINARY + +require 'helper' + +describe "WebSocket server draft76" do + include EM::SpecHelper + default_timeout 1 + + before :each do + @request = { + :port => 80, + :method => "GET", + :path => "/demo", + :headers => { + 'Host' => 'example.com', + 'Connection' => 'Upgrade', + 'Sec-WebSocket-Key2' => '12998 5 Y3 1 .P00', + 'Sec-WebSocket-Protocol' => 'sample', + 'Upgrade' => 'WebSocket', + 'Sec-WebSocket-Key1' => '4 @1 46546xW%0l 1 5', + 'Origin' => 'http://example.com' + }, + :body => '^n:ds[4U' + } + + @response = { + :headers => { + "Upgrade" => "WebSocket", + "Connection" => "Upgrade", + "Sec-WebSocket-Location" => "ws://example.com/demo", + "Sec-WebSocket-Origin" => "http://example.com", + "Sec-WebSocket-Protocol" => "sample" + }, + :body => "8jKS\'y:G*Co,Wxa-" + } + end + + def start_client + client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient) + client.send_data(format_request(@request)) + yield client if block_given? + return client + end + + it_behaves_like "a websocket server" do + let(:version) { 76 } + end + + it "should send back the correct handshake response" do + em { + start_server + + start_client { |connection| + connection.onopen { + connection.handshake_response.lines.sort. + should == format_response(@response).lines.sort + done + } + } + } + end + + it "should send closing frame back and close the connection after recieving closing frame" do + em { + start_server + + connection = start_client + + # Send closing frame after handshake complete + connection.onopen { + connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING) + } + + # Check that this causes a termination string to be returned and the + # connection close + connection.onclose { + connection.packets[0].should == + EM::WebSocket::Handler76::TERMINATE_STRING + done + } + } + end + + it "should ignore any data received after the closing frame" do + em { + start_server { |ws| + # Fail if foobar message is received + ws.onmessage { |msg| + fail + } + } + + connection = start_client + + # Send closing frame after handshake complete, followed by another msg + connection.onopen { + connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING) + connection.send('foobar') + } + + connection.onclose { + done + } + } + end + + it "should accept null bytes within the frame after a line return" do + em { + start_server { |ws| + ws.onmessage { |msg| + msg.should == "\n\000" + } + } + + connection = start_client + + # Send closing frame after handshake complete + connection.onopen { + connection.send_data("\000\n\000\377") + connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING) + } + + connection.onclose { + done + } + } + end + + it "should handle unreasonable frame lengths by calling onerror callback" do + em { + start_server { |server| + server.onerror { |error| + error.should be_an_instance_of EM::WebSocket::WSMessageTooBigError + error.message.should == "Frame length too long (1180591620717411303296 bytes)" + done + } + } + + client = start_client + + # This particular frame indicates a message length of + # 1180591620717411303296 bytes. Such a message would previously cause + # a "bignum too big to convert into `long'" error. + # However it is clearly unreasonable and should be rejected. + client.onopen { + client.send_data("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00") + } + } + end + + it "should handle impossible frames by calling onerror callback" do + em { + start_server { |server| + server.onerror { |error| + error.should be_an_instance_of EM::WebSocket::WSProtocolError + error.message.should == "Invalid frame received" + done + } + } + + client = start_client + + client.onopen { + client.send_data("foobar") # Does not start with \x00 or \xff + } + } + end + + it "should handle invalid http requests by raising HandshakeError passed to onerror callback" do + em { + start_server { |server| + server.onerror { |error| + error.should be_an_instance_of EM::WebSocket::HandshakeError + error.message.should == "Invalid HTTP header: Could not parse data entirely (1 != 29)" + done + } + } + + client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient) + client.send_data("This is not a HTTP header\r\n\r\n") + } + end + + it "should handle handshake request split into two TCP packets" do + em { + start_server + + # Create a fake client which sends draft 76 handshake + connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient) + data = format_request(@request) + # Sends first half of the request + connection.send_data(data[0...(data.length / 2)]) + + connection.onopen { + connection.handshake_response.lines.sort. + should == format_response(@response).lines.sort + done + } + + EM.add_timer(0.1) do + # Sends second half of the request + connection.send_data(data[(data.length / 2)..-1]) + end + } + end + + it "should report that close codes are not supported" do + em { + start_server { |ws| + ws.onopen { + ws.supports_close_codes?.should == false + done + } + } + start_client + } + end + + it "should call onclose when the server closes the connection [antiregression]" do + em { + start_server { |ws| + ws.onopen { + EM.add_timer(0.1) { + ws.close() + } + } + ws.onclose { + done + } + } + start_client + } + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/gte_03_examples.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/gte_03_examples.rb new file mode 100644 index 0000000..f841618 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/gte_03_examples.rb @@ -0,0 +1,42 @@ +shared_examples_for "a WebSocket server drafts 3 and above" do + it "should force close connections after a timeout if close handshake is not sent by the client" do + em { + server_onerror_fired = false + server_onclose_fired = false + client_got_close_handshake = false + + start_server(:close_timeout => 0.1) { |ws| + ws.onopen { + # 1: Send close handshake to client + EM.next_tick { ws.close(4999, "Close message") } + } + + ws.onerror { |e| + # 3: Client should receive onerror + e.class.should == EM::WebSocket::WSProtocolError + e.message.should == "Close handshake un-acked after 0.1s, closing tcp connection" + server_onerror_fired = true + } + + ws.onclose { + server_onclose_fired = true + } + } + start_client { |client| + client.onmessage { |msg| + # 2: Client does not respond to close handshake (the fake client + # doesn't understand them at all hence this is in onmessage) + msg.should =~ /Close message/ if version >= 6 + client_got_close_handshake = true + } + + client.onclose { + server_onerror_fired.should == true + server_onclose_fired.should == true + client_got_close_handshake.should == true + done + } + } + } + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/shared_examples.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/shared_examples.rb new file mode 100644 index 0000000..0cd6b42 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/integration/shared_examples.rb @@ -0,0 +1,265 @@ +# encoding: UTF-8 + +# These tests are run against all draft versions +# +shared_examples_for "a websocket server" do + it "should expose the protocol version" do + em { + start_server { |ws| + ws.onopen { |handshake| + handshake.protocol_version.should == version + done + } + } + + start_client + } + end + + it "should expose the origin header" do + em { + start_server { |ws| + ws.onopen { |handshake| + handshake.origin.should == 'http://example.com' + done + } + } + + start_client + } + end + + it "should expose the remote IP address" do + em { + start_server { |ws| + ws.onopen { + ws.remote_ip.should == "127.0.0.1" + done + } + } + + start_client + } + end + + it "should send messages successfully" do + em { + start_server { |ws| + ws.onmessage { |message| + message.should == "hello server" + done + } + } + + start_client { |client| + client.onopen { + client.send("hello server") + } + } + } + end + + it "should allow connection to be closed with valid close code" do + em { + start_server { |ws| + ws.onopen { + ws.close(4004, "Bye bye") + done + } + } + + start_client + # TODO: Use a real client which understands how to respond to closing + # handshakes, sending the handshake currently untested + } + end + + it "should raise error if if invalid close code is used" do + em { + start_server { |ws| + ws.onopen { + lambda { + ws.close(2000) + }.should raise_error("Application code may only use codes from 1000, 3000-4999") + done + } + } + + start_client + } + end + + it "should call onclose with was_clean set to false if connection closed without closing handshake by server" do + em { + start_server { |ws| + ws.onopen { + # Close tcp connection (no close handshake) + ws.close_connection + } + ws.onclose { |event| + event.should == {:code => 1006, :was_clean => false} + done + } + } + start_client + } + end + + it "should call onclose with was_clean set to false if connection closed without closing handshake by client" do + em { + start_server { |ws| + ws.onclose { |event| + event.should == {:code => 1006, :was_clean => false} + done + } + } + start_client { |client| + client.onopen { + # Close tcp connection (no close handshake) + client.close_connection + } + } + } + end + + it "should call onerror if an application error raised in onopen" do + em { + start_server { |ws| + ws.onopen { + raise "application error" + } + + ws.onerror { |e| + e.message.should == "application error" + done + } + } + + start_client + } + end + + it "should call onerror if an application error raised in onmessage" do + em { + start_server { |server| + server.onmessage { + raise "application error" + } + + server.onerror { |e| + e.message.should == "application error" + done + } + } + + start_client { |client| + client.onopen { + client.send('a message') + } + } + } + end + + it "should call onerror in an application error raised in onclose" do + em { + start_server { |server| + server.onclose { + raise "application error" + } + + server.onerror { |e| + e.message.should == "application error" + done + } + } + + start_client { |client| + client.onopen { + EM.add_timer(0.1) { + client.close_connection + } + } + } + } + end + + it "should close the connection when a too long frame is sent" do + em { + start_server { |server| + server.max_frame_size = 20 + + server.onerror { |e| + # 3: Error should be reported to server + e.class.should == EventMachine::WebSocket::WSMessageTooBigError + e.message.should =~ /Frame length too long/ + } + } + + start_client { |client| + client.onopen { + EM.next_tick { + client.send("This message is longer than 20 characters") + } + + } + + client.onmessage { |msg| + # 4: This is actually the close message. Really need to use a real + # WebSocket client in these tests... + done + } + + client.onclose { + # 4: Drafts 75 & 76 don't send a close message, they just close the + # connection + done + } + } + } + end + + # Only run these tests on ruby 1.9 + if "a".respond_to?(:force_encoding) + it "should raise error if you try to send non utf8 text data to ws" do + em { + start_server { |server| + server.onopen { + # Create a string which claims to be UTF-8 but which is not + s = "ê" # utf-8 string + s.encode!("ISO-8859-1") + s.force_encoding("UTF-8") + s.valid_encoding?.should == false # now invalid utf8 + + # Send non utf8 encoded data + server.send(s) + } + server.onerror { |error| + error.class.should == EventMachine::WebSocket::WebSocketError + error.message.should == "Data sent to WebSocket must be valid UTF-8 but was UTF-8 (valid: false)" + done + } + } + + start_client { } + } + end + + it "should not change the encoding of strings sent to send [antiregression]" do + em { + start_server { |server| + server.onopen { + s = "example string" + s.force_encoding("UTF-8") + + server.send(s) + + s.encoding.should == Encoding.find("UTF-8") + done + } + } + + start_client { } + } + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/unit/framing_spec.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/unit/framing_spec.rb new file mode 100644 index 0000000..b2a8d33 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/unit/framing_spec.rb @@ -0,0 +1,298 @@ +# encoding: BINARY + +require 'helper' + +describe EM::WebSocket::Framing03 do + class FramingContainer + include EM::WebSocket::Framing03 + + def initialize + @connection = Object.new + def @connection.max_frame_size + 1000000 + end + end + + def <<(data) + @data << data + process_data + end + + def debug(*args); end + end + + before :each do + @f = FramingContainer.new + @f.initialize_framing + end + + describe "basic examples" do + it "connection close" do + @f.should_receive(:message).with(:close, '', '') + @f << 0b00000001 + @f << 0b00000000 + end + + it "ping" do + @f.should_receive(:message).with(:ping, '', '') + @f << 0b00000010 + @f << 0b00000000 + end + + it "pong" do + @f.should_receive(:message).with(:pong, '', '') + @f << 0b00000011 + @f << 0b00000000 + end + + it "text" do + @f.should_receive(:message).with(:text, '', 'foo') + @f << 0b00000100 + @f << 0b00000011 + @f << 'foo' + end + + it "Text in two frames" do + @f.should_receive(:message).with(:text, '', 'hello world') + @f << 0b10000100 + @f << 0b00000110 + @f << "hello " + @f << 0b00000000 + @f << 0b00000101 + @f << "world" + end + + it "2 byte extended payload length text frame" do + data = 'a' * 256 + @f.should_receive(:message).with(:text, '', data) + @f << 0b00000100 # Single frame, text + @f << 0b01111110 # Length 126 (so read 2 bytes) + @f << 0b00000001 # Two bytes in network byte order (256) + @f << 0b00000000 + @f << data + end + end + + # These examples are straight from the spec + # http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-03#section-4.6 + describe "examples from the spec" do + it "a single-frame text message" do + @f.should_receive(:message).with(:text, '', 'Hello') + @f << "\x04\x05Hello" + end + + it "a fragmented text message" do + @f.should_receive(:message).with(:text, '', 'Hello') + @f << "\x84\x03Hel" + @f << "\x00\x02lo" + end + + it "Ping request and response" do + @f.should_receive(:message).with(:ping, '', 'Hello') + @f << "\x02\x05Hello" + end + + it "256 bytes binary message in a single frame" do + data = "a"*256 + @f.should_receive(:message).with(:binary, '', data) + @f << "\x05\x7E\x01\x00" + data + end + + it "64KiB binary message in a single frame" do + data = "a"*65536 + @f.should_receive(:message).with(:binary, '', data) + @f << "\x05\x7F\x00\x00\x00\x00\x00\x01\x00\x00" + data + end + end + + describe "other tests" do + it "should accept a fragmented unmasked text message in 3 frames" do + @f.should_receive(:message).with(:text, '', 'Hello world') + @f << "\x84\x03Hel" + @f << "\x80\x02lo" + @f << "\x00\x06 world" + end + end + + describe "error cases" do + it "should raise an exception on continuation frame without preceeding more frame" do + lambda { + @f << 0b00000000 # Single frame, continuation + @f << 0b00000001 # Length 1 + @f << 'f' + }.should raise_error(EM::WebSocket::WebSocketError, 'Continuation frame not expected') + end + end +end + +# These examples are straight from the spec +# http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-03#section-4.6 +describe EM::WebSocket::Framing04 do + class FramingContainer04 + include EM::WebSocket::Framing04 + + def initialize + @connection = Object.new + def @connection.max_frame_size + 1000000 + end + end + + def <<(data) + @data << data + process_data + end + + def debug(*args); end + end + + before :each do + @f = FramingContainer04.new + @f.initialize_framing + end + + describe "examples from the spec" do + it "a single-frame text message" do + @f.should_receive(:message).with(:text, '', 'Hello') + @f << "\x84\x05\x48\x65\x6c\x6c\x6f" # "\x84\x05Hello" + end + + it "a fragmented text message" do + @f.should_receive(:message).with(:text, '', 'Hello') + @f << "\x04\x03Hel" + @f << "\x80\x02lo" + end + + it "Ping request" do + @f.should_receive(:message).with(:ping, '', 'Hello') + @f << "\x82\x05Hello" + end + + it "a pong response" do + @f.should_receive(:message).with(:pong, '', 'Hello') + @f << "\x83\x05Hello" + end + + it "256 bytes binary message in a single frame" do + data = "a"*256 + @f.should_receive(:message).with(:binary, '', data) + @f << "\x85\x7E\x01\x00" + data + end + + it "64KiB binary message in a single frame" do + data = "a"*65536 + @f.should_receive(:message).with(:binary, '', data) + @f << "\x85\x7F\x00\x00\x00\x00\x00\x01\x00\x00" + data + end + end + + describe "other tests" do + it "should accept a fragmented unmasked text message in 3 frames" do + @f.should_receive(:message).with(:text, '', 'Hello world') + @f << "\x04\x03Hel" + @f << "\x00\x02lo" + @f << "\x80\x06 world" + end + end +end + +describe EM::WebSocket::Framing07 do + class FramingContainer07 + include EM::WebSocket::Framing07 + + def initialize + @connection = Object.new + def @connection.max_frame_size + 1000000 + end + end + + def <<(data) + @data << data + process_data + end + + def debug(*args); end + end + + before :each do + @f = FramingContainer07.new + @f.initialize_framing + end + + # These examples are straight from the spec + # http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-07#section-4.6 + describe "examples from the spec" do + it "a single-frame unmakedtext message" do + @f.should_receive(:message).with(:text, '', 'Hello') + @f << "\x81\x05\x48\x65\x6c\x6c\x6f" # "\x84\x05Hello" + end + + it "a single-frame masked text message" do + @f.should_receive(:message).with(:text, '', 'Hello') + @f << "\x81\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58" # "\x84\x05Hello" + end + + it "a fragmented unmasked text message" do + @f.should_receive(:message).with(:text, '', 'Hello') + @f << "\x01\x03Hel" + @f << "\x80\x02lo" + end + + it "Ping request" do + @f.should_receive(:message).with(:ping, '', 'Hello') + @f << "\x89\x05Hello" + end + + it "a pong response" do + @f.should_receive(:message).with(:pong, '', 'Hello') + @f << "\x8a\x05Hello" + end + + it "256 bytes binary message in a single unmasked frame" do + data = "a"*256 + @f.should_receive(:message).with(:binary, '', data) + @f << "\x82\x7E\x01\x00" + data + end + + it "64KiB binary message in a single unmasked frame" do + data = "a"*65536 + @f.should_receive(:message).with(:binary, '', data) + @f << "\x82\x7F\x00\x00\x00\x00\x00\x01\x00\x00" + data + end + end + + describe "other tests" do + it "should raise a WSProtocolError if an invalid frame type is requested" do + lambda { + # Opcode 3 is not supported by this draft + @f << "\x83\x05Hello" + }.should raise_error(EventMachine::WebSocket::WSProtocolError, "Unknown opcode 3") + end + + it "should accept a fragmented unmasked text message in 3 frames" do + @f.should_receive(:message).with(:text, '', 'Hello world') + @f << "\x01\x03Hel" + @f << "\x00\x02lo" + @f << "\x80\x06 world" + end + + it "should raise if non-fin frame is followed by a non-continuation data frame (continuation frame would be expected)" do + lambda { + @f << 0b00000001 # Not fin, text + @f << 0b00000001 # Length 1 + @f << 'f' + @f << 0b10000001 # fin, text (continutation expected) + @f << 0b00000001 # Length 1 + @f << 'b' + }.should raise_error(EM::WebSocket::WebSocketError, 'Continuation frame expected') + end + + it "should raise on non-fin control frames (control frames must not be fragmented)" do + lambda { + @f << 0b00001010 # Not fin, pong (opcode 10) + @f << 0b00000000 # Length 1 + }.should raise_error(EM::WebSocket::WebSocketError, 'Control frames must not be fragmented') + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/unit/handshake_spec.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/unit/handshake_spec.rb new file mode 100644 index 0000000..2754ba7 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/unit/handshake_spec.rb @@ -0,0 +1,216 @@ +require 'helper' + +describe EM::WebSocket::Handshake do + def handshake(request, secure = false) + handshake = EM::WebSocket::Handshake.new(secure) + handshake.receive_data(format_request(request)) + handshake + end + + before :each do + @request = { + :port => 80, + :method => "GET", + :path => "/demo", + :headers => { + 'Host' => 'example.com', + 'Connection' => 'Upgrade', + 'Sec-WebSocket-Key2' => '12998 5 Y3 1 .P00', + 'Sec-WebSocket-Protocol' => 'sample', + 'Upgrade' => 'WebSocket', + 'Sec-WebSocket-Key1' => '4 @1 46546xW%0l 1 5', + 'Origin' => 'http://example.com' + }, + :body => '^n:ds[4U' + } + @secure_request = @request.merge(:port => 443) + + @response = { + :headers => { + "Upgrade" => "WebSocket", + "Connection" => "Upgrade", + "Sec-WebSocket-Location" => "ws://example.com/demo", + "Sec-WebSocket-Origin" => "http://example.com", + "Sec-WebSocket-Protocol" => "sample" + }, + :body => "8jKS\'y:G*Co,Wxa-" + } + @secure_response = @response.merge(:headers => @response[:headers].merge('Sec-WebSocket-Location' => "wss://example.com/demo")) + end + + it "should handle good request" do + handshake(@request).should succeed_with_upgrade(@response) + end + + it "should handle good request to secure default port if secure mode is enabled" do + handshake(@secure_request, true). + should succeed_with_upgrade(@secure_response) + end + + it "should not handle good request to secure default port if secure mode is disabled" do + handshake(@secure_request, false). + should_not succeed_with_upgrade(@secure_response) + end + + it "should handle good request on nondefault port" do + @request[:port] = 8081 + @request[:headers]['Host'] = 'example.com:8081' + @response[:headers]['Sec-WebSocket-Location'] = + 'ws://example.com:8081/demo' + + handshake(@request).should succeed_with_upgrade(@response) + end + + it "should handle good request to secure nondefault port" do + @secure_request[:port] = 8081 + @secure_request[:headers]['Host'] = 'example.com:8081' + @secure_response[:headers]['Sec-WebSocket-Location'] = 'wss://example.com:8081/demo' + + handshake(@secure_request, true). + should succeed_with_upgrade(@secure_response) + end + + it "should handle good request with no protocol" do + @request[:headers].delete('Sec-WebSocket-Protocol') + @response[:headers].delete("Sec-WebSocket-Protocol") + + handshake(@request).should succeed_with_upgrade(@response) + end + + it "should handle extra headers by simply ignoring them" do + @request[:headers]['EmptyValue'] = "" + @request[:headers]['AKey'] = "AValue" + + handshake(@request).should succeed_with_upgrade(@response) + end + + it "should raise error on HTTP request" do + @request[:headers] = { + 'Host' => 'www.google.com', + 'User-Agent' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 GTB6 GTBA', + 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'Accept-Language' => 'en-us,en;q=0.5', + 'Accept-Encoding' => 'gzip,deflate', + 'Accept-Charset' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', + 'Keep-Alive' => '300', + 'Connection' => 'keep-alive', + } + + handshake(@request).should fail_with_error(EM::WebSocket::HandshakeError) + end + + it "should raise error on wrong method" do + @request[:method] = 'POST' + + handshake(@request).should fail_with_error(EM::WebSocket::HandshakeError) + end + + it "should raise error if upgrade header incorrect" do + @request[:headers]['Upgrade'] = 'NonWebSocket' + + handshake(@request).should fail_with_error(EM::WebSocket::HandshakeError) + end + + it "should raise error if Sec-WebSocket-Protocol is empty" do + @request[:headers]['Sec-WebSocket-Protocol'] = '' + + handshake(@request).should fail_with_error(EM::WebSocket::HandshakeError) + end + + %w[Sec-WebSocket-Key1 Sec-WebSocket-Key2].each do |header| + it "should raise error if #{header} has zero spaces" do + @request[:headers][header] = 'nospaces' + + handshake(@request). + should fail_with_error(EM::WebSocket::HandshakeError, 'Websocket Key1 or Key2 does not contain spaces - this is a symptom of a cross-protocol attack') + end + end + + it "should raise error if Sec-WebSocket-Key1 is missing" do + @request[:headers].delete("Sec-WebSocket-Key1") + + # The error message isn't correct since key1 is used to heuristically + # determine the protocol version in use, however this test at least checks + # that the handshake does correctly fail + handshake(@request). + should fail_with_error(EM::WebSocket::HandshakeError, 'Extra bytes after header') + end + + it "should raise error if Sec-WebSocket-Key2 is missing" do + @request[:headers].delete("Sec-WebSocket-Key2") + + handshake(@request). + should fail_with_error(EM::WebSocket::HandshakeError, 'WebSocket key1 or key2 is missing') + end + + it "should raise error if spaces do not divide numbers in Sec-WebSocket-Key* " do + @request[:headers]['Sec-WebSocket-Key2'] = '12998 5 Y3 1.P00' + + handshake(@request). + should fail_with_error(EM::WebSocket::HandshakeError, 'Invalid Key "12998 5 Y3 1.P00"') + end + + it "should raise error if the HTTP header is empty" do + handshake = EM::WebSocket::Handshake.new(false) + handshake.receive_data("\r\n\r\nfoobar") + + handshake. + should fail_with_error(EM::WebSocket::HandshakeError, 'Invalid HTTP header: Could not parse data entirely (4 != 10)') + end + + # This might seems crazy, but very occasionally we saw multiple "Upgrade: + # WebSocket" headers in the wild. RFC 4.2.1 isn't particularly clear on this + # point, so for now I have decided not to accept --@mloughran + it "should raise error on multiple upgrade headers" do + handshake = EM::WebSocket::Handshake.new(false) + + # Add a duplicate upgrade header + headers = format_request(@request) + upgrade_header = "Upgrade: WebSocket\r\n" + headers.gsub!(upgrade_header, "#{upgrade_header}#{upgrade_header}") + + handshake.receive_data(headers) + + handshake.errback { |e| + e.class.should == EM::WebSocket::HandshakeError + e.message.should == 'Invalid upgrade header: ["WebSocket", "WebSocket"]' + } + end + + it "should cope with requests where the header is split" do + request = format_request(@request) + incomplete_request = request[0...(request.length / 2)] + rest = request[(request.length / 2)..-1] + handshake = EM::WebSocket::Handshake.new(false) + handshake.receive_data(incomplete_request) + + handshake.instance_variable_get(:@deferred_status).should == nil + + # Send the remaining header + handshake.receive_data(rest) + + handshake(@request).should succeed_with_upgrade(@response) + end + + it "should cope with requests where the third key is split" do + request = format_request(@request) + # Removes last two bytes of the third key + incomplete_request = request[0..-3] + rest = request[-2..-1] + handshake = EM::WebSocket::Handshake.new(false) + handshake.receive_data(incomplete_request) + + handshake.instance_variable_get(:@deferred_status).should == nil + + # Send the remaining third key + handshake.receive_data(rest) + + handshake(@request).should succeed_with_upgrade(@response) + end + + it "should fail if the request URI is invalid" do + @request[:path] = "/%" + handshake(@request).should \ + fail_with_error(EM::WebSocket::HandshakeError, 'Invalid request URI: /%') + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/unit/masking_spec.rb b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/unit/masking_spec.rb new file mode 100644 index 0000000..065aa95 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/em-websocket-0.5.3/spec/unit/masking_spec.rb @@ -0,0 +1,29 @@ +# encoding: BINARY + +require 'helper' + +describe EM::WebSocket::MaskedString do + it "should allow reading 4 byte mask and unmasking byte / bytes" do + t = EM::WebSocket::MaskedString.new("\x00\x00\x00\x01\x00\x01\x00\x01") + t.read_mask + t.getbyte(3).should == 0x00 + t.getbytes(4, 4).should == "\x00\x01\x00\x00" + t.getbytes(5, 3).should == "\x01\x00\x00" + end + + it "should return nil from getbyte if index requested is out of range" do + t = EM::WebSocket::MaskedString.new("\x00\x00\x00\x00\x53") + t.read_mask + t.getbyte(4).should == 0x53 + t.getbyte(5).should == nil + end + + it "should allow switching masking on and off" do + t = EM::WebSocket::MaskedString.new("\x02\x00\x00\x00\x03") + t.getbyte(4).should == 0x03 + t.read_mask + t.getbyte(4).should == 0x01 + t.unset_mask + t.getbyte(4).should == 0x03 + end +end -- cgit v1.2.3