em-websocket 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/CHANGELOG.rdoc +10 -0
  2. data/README.md +16 -0
  3. data/examples/test.html +11 -8
  4. data/lib/em-websocket.rb +6 -3
  5. data/lib/em-websocket/close03.rb +11 -0
  6. data/lib/em-websocket/close05.rb +11 -0
  7. data/lib/em-websocket/close06.rb +16 -0
  8. data/lib/em-websocket/close75.rb +10 -0
  9. data/lib/em-websocket/connection.rb +58 -32
  10. data/lib/em-websocket/framing03.rb +9 -30
  11. data/lib/em-websocket/framing04.rb +15 -0
  12. data/lib/em-websocket/framing05.rb +157 -0
  13. data/lib/em-websocket/framing76.rb +5 -6
  14. data/lib/em-websocket/handler.rb +2 -4
  15. data/lib/em-websocket/handler03.rb +2 -6
  16. data/lib/em-websocket/handler05.rb +10 -0
  17. data/lib/em-websocket/handler06.rb +10 -0
  18. data/lib/em-websocket/handler75.rb +1 -0
  19. data/lib/em-websocket/handler76.rb +1 -0
  20. data/lib/em-websocket/handler_factory.rb +41 -22
  21. data/lib/em-websocket/handshake04.rb +35 -0
  22. data/lib/em-websocket/handshake75.rb +4 -4
  23. data/lib/em-websocket/handshake76.rb +8 -8
  24. data/lib/em-websocket/masking04.rb +27 -0
  25. data/lib/em-websocket/message_processor_03.rb +33 -0
  26. data/lib/em-websocket/message_processor_06.rb +46 -0
  27. data/lib/em-websocket/version.rb +1 -1
  28. data/spec/helper.rb +54 -2
  29. data/spec/integration/common_spec.rb +115 -0
  30. data/spec/integration/draft03_spec.rb +26 -11
  31. data/spec/integration/draft05_spec.rb +45 -0
  32. data/spec/integration/draft06_spec.rb +79 -0
  33. data/spec/integration/draft75_spec.rb +115 -0
  34. data/spec/integration/draft76_spec.rb +25 -10
  35. data/spec/integration/shared_examples.rb +62 -0
  36. data/spec/unit/framing_spec.rb +55 -0
  37. data/spec/unit/masking_spec.rb +18 -0
  38. metadata +29 -33
  39. data/spec/websocket_spec.rb +0 -210
@@ -1,4 +1,5 @@
1
1
  require 'helper'
2
+ require 'integration/shared_examples'
2
3
 
3
4
  describe "WebSocket server draft76" do
4
5
  before :each do
@@ -30,6 +31,20 @@ describe "WebSocket server draft76" do
30
31
  }
31
32
  end
32
33
 
34
+ it_behaves_like "a websocket server" do
35
+ def start_server
36
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
37
+ yield ws
38
+ }
39
+ end
40
+
41
+ def start_client
42
+ client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
43
+ client.send_data(format_request(@request))
44
+ yield client if block_given?
45
+ end
46
+ end
47
+
33
48
  it "should send back the correct handshake response" do
34
49
  EM.run do
35
50
  EM.add_timer(0.1) do
@@ -39,7 +54,7 @@ describe "WebSocket server draft76" do
39
54
  connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
40
55
  connection.send_data(format_request(@request))
41
56
 
42
- connection.onopen = lambda {
57
+ connection.onopen {
43
58
  connection.handshake_response.lines.sort.
44
59
  should == format_response(@response).lines.sort
45
60
  EM.stop
@@ -58,13 +73,13 @@ describe "WebSocket server draft76" do
58
73
  connection.send_data(format_request(@request))
59
74
 
60
75
  # Send closing frame after handshake complete
61
- connection.onopen = lambda {
76
+ connection.onopen {
62
77
  connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING)
63
78
  }
64
79
 
65
80
  # Check that this causes a termination string to be returned and the
66
81
  # connection close
67
- connection.onclose = lambda {
82
+ connection.onclose {
68
83
  connection.packets[0].should ==
69
84
  EM::WebSocket::Handler76::TERMINATE_STRING
70
85
  EM.stop
@@ -88,12 +103,12 @@ describe "WebSocket server draft76" do
88
103
  connection.send_data(format_request(@request))
89
104
 
90
105
  # Send closing frame after handshake complete, followed by another msg
91
- connection.onopen = lambda {
106
+ connection.onopen {
92
107
  connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING)
93
108
  connection.send('foobar')
94
109
  }
95
110
 
96
- connection.onclose = lambda {
111
+ connection.onclose {
97
112
  EM.stop
98
113
  }
99
114
  end
@@ -114,12 +129,12 @@ describe "WebSocket server draft76" do
114
129
  connection.send_data(format_request(@request))
115
130
 
116
131
  # Send closing frame after handshake complete
117
- connection.onopen = lambda {
132
+ connection.onopen {
118
133
  connection.send_data("\000\n\000\377")
119
134
  connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING)
120
135
  }
121
136
 
122
- connection.onclose = lambda {
137
+ connection.onclose {
123
138
  EM.stop
124
139
  }
125
140
  end
@@ -144,7 +159,7 @@ describe "WebSocket server draft76" do
144
159
  # 1180591620717411303296 bytes. Such a message would previously cause
145
160
  # a "bignum too big to convert into `long'" error.
146
161
  # However it is clearly unreasonable and should be rejected.
147
- client.onopen = lambda {
162
+ client.onopen {
148
163
  client.send_data("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00")
149
164
  }
150
165
  end
@@ -164,7 +179,7 @@ describe "WebSocket server draft76" do
164
179
  client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
165
180
  client.send_data(format_request(@request))
166
181
 
167
- client.onopen = lambda {
182
+ client.onopen {
168
183
  client.send_data("foobar") # Does not start with \x00 or \xff
169
184
  }
170
185
  end
@@ -196,7 +211,7 @@ describe "WebSocket server draft76" do
196
211
  # Sends first half of the request
197
212
  connection.send_data(data[0...(data.length / 2)])
198
213
 
199
- connection.onopen = lambda {
214
+ connection.onopen {
200
215
  connection.handshake_response.lines.sort.
201
216
  should == format_response(@response).lines.sort
202
217
  EM.stop
@@ -0,0 +1,62 @@
1
+ shared_examples_for "a websocket server" do
2
+ it "should call onerror if an application error raised in onopen" do
3
+ EM.run {
4
+ start_server { |ws|
5
+ ws.onopen {
6
+ raise "application error"
7
+ }
8
+
9
+ ws.onerror { |e|
10
+ e.message.should == "application error"
11
+ EM.stop
12
+ }
13
+ }
14
+
15
+ start_client
16
+ }
17
+ end
18
+
19
+ it "should call onerror if an application error raised in onmessage" do
20
+ EM.run {
21
+ start_server { |server|
22
+ server.onmessage {
23
+ raise "application error"
24
+ }
25
+
26
+ server.onerror { |e|
27
+ e.message.should == "application error"
28
+ EM.stop
29
+ }
30
+ }
31
+
32
+ start_client { |client|
33
+ client.onopen {
34
+ client.send('a message')
35
+ }
36
+ }
37
+ }
38
+ end
39
+
40
+ it "should call onerror in an application error raised in onclose" do
41
+ EM.run {
42
+ start_server { |server|
43
+ server.onclose {
44
+ raise "application error"
45
+ }
46
+
47
+ server.onerror { |e|
48
+ e.message.should == "application error"
49
+ EM.stop
50
+ }
51
+ }
52
+
53
+ start_client { |client|
54
+ client.onopen {
55
+ EM.add_timer(0.1) {
56
+ client.close_connection
57
+ }
58
+ }
59
+ }
60
+ }
61
+ end
62
+ end
@@ -106,3 +106,58 @@ describe EM::WebSocket::Framing03 do
106
106
  end
107
107
  end
108
108
  end
109
+
110
+ # These examples are straight from the spec
111
+ # http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-03#section-4.6
112
+ describe EM::WebSocket::Framing04 do
113
+ class FramingContainer04
114
+ include EM::WebSocket::Framing04
115
+
116
+ def <<(data)
117
+ @data << data
118
+ process_data(data)
119
+ end
120
+
121
+ def debug(*args); end
122
+ end
123
+
124
+ before :each do
125
+ @f = FramingContainer04.new
126
+ @f.initialize_framing
127
+ end
128
+
129
+ describe "examples from the spec" do
130
+ it "a single-frame text message" do
131
+ @f.should_receive(:message).with(:text, '', 'Hello')
132
+ @f << "\x84\x05\x48\x65\x6c\x6c\x6f" # "\x84\x05Hello"
133
+ end
134
+
135
+ it "a fragmented text message" do
136
+ @f.should_receive(:message).with(:text, '', 'Hello')
137
+ @f << "\x04\x03Hel"
138
+ @f << "\x80\x02lo"
139
+ end
140
+
141
+ it "Ping request" do
142
+ @f.should_receive(:message).with(:ping, '', 'Hello')
143
+ @f << "\x82\x05Hello"
144
+ end
145
+
146
+ it "a pong response" do
147
+ @f.should_receive(:message).with(:pong, '', 'Hello')
148
+ @f << "\x83\x05Hello"
149
+ end
150
+
151
+ it "256 bytes binary message in a single frame" do
152
+ data = "a"*256
153
+ @f.should_receive(:message).with(:binary, '', data)
154
+ @f << "\x85\x7E\x01\x00" + data
155
+ end
156
+
157
+ it "64KiB binary message in a single frame" do
158
+ data = "a"*65536
159
+ @f.should_receive(:message).with(:binary, '', data)
160
+ @f << "\x85\x7F\x00\x00\x00\x00\x00\x01\x00\x00" + data
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,18 @@
1
+ require 'helper'
2
+
3
+ describe EM::WebSocket::MaskedString do
4
+ it "should allow reading 4 byte mask and unmasking byte / bytes" do
5
+ t = EM::WebSocket::MaskedString.new("\x00\x00\x00\x01\x00\x01\x00\x01")
6
+ t.read_mask
7
+ t.getbyte(3).should == 0x00
8
+ t.getbytes(0, 4).should == "\x00\x01\x00\x00"
9
+ t.getbytes(1, 3).should == "\x01\x00\x00"
10
+ end
11
+
12
+ it "should return nil from getbyte if index requested is out of range" do
13
+ t = EM::WebSocket::MaskedString.new("\x00\x00\x00\x00\x53")
14
+ t.read_mask
15
+ t.getbyte(0).should == 0x53
16
+ t.getbyte(1).should == nil
17
+ end
18
+ end
metadata CHANGED
@@ -1,12 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: em-websocket
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 2
8
- - 1
9
- version: 0.2.1
4
+ prerelease:
5
+ version: 0.3.0
10
6
  platform: ruby
11
7
  authors:
12
8
  - Ilya Grigorik
@@ -14,7 +10,7 @@ autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
12
 
17
- date: 2011-03-01 00:00:00 +00:00
13
+ date: 2011-05-06 00:00:00 +01:00
18
14
  default_executable:
19
15
  dependencies:
20
16
  - !ruby/object:Gem::Dependency
@@ -25,10 +21,6 @@ dependencies:
25
21
  requirements:
26
22
  - - ">="
27
23
  - !ruby/object:Gem::Version
28
- segments:
29
- - 0
30
- - 12
31
- - 9
32
24
  version: 0.12.9
33
25
  type: :runtime
34
26
  version_requirements: *id001
@@ -40,10 +32,6 @@ dependencies:
40
32
  requirements:
41
33
  - - ">="
42
34
  - !ruby/object:Gem::Version
43
- segments:
44
- - 2
45
- - 1
46
- - 1
47
35
  version: 2.1.1
48
36
  type: :runtime
49
37
  version_requirements: *id002
@@ -55,10 +43,6 @@ dependencies:
55
43
  requirements:
56
44
  - - ~>
57
45
  - !ruby/object:Gem::Version
58
- segments:
59
- - 0
60
- - 2
61
- - 6
62
46
  version: 0.2.6
63
47
  type: :development
64
48
  version_requirements: *id003
@@ -70,11 +54,7 @@ dependencies:
70
54
  requirements:
71
55
  - - ~>
72
56
  - !ruby/object:Gem::Version
73
- segments:
74
- - 2
75
- - 0
76
- - 0
77
- version: 2.0.0
57
+ version: 2.5.0
78
58
  type: :development
79
59
  version_requirements: *id004
80
60
  - !ruby/object:Gem::Dependency
@@ -85,8 +65,6 @@ dependencies:
85
65
  requirements:
86
66
  - - ">="
87
67
  - !ruby/object:Gem::Version
88
- segments:
89
- - 0
90
68
  version: "0"
91
69
  type: :development
92
70
  version_requirements: *id005
@@ -114,25 +92,42 @@ files:
114
92
  - examples/multicast.rb
115
93
  - examples/test.html
116
94
  - lib/em-websocket.rb
95
+ - lib/em-websocket/close03.rb
96
+ - lib/em-websocket/close05.rb
97
+ - lib/em-websocket/close06.rb
98
+ - lib/em-websocket/close75.rb
117
99
  - lib/em-websocket/connection.rb
118
100
  - lib/em-websocket/debugger.rb
119
101
  - lib/em-websocket/framing03.rb
102
+ - lib/em-websocket/framing04.rb
103
+ - lib/em-websocket/framing05.rb
120
104
  - lib/em-websocket/framing76.rb
121
105
  - lib/em-websocket/handler.rb
122
106
  - lib/em-websocket/handler03.rb
107
+ - lib/em-websocket/handler05.rb
108
+ - lib/em-websocket/handler06.rb
123
109
  - lib/em-websocket/handler75.rb
124
110
  - lib/em-websocket/handler76.rb
125
111
  - lib/em-websocket/handler_factory.rb
112
+ - lib/em-websocket/handshake04.rb
126
113
  - lib/em-websocket/handshake75.rb
127
114
  - lib/em-websocket/handshake76.rb
115
+ - lib/em-websocket/masking04.rb
116
+ - lib/em-websocket/message_processor_03.rb
117
+ - lib/em-websocket/message_processor_06.rb
128
118
  - lib/em-websocket/version.rb
129
119
  - lib/em-websocket/websocket.rb
130
120
  - spec/helper.rb
121
+ - spec/integration/common_spec.rb
131
122
  - spec/integration/draft03_spec.rb
123
+ - spec/integration/draft05_spec.rb
124
+ - spec/integration/draft06_spec.rb
125
+ - spec/integration/draft75_spec.rb
132
126
  - spec/integration/draft76_spec.rb
127
+ - spec/integration/shared_examples.rb
133
128
  - spec/unit/framing_spec.rb
134
129
  - spec/unit/handler_spec.rb
135
- - spec/websocket_spec.rb
130
+ - spec/unit/masking_spec.rb
136
131
  has_rdoc: true
137
132
  homepage: http://github.com/igrigorik/em-websocket
138
133
  licenses: []
@@ -147,28 +142,29 @@ required_ruby_version: !ruby/object:Gem::Requirement
147
142
  requirements:
148
143
  - - ">="
149
144
  - !ruby/object:Gem::Version
150
- segments:
151
- - 0
152
145
  version: "0"
153
146
  required_rubygems_version: !ruby/object:Gem::Requirement
154
147
  none: false
155
148
  requirements:
156
149
  - - ">="
157
150
  - !ruby/object:Gem::Version
158
- segments:
159
- - 0
160
151
  version: "0"
161
152
  requirements: []
162
153
 
163
154
  rubyforge_project: em-websocket
164
- rubygems_version: 1.3.7
155
+ rubygems_version: 1.6.2
165
156
  signing_key:
166
157
  specification_version: 3
167
158
  summary: EventMachine based WebSocket server
168
159
  test_files:
169
160
  - spec/helper.rb
161
+ - spec/integration/common_spec.rb
170
162
  - spec/integration/draft03_spec.rb
163
+ - spec/integration/draft05_spec.rb
164
+ - spec/integration/draft06_spec.rb
165
+ - spec/integration/draft75_spec.rb
171
166
  - spec/integration/draft76_spec.rb
167
+ - spec/integration/shared_examples.rb
172
168
  - spec/unit/framing_spec.rb
173
169
  - spec/unit/handler_spec.rb
174
- - spec/websocket_spec.rb
170
+ - spec/unit/masking_spec.rb
@@ -1,210 +0,0 @@
1
- require 'helper'
2
-
3
- describe EventMachine::WebSocket do
4
-
5
- it "should automatically complete WebSocket handshake" do
6
- EM.run do
7
- MSG = "Hello World!"
8
- EventMachine.add_timer(0.1) do
9
- http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get :timeout => 0
10
- http.errback { failed }
11
- http.callback { http.response_header.status.should == 101 }
12
-
13
- http.stream { |msg|
14
- msg.should == MSG
15
- EventMachine.stop
16
- }
17
- end
18
-
19
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
20
- ws.onopen {
21
- ws.send MSG
22
- }
23
- end
24
- end
25
- end
26
-
27
- it "should fail on non WebSocket requests" do
28
- EM.run do
29
- EventMachine.add_timer(0.1) do
30
- http = EventMachine::HttpRequest.new('http://127.0.0.1:12345/').get :timeout => 0
31
- http.errback { failed }
32
- http.callback {
33
- http.response_header.status.should == 400
34
- EventMachine.stop
35
- }
36
- end
37
-
38
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) {}
39
- end
40
- end
41
-
42
- it "should split multiple messages into separate callbacks" do
43
- EM.run do
44
- messages = %w[1 2]
45
- received = []
46
-
47
- EventMachine.add_timer(0.1) do
48
- http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get :timeout => 0
49
- http.errback { failed }
50
- http.stream {|msg|}
51
- http.callback {
52
- http.response_header.status.should == 101
53
- http.send messages[0]
54
- http.send messages[1]
55
- }
56
- end
57
-
58
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
59
- ws.onopen {}
60
- ws.onclose {}
61
- ws.onmessage {|msg|
62
- msg.should == messages[received.size]
63
- received.push msg
64
-
65
- EventMachine.stop if received.size == messages.size
66
- }
67
- end
68
- end
69
- end
70
-
71
- it "should call onclose callback when client closes connection" do
72
- EM.run do
73
- EventMachine.add_timer(0.1) do
74
- http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get :timeout => 0
75
- http.errback { failed }
76
- http.callback {
77
- http.response_header.status.should == 101
78
- http.close_connection
79
- }
80
- http.stream{|msg|}
81
- end
82
-
83
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
84
- ws.onopen {}
85
- ws.onclose {
86
- ws.state.should == :closed
87
- EventMachine.stop
88
- }
89
- end
90
- end
91
- end
92
-
93
- it "should call onerror callback with raised exception and close connection on bad handshake" do
94
- EM.run do
95
- EventMachine.add_timer(0.1) do
96
- http = EventMachine::HttpRequest.new('http://127.0.0.1:12345/').get :timeout => 0
97
- http.errback { http.response_header.status.should == 0 }
98
- http.callback { failed }
99
- end
100
-
101
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
102
- ws.onopen { failed }
103
- ws.onclose { EventMachine.stop }
104
- ws.onerror {|e|
105
- e.should be_an_instance_of EventMachine::WebSocket::HandshakeError
106
- e.message.should match('Connection and Upgrade headers required')
107
- EventMachine.stop
108
- }
109
- end
110
- end
111
- end
112
-
113
- it "should populate ws.request with appropriate headers" do
114
- EM.run do
115
- EventMachine.add_timer(0.1) do
116
- http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get :timeout => 0
117
- http.errback { failed }
118
- http.callback {
119
- http.response_header.status.should == 101
120
- http.close_connection
121
- }
122
- http.stream { |msg| }
123
- end
124
-
125
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
126
- ws.onopen {
127
- ws.request["User-Agent"].should == "EventMachine HttpClient"
128
- ws.request["Connection"].should == "Upgrade"
129
- ws.request["Upgrade"].should == "WebSocket"
130
- ws.request["Path"].should == "/"
131
- ws.request["Origin"].should == "127.0.0.1"
132
- ws.request["Host"].to_s.should == "ws://127.0.0.1:12345"
133
- }
134
- ws.onclose {
135
- ws.state.should == :closed
136
- EventMachine.stop
137
- }
138
- end
139
- end
140
- end
141
-
142
- it "should allow sending and retrieving query string args passed in on the connection request." do
143
- EM.run do
144
- EventMachine.add_timer(0.1) do
145
- http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get(:query => {'foo' => 'bar', 'baz' => 'qux'}, :timeout => 0)
146
- http.errback { failed }
147
- http.callback {
148
- http.response_header.status.should == 101
149
- http.close_connection
150
- }
151
- http.stream { |msg| }
152
- end
153
-
154
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
155
- ws.onopen {
156
- path, query = ws.request["Path"].split('?')
157
- path.should == '/'
158
- Hash[*query.split(/&|=/)].should == {"foo"=>"bar", "baz"=>"qux"}
159
- ws.request["Query"]["foo"].should == "bar"
160
- ws.request["Query"]["baz"].should == "qux"
161
- }
162
- ws.onclose {
163
- ws.state.should == :closed
164
- EventMachine.stop
165
- }
166
- end
167
- end
168
- end
169
-
170
- it "should ws.response['Query'] to empty hash when no query string params passed in connection URI" do
171
- EM.run do
172
- EventMachine.add_timer(0.1) do
173
- http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get(:timeout => 0)
174
- http.errback { failed }
175
- http.callback {
176
- http.response_header.status.should == 101
177
- http.close_connection
178
- }
179
- http.stream { |msg| }
180
- end
181
-
182
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
183
- ws.onopen {
184
- ws.request["Path"].should == "/"
185
- ws.request["Query"].should == {}
186
- }
187
- ws.onclose {
188
- ws.state.should == :closed
189
- EventMachine.stop
190
- }
191
- end
192
- end
193
- end
194
-
195
- it "should raise an exception if frame sent before handshake complete" do
196
- EM.run {
197
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |c|
198
- # We're not using a real client so the handshake will not be sent
199
- EM.add_timer(0.1) {
200
- lambda {
201
- c.send('early message')
202
- }.should raise_error('Cannot send data before onopen callback')
203
- EM.stop
204
- }
205
- }
206
-
207
- client = EM.connect('0.0.0.0', 12345, EM::Connection)
208
- }
209
- end
210
- end