File: draft03_spec.rb

package info (click to toggle)
ruby-em-websocket 0.5.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 416 kB
  • sloc: ruby: 3,137; makefile: 5
file content (298 lines) | stat: -rw-r--r-- 7,367 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
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