websocket-rack 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -0
- data/Gemfile +3 -0
- data/README.md +105 -0
- data/Rakefile +11 -0
- data/example/example.ru +31 -0
- data/example/html/FABridge.js +604 -0
- data/example/html/WebSocketMain.swf +0 -0
- data/example/html/index.html +76 -0
- data/example/html/swfobject.js +4 -0
- data/example/html/web_socket.js +388 -0
- data/lib/rack/websocket/application.rb +65 -0
- data/lib/rack/websocket/connection.rb +105 -0
- data/lib/rack/websocket/debugger.rb +17 -0
- data/lib/rack/websocket/extensions/thin/connection.rb +71 -0
- data/lib/rack/websocket/extensions/thin.rb +16 -0
- data/lib/rack/websocket/framing03.rb +178 -0
- data/lib/rack/websocket/framing76.rb +115 -0
- data/lib/rack/websocket/handler.rb +43 -0
- data/lib/rack/websocket/handler03.rb +14 -0
- data/lib/rack/websocket/handler75.rb +8 -0
- data/lib/rack/websocket/handler76.rb +11 -0
- data/lib/rack/websocket/handler_factory.rb +61 -0
- data/lib/rack/websocket/handshake75.rb +21 -0
- data/lib/rack/websocket/handshake76.rb +71 -0
- data/lib/rack/websocket.rb +40 -0
- data/spec/helper.rb +44 -0
- data/spec/integration/draft03_spec.rb +252 -0
- data/spec/integration/draft76_spec.rb +212 -0
- data/spec/unit/framing_spec.rb +108 -0
- data/spec/unit/handler_spec.rb +136 -0
- data/spec/websocket_spec.rb +210 -0
- data/websocket-rack.gemspec +23 -0
- metadata +147 -0
| @@ -0,0 +1,212 @@ | |
| 1 | 
            +
            # require 'helper'
         | 
| 2 | 
            +
            # 
         | 
| 3 | 
            +
            # describe "WebSocket server draft76" do
         | 
| 4 | 
            +
            #   before :each do
         | 
| 5 | 
            +
            #     @request = {
         | 
| 6 | 
            +
            #       :port => 80,
         | 
| 7 | 
            +
            #       :method => "GET",
         | 
| 8 | 
            +
            #       :path => "/demo",
         | 
| 9 | 
            +
            #       :headers => {
         | 
| 10 | 
            +
            #         'Host' => 'example.com',
         | 
| 11 | 
            +
            #         'Connection' => 'Upgrade',
         | 
| 12 | 
            +
            #         'Sec-WebSocket-Key2' => '12998 5 Y3 1  .P00',
         | 
| 13 | 
            +
            #         'Sec-WebSocket-Protocol' => 'sample',
         | 
| 14 | 
            +
            #         'Upgrade' => 'WebSocket',
         | 
| 15 | 
            +
            #         'Sec-WebSocket-Key1' => '4 @1  46546xW%0l 1 5',
         | 
| 16 | 
            +
            #         'Origin' => 'http://example.com'
         | 
| 17 | 
            +
            #       },
         | 
| 18 | 
            +
            #       :body => '^n:ds[4U'
         | 
| 19 | 
            +
            #     }
         | 
| 20 | 
            +
            # 
         | 
| 21 | 
            +
            #     @response = {
         | 
| 22 | 
            +
            #       :headers => {
         | 
| 23 | 
            +
            #         "Upgrade" => "WebSocket",
         | 
| 24 | 
            +
            #         "Connection" => "Upgrade",
         | 
| 25 | 
            +
            #         "Sec-WebSocket-Location" => "ws://example.com/demo",
         | 
| 26 | 
            +
            #         "Sec-WebSocket-Origin" => "http://example.com",
         | 
| 27 | 
            +
            #         "Sec-WebSocket-Protocol" => "sample"
         | 
| 28 | 
            +
            #       },
         | 
| 29 | 
            +
            #       :body => "8jKS\'y:G*Co,Wxa-"
         | 
| 30 | 
            +
            #     }
         | 
| 31 | 
            +
            #   end
         | 
| 32 | 
            +
            #   
         | 
| 33 | 
            +
            #   it "should send back the correct handshake response" do
         | 
| 34 | 
            +
            #     EM.run do
         | 
| 35 | 
            +
            #       EM.add_timer(0.1) do
         | 
| 36 | 
            +
            #         EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
         | 
| 37 | 
            +
            #         
         | 
| 38 | 
            +
            #         # Create a fake client which sends draft 76 handshake
         | 
| 39 | 
            +
            #         connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
         | 
| 40 | 
            +
            #         connection.send_data(format_request(@request))
         | 
| 41 | 
            +
            #         
         | 
| 42 | 
            +
            #         connection.onopen = lambda {
         | 
| 43 | 
            +
            #           connection.handshake_response.lines.sort.
         | 
| 44 | 
            +
            #             should == format_response(@response).lines.sort
         | 
| 45 | 
            +
            #           EM.stop
         | 
| 46 | 
            +
            #         }
         | 
| 47 | 
            +
            #       end
         | 
| 48 | 
            +
            #     end
         | 
| 49 | 
            +
            #   end
         | 
| 50 | 
            +
            #   
         | 
| 51 | 
            +
            #   it "should send closing frame back and close the connection after recieving closing frame" do
         | 
| 52 | 
            +
            #     EM.run do
         | 
| 53 | 
            +
            #       EM.add_timer(0.1) do
         | 
| 54 | 
            +
            #         EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
         | 
| 55 | 
            +
            #   
         | 
| 56 | 
            +
            #         # Create a fake client which sends draft 76 handshake
         | 
| 57 | 
            +
            #         connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
         | 
| 58 | 
            +
            #         connection.send_data(format_request(@request))
         | 
| 59 | 
            +
            #   
         | 
| 60 | 
            +
            #         # Send closing frame after handshake complete
         | 
| 61 | 
            +
            #         connection.onopen = lambda {
         | 
| 62 | 
            +
            #           connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING)
         | 
| 63 | 
            +
            #         }
         | 
| 64 | 
            +
            #   
         | 
| 65 | 
            +
            #         # Check that this causes a termination string to be returned and the 
         | 
| 66 | 
            +
            #         # connection close
         | 
| 67 | 
            +
            #         connection.onclose = lambda {
         | 
| 68 | 
            +
            #           connection.packets[0].should == 
         | 
| 69 | 
            +
            #             EM::WebSocket::Handler76::TERMINATE_STRING
         | 
| 70 | 
            +
            #           EM.stop
         | 
| 71 | 
            +
            #         }
         | 
| 72 | 
            +
            #       end
         | 
| 73 | 
            +
            #     end
         | 
| 74 | 
            +
            #   end
         | 
| 75 | 
            +
            #   
         | 
| 76 | 
            +
            #   it "should ignore any data received after the closing frame" do
         | 
| 77 | 
            +
            #     EM.run do
         | 
| 78 | 
            +
            #       EM.add_timer(0.1) do
         | 
| 79 | 
            +
            #         EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
         | 
| 80 | 
            +
            #           # Fail if foobar message is received
         | 
| 81 | 
            +
            #           ws.onmessage { |msg|
         | 
| 82 | 
            +
            #             failed
         | 
| 83 | 
            +
            #           }
         | 
| 84 | 
            +
            #         }
         | 
| 85 | 
            +
            #         
         | 
| 86 | 
            +
            #         # Create a fake client which sends draft 76 handshake
         | 
| 87 | 
            +
            #         connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
         | 
| 88 | 
            +
            #         connection.send_data(format_request(@request))
         | 
| 89 | 
            +
            #   
         | 
| 90 | 
            +
            #         # Send closing frame after handshake complete, followed by another msg
         | 
| 91 | 
            +
            #         connection.onopen = lambda {
         | 
| 92 | 
            +
            #           connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING)
         | 
| 93 | 
            +
            #           connection.send('foobar')
         | 
| 94 | 
            +
            #         }
         | 
| 95 | 
            +
            #   
         | 
| 96 | 
            +
            #         connection.onclose = lambda {
         | 
| 97 | 
            +
            #           EM.stop
         | 
| 98 | 
            +
            #         }
         | 
| 99 | 
            +
            #       end
         | 
| 100 | 
            +
            #     end
         | 
| 101 | 
            +
            #   end
         | 
| 102 | 
            +
            # 
         | 
| 103 | 
            +
            #   it "should accept null bytes within the frame after a line return" do
         | 
| 104 | 
            +
            #     EM.run do
         | 
| 105 | 
            +
            #       EM.add_timer(0.1) do
         | 
| 106 | 
            +
            #         EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
         | 
| 107 | 
            +
            #           ws.onmessage { |msg|
         | 
| 108 | 
            +
            #             msg.should == "\n\000" 
         | 
| 109 | 
            +
            #           }
         | 
| 110 | 
            +
            #         }
         | 
| 111 | 
            +
            #   
         | 
| 112 | 
            +
            #         # Create a fake client which sends draft 76 handshake
         | 
| 113 | 
            +
            #         connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
         | 
| 114 | 
            +
            #         connection.send_data(format_request(@request))
         | 
| 115 | 
            +
            #   
         | 
| 116 | 
            +
            #         # Send closing frame after handshake complete
         | 
| 117 | 
            +
            #         connection.onopen = lambda {
         | 
| 118 | 
            +
            #           connection.send_data("\000\n\000\377")
         | 
| 119 | 
            +
            #           connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING)
         | 
| 120 | 
            +
            #         }
         | 
| 121 | 
            +
            #   
         | 
| 122 | 
            +
            #         connection.onclose = lambda {
         | 
| 123 | 
            +
            #           EM.stop
         | 
| 124 | 
            +
            #         }
         | 
| 125 | 
            +
            #       end
         | 
| 126 | 
            +
            #     end
         | 
| 127 | 
            +
            #   end
         | 
| 128 | 
            +
            # 
         | 
| 129 | 
            +
            #   it "should handle unreasonable frame lengths by calling onerror callback" do
         | 
| 130 | 
            +
            #     EM.run do
         | 
| 131 | 
            +
            #       EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |server|
         | 
| 132 | 
            +
            #         server.onerror { |error|
         | 
| 133 | 
            +
            #           error.should be_an_instance_of EM::WebSocket::DataError
         | 
| 134 | 
            +
            #           error.message.should == "Frame length too long (1180591620717411303296 bytes)"
         | 
| 135 | 
            +
            #           EM.stop
         | 
| 136 | 
            +
            #         }
         | 
| 137 | 
            +
            #       }
         | 
| 138 | 
            +
            # 
         | 
| 139 | 
            +
            #       # Create a fake client which sends draft 76 handshake
         | 
| 140 | 
            +
            #       client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
         | 
| 141 | 
            +
            #       client.send_data(format_request(@request))
         | 
| 142 | 
            +
            # 
         | 
| 143 | 
            +
            #       # This particular frame indicates a message length of
         | 
| 144 | 
            +
            #       # 1180591620717411303296 bytes. Such a message would previously cause
         | 
| 145 | 
            +
            #       # a "bignum too big to convert into `long'" error.
         | 
| 146 | 
            +
            #       # However it is clearly unreasonable and should be rejected.
         | 
| 147 | 
            +
            #       client.onopen = lambda {
         | 
| 148 | 
            +
            #         client.send_data("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00")
         | 
| 149 | 
            +
            #       }
         | 
| 150 | 
            +
            #     end
         | 
| 151 | 
            +
            #   end
         | 
| 152 | 
            +
            #   
         | 
| 153 | 
            +
            #   it "should handle impossible frames by calling onerror callback" do
         | 
| 154 | 
            +
            #     EM.run do
         | 
| 155 | 
            +
            #       EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |server|
         | 
| 156 | 
            +
            #         server.onerror { |error|
         | 
| 157 | 
            +
            #           error.should be_an_instance_of EM::WebSocket::DataError
         | 
| 158 | 
            +
            #           error.message.should == "Invalid frame received"
         | 
| 159 | 
            +
            #           EM.stop
         | 
| 160 | 
            +
            #         }
         | 
| 161 | 
            +
            #       }
         | 
| 162 | 
            +
            # 
         | 
| 163 | 
            +
            #       # Create a fake client which sends draft 76 handshake
         | 
| 164 | 
            +
            #       client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
         | 
| 165 | 
            +
            #       client.send_data(format_request(@request))
         | 
| 166 | 
            +
            # 
         | 
| 167 | 
            +
            #       client.onopen = lambda {
         | 
| 168 | 
            +
            #         client.send_data("foobar") # Does not start with \x00 or \xff
         | 
| 169 | 
            +
            #       }
         | 
| 170 | 
            +
            #     end
         | 
| 171 | 
            +
            #   end
         | 
| 172 | 
            +
            # 
         | 
| 173 | 
            +
            #   it "should handle invalid http requests by raising HandshakeError passed to onerror callback" do
         | 
| 174 | 
            +
            #     EM.run {
         | 
| 175 | 
            +
            #       EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |server|
         | 
| 176 | 
            +
            #         server.onerror { |error|
         | 
| 177 | 
            +
            #           error.should be_an_instance_of EM::WebSocket::HandshakeError
         | 
| 178 | 
            +
            #           error.message.should == "Invalid HTTP header"
         | 
| 179 | 
            +
            #           EM.stop
         | 
| 180 | 
            +
            #         }
         | 
| 181 | 
            +
            #       }
         | 
| 182 | 
            +
            #       
         | 
| 183 | 
            +
            #       client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
         | 
| 184 | 
            +
            #       client.send_data("This is not a HTTP header\r\n\r\n")
         | 
| 185 | 
            +
            #     }
         | 
| 186 | 
            +
            #   end
         | 
| 187 | 
            +
            # 
         | 
| 188 | 
            +
            #   it "should handle handshake request split into two TCP packets" do
         | 
| 189 | 
            +
            #     EM.run do
         | 
| 190 | 
            +
            #       EM.add_timer(0.1) do
         | 
| 191 | 
            +
            #         EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
         | 
| 192 | 
            +
            # 
         | 
| 193 | 
            +
            #         # Create a fake client which sends draft 76 handshake
         | 
| 194 | 
            +
            #         connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
         | 
| 195 | 
            +
            #         data = format_request(@request)
         | 
| 196 | 
            +
            #         # Sends first half of the request
         | 
| 197 | 
            +
            #         connection.send_data(data[0...(data.length / 2)])
         | 
| 198 | 
            +
            # 
         | 
| 199 | 
            +
            #         connection.onopen = lambda {
         | 
| 200 | 
            +
            #           connection.handshake_response.lines.sort.
         | 
| 201 | 
            +
            #             should == format_response(@response).lines.sort
         | 
| 202 | 
            +
            #           EM.stop
         | 
| 203 | 
            +
            #         }
         | 
| 204 | 
            +
            # 
         | 
| 205 | 
            +
            #         EM.add_timer(0.1) do
         | 
| 206 | 
            +
            #           # Sends second half of the request
         | 
| 207 | 
            +
            #           connection.send_data(data[(data.length / 2)..-1])
         | 
| 208 | 
            +
            #         end
         | 
| 209 | 
            +
            #       end
         | 
| 210 | 
            +
            #     end
         | 
| 211 | 
            +
            #   end
         | 
| 212 | 
            +
            # end
         | 
| @@ -0,0 +1,108 @@ | |
| 1 | 
            +
            require 'helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe Rack::WebSocket::Framing03 do
         | 
| 4 | 
            +
              class FramingContainer
         | 
| 5 | 
            +
                include Rack::WebSocket::Framing03
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def <<(data)
         | 
| 8 | 
            +
                  @data << data
         | 
| 9 | 
            +
                  process_data(data)
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def debug(*args); end
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              before :each do
         | 
| 16 | 
            +
                @f = FramingContainer.new
         | 
| 17 | 
            +
                @f.initialize_framing
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              describe "basic examples" do
         | 
| 21 | 
            +
                it "connection close" do
         | 
| 22 | 
            +
                  @f.should_receive(:message).with(:close, '', '')
         | 
| 23 | 
            +
                  @f << 0b00000001
         | 
| 24 | 
            +
                  @f << 0b00000000
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                it "ping" do
         | 
| 28 | 
            +
                  @f.should_receive(:message).with(:ping, '', '')
         | 
| 29 | 
            +
                  @f << 0b00000010
         | 
| 30 | 
            +
                  @f << 0b00000000
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                it "pong" do
         | 
| 34 | 
            +
                  @f.should_receive(:message).with(:pong, '', '')
         | 
| 35 | 
            +
                  @f << 0b00000011
         | 
| 36 | 
            +
                  @f << 0b00000000
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                it "text" do
         | 
| 40 | 
            +
                  @f.should_receive(:message).with(:text, '', 'foo')
         | 
| 41 | 
            +
                  @f << 0b00000100
         | 
| 42 | 
            +
                  @f << 0b00000011
         | 
| 43 | 
            +
                  @f << 'foo'
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                it "Text in two frames" do
         | 
| 47 | 
            +
                  @f.should_receive(:message).with(:text, '', 'hello world')
         | 
| 48 | 
            +
                  @f << 0b10000100
         | 
| 49 | 
            +
                  @f << 0b00000110
         | 
| 50 | 
            +
                  @f << "hello "
         | 
| 51 | 
            +
                  @f << 0b00000000
         | 
| 52 | 
            +
                  @f << 0b00000101
         | 
| 53 | 
            +
                  @f << "world"
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                it "2 byte extended payload length text frame" do
         | 
| 57 | 
            +
                  data = 'a' * 256
         | 
| 58 | 
            +
                  @f.should_receive(:message).with(:text, '', data)
         | 
| 59 | 
            +
                  @f << 0b00000100 # Single frame, text
         | 
| 60 | 
            +
                  @f << 0b01111110 # Length 126 (so read 2 bytes)
         | 
| 61 | 
            +
                  @f << 0b00000001 # Two bytes in network byte order (256)
         | 
| 62 | 
            +
                  @f << 0b00000000
         | 
| 63 | 
            +
                  @f << data
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
              # These examples are straight from the spec
         | 
| 68 | 
            +
              # http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-03#section-4.6
         | 
| 69 | 
            +
              describe "examples from the spec" do
         | 
| 70 | 
            +
                it "a single-frame text message" do
         | 
| 71 | 
            +
                  @f.should_receive(:message).with(:text, '', 'Hello')
         | 
| 72 | 
            +
                  @f << "\x04\x05Hello"
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                it "a fragmented text message" do
         | 
| 76 | 
            +
                  @f.should_receive(:message).with(:text, '', 'Hello')
         | 
| 77 | 
            +
                  @f << "\x84\x03Hel"
         | 
| 78 | 
            +
                  @f << "\x00\x02lo"
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                it "Ping request and response" do
         | 
| 82 | 
            +
                  @f.should_receive(:message).with(:ping, '', 'Hello')
         | 
| 83 | 
            +
                  @f << "\x02\x05Hello"
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                it "256 bytes binary message in a single frame" do
         | 
| 87 | 
            +
                  data = "a"*256
         | 
| 88 | 
            +
                  @f.should_receive(:message).with(:binary, '', data)
         | 
| 89 | 
            +
                  @f << "\x05\x7E\x01\x00" + data
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                it "64KiB binary message in a single frame" do
         | 
| 93 | 
            +
                  data = "a"*65536
         | 
| 94 | 
            +
                  @f.should_receive(:message).with(:binary, '', data)
         | 
| 95 | 
            +
                  @f << "\x05\x7F\x00\x00\x00\x00\x00\x01\x00\x00" + data
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
              end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
              describe "error cases" do
         | 
| 100 | 
            +
                it "should raise an exception on continuation frame without preceeding more frame" do
         | 
| 101 | 
            +
                  lambda {
         | 
| 102 | 
            +
                    @f << 0b00000000 # Single frame, continuation
         | 
| 103 | 
            +
                    @f << 0b00000001 # Length 1
         | 
| 104 | 
            +
                    @f << 'f'
         | 
| 105 | 
            +
                  }.should raise_error(Rack::WebSocket::WebSocketError, 'Continuation frame not expected')
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
              end
         | 
| 108 | 
            +
            end
         | 
| @@ -0,0 +1,136 @@ | |
| 1 | 
            +
            require 'helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe "Rack::WebSocket::Handler" do
         | 
| 4 | 
            +
              before :each do
         | 
| 5 | 
            +
                @request = {
         | 
| 6 | 
            +
                  :port => 80,
         | 
| 7 | 
            +
                  :method => "GET",
         | 
| 8 | 
            +
                  :path => "/demo",
         | 
| 9 | 
            +
                  :headers => {
         | 
| 10 | 
            +
                    'Host' => 'example.com',
         | 
| 11 | 
            +
                    'Connection' => 'Upgrade',
         | 
| 12 | 
            +
                    'Sec-WebSocket-Key2' => '12998 5 Y3 1  .P00',
         | 
| 13 | 
            +
                    'Sec-WebSocket-Protocol' => 'sample',
         | 
| 14 | 
            +
                    'Upgrade' => 'WebSocket',
         | 
| 15 | 
            +
                    'Sec-WebSocket-Key1' => '4 @1  46546xW%0l 1 5',
         | 
| 16 | 
            +
                    'Origin' => 'http://example.com'
         | 
| 17 | 
            +
                  },
         | 
| 18 | 
            +
                  :body => '^n:ds[4U'
         | 
| 19 | 
            +
                }
         | 
| 20 | 
            +
                @secure_request = @request.merge(:port => 443)
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                @response = {
         | 
| 23 | 
            +
                  :headers => {
         | 
| 24 | 
            +
                    "Upgrade" => "WebSocket",
         | 
| 25 | 
            +
                    "Connection" => "Upgrade",
         | 
| 26 | 
            +
                    "Sec-WebSocket-Location" => "ws://example.com/demo",
         | 
| 27 | 
            +
                    "Sec-WebSocket-Origin" => "http://example.com",
         | 
| 28 | 
            +
                    "Sec-WebSocket-Protocol" => "sample"
         | 
| 29 | 
            +
                  },
         | 
| 30 | 
            +
                  :body => "8jKS\'y:G*Co,Wxa-"
         | 
| 31 | 
            +
                }
         | 
| 32 | 
            +
                @secure_response = @response.merge(:headers => @response[:headers].merge('Sec-WebSocket-Location' => "wss://example.com:443/demo"))
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              it "should handle good request" do
         | 
| 36 | 
            +
                handler(@request).should send_handshake(@response)
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              it "should handle good request to secure default port if secure mode is enabled" do
         | 
| 40 | 
            +
                handler(@secure_request, true).should send_handshake(@secure_response)
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              it "should not handle good request to secure default port if secure mode is disabled" do
         | 
| 44 | 
            +
                handler(@secure_request, false).should_not send_handshake(@secure_response)
         | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              it "should handle good request on nondefault port" do
         | 
| 48 | 
            +
                @request[:port] = 8081
         | 
| 49 | 
            +
                @request[:headers]['Host'] = 'example.com:8081'
         | 
| 50 | 
            +
                @response[:headers]['Sec-WebSocket-Location'] =
         | 
| 51 | 
            +
                  'ws://example.com:8081/demo'
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                handler(@request).should send_handshake(@response)
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              it "should handle good request to secure nondefault port" do
         | 
| 57 | 
            +
                @secure_request[:port] = 8081
         | 
| 58 | 
            +
                @secure_request[:headers]['Host'] = 'example.com:8081'
         | 
| 59 | 
            +
                @secure_response[:headers]['Sec-WebSocket-Location'] = 'wss://example.com:8081/demo'
         | 
| 60 | 
            +
                handler(@secure_request, true).should send_handshake(@secure_response)
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
              it "should handle good request with no protocol" do
         | 
| 64 | 
            +
                @request[:headers].delete('Sec-WebSocket-Protocol')
         | 
| 65 | 
            +
                @response[:headers].delete("Sec-WebSocket-Protocol")
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                handler(@request).should send_handshake(@response)
         | 
| 68 | 
            +
              end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
              it "should handle extra headers by simply ignoring them" do
         | 
| 71 | 
            +
                @request[:headers]['EmptyValue'] = ""
         | 
| 72 | 
            +
                @request[:headers]['AKey'] = "AValue"
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                handler(@request).should send_handshake(@response)
         | 
| 75 | 
            +
              end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
              it "should raise error on HTTP request" do
         | 
| 78 | 
            +
                @request[:headers] = {
         | 
| 79 | 
            +
                  'Host' => 'www.google.com',
         | 
| 80 | 
            +
                  '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',
         | 
| 81 | 
            +
                  'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
         | 
| 82 | 
            +
                  'Accept-Language' => 'en-us,en;q=0.5',
         | 
| 83 | 
            +
                  'Accept-Encoding' => 'gzip,deflate',
         | 
| 84 | 
            +
                  'Accept-Charset' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
         | 
| 85 | 
            +
                  'Keep-Alive' => '300',
         | 
| 86 | 
            +
                  'Connection' => 'keep-alive',
         | 
| 87 | 
            +
                }
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                lambda {
         | 
| 90 | 
            +
                  handler(@request).handshake
         | 
| 91 | 
            +
                }.should raise_error(Rack::WebSocket::HandshakeError)
         | 
| 92 | 
            +
              end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
              it "should raise error on wrong method" do
         | 
| 95 | 
            +
                @request[:method] = 'POST'
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                lambda {
         | 
| 98 | 
            +
                  handler(@request).handshake
         | 
| 99 | 
            +
                }.should raise_error(Rack::WebSocket::HandshakeError)
         | 
| 100 | 
            +
              end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
              it "should raise error if upgrade header incorrect" do
         | 
| 103 | 
            +
                @request[:headers]['Upgrade'] = 'NonWebSocket'
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                lambda {
         | 
| 106 | 
            +
                  handler(@request).handshake
         | 
| 107 | 
            +
                }.should raise_error(Rack::WebSocket::HandshakeError)
         | 
| 108 | 
            +
              end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
              it "should raise error if Sec-WebSocket-Protocol is empty" do
         | 
| 111 | 
            +
                @request[:headers]['Sec-WebSocket-Protocol'] = ''
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                lambda {
         | 
| 114 | 
            +
                  handler(@request).handshake
         | 
| 115 | 
            +
                }.should raise_error(Rack::WebSocket::HandshakeError)
         | 
| 116 | 
            +
              end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
              %w[Sec-WebSocket-Key1 Sec-WebSocket-Key2].each do |header|
         | 
| 119 | 
            +
                it "should raise error if #{header} has zero spaces" do
         | 
| 120 | 
            +
                  @request[:headers][header] = 'nospaces'
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                  lambda {
         | 
| 123 | 
            +
                    handler(@request).handshake
         | 
| 124 | 
            +
                  }.should raise_error(Rack::WebSocket::HandshakeError, 'Websocket Key1 or Key2 does not contain spaces - this is a symptom of a cross-protocol attack')
         | 
| 125 | 
            +
                end
         | 
| 126 | 
            +
              end
         | 
| 127 | 
            +
              
         | 
| 128 | 
            +
              it "should raise error if spaces do not divide numbers in Sec-WebSocket-Key* " do
         | 
| 129 | 
            +
                @request[:headers]['Sec-WebSocket-Key2'] = '12998 5 Y3 1.P00'
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                lambda {
         | 
| 132 | 
            +
                  handler(@request).handshake
         | 
| 133 | 
            +
                }.should raise_error(Rack::WebSocket::HandshakeError, 'Invalid Key "12998 5 Y3 1.P00"')
         | 
| 134 | 
            +
              end
         | 
| 135 | 
            +
             | 
| 136 | 
            +
            end
         |