em-websocket 0.0.6 → 0.1.1

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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.6
1
+ 0.1.1
@@ -25,19 +25,21 @@ EventMachine.run {
25
25
 
26
26
 
27
27
  EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080, :debug => true) do |ws|
28
+
28
29
  ws.onopen {
29
- @sid = @channel.subscribe { |msg| ws.send msg }
30
- @channel.push "#{@sid} connected!"
31
- }
30
+ sid = @channel.subscribe { |msg| ws.send msg }
31
+ @channel.push "#{sid} connected!"
32
32
 
33
- ws.onmessage { |msg|
34
- @channel.push "<#{@sid}>: #{msg}"
35
- }
33
+ ws.onmessage { |msg|
34
+ @channel.push "<#{sid}>: #{msg}"
35
+ }
36
+
37
+ ws.onclose {
38
+ @channel.unsubscribe(sid)
39
+ }
36
40
 
37
- ws.onclose {
38
- @channel.unsubscribe(@sid)
39
41
  }
40
42
  end
41
43
 
42
44
  puts "Server started"
43
- }
45
+ }
@@ -3,6 +3,6 @@ $:.unshift(File.dirname(__FILE__) + '/../lib')
3
3
  #require "rubygems"
4
4
  require "eventmachine"
5
5
 
6
- %w[ websocket connection ].each do |file|
6
+ %w[ debugger websocket connection handler_factory handler handler75 handler76 ].each do |file|
7
7
  require "em-websocket/#{file}"
8
8
  end
@@ -3,9 +3,7 @@ require 'addressable/uri'
3
3
  module EventMachine
4
4
  module WebSocket
5
5
  class Connection < EventMachine::Connection
6
-
7
- PATH = /^GET (\/[^\s]*) HTTP\/1\.1$/
8
- HEADER = /^([^:]+):\s*([^$]+)/
6
+ include Debugger
9
7
 
10
8
  attr_reader :state, :request
11
9
 
@@ -17,14 +15,18 @@ module EventMachine
17
15
  def initialize(options)
18
16
  @options = options
19
17
  @debug = options[:debug] || false
18
+ @secure = options[:secure] || false
20
19
  @state = :handshake
21
20
  @request = {}
22
21
  @data = ''
23
- @skip_onclose = false
24
22
 
25
23
  debug [:initialize]
26
24
  end
27
25
 
26
+ def post_init
27
+ start_tls if @secure
28
+ end
29
+
28
30
  def receive_data(data)
29
31
  debug [:receive_data, data]
30
32
 
@@ -40,58 +42,36 @@ module EventMachine
40
42
  end
41
43
 
42
44
  def dispatch
43
- while case @state
45
+ case @state
44
46
  when :handshake
45
- new_request
46
- when :upgrade
47
- send_upgrade
47
+ handshake
48
48
  when :connected
49
49
  process_message
50
50
  else raise RuntimeError, "invalid state: #{@state}"
51
- end
52
51
  end
53
52
  end
54
53
 
55
- def new_request
56
- if @data.match(/\r\n\r\n$/)
54
+ def handshake
55
+ if @data.match(/<policy-file-request\s*\/>/)
56
+ send_flash_cross_domain_file
57
+ return false
58
+ else
57
59
  debug [:inbound_headers, @data]
58
- lines = @data.split("\r\n")
59
-
60
60
  begin
61
- # extract request path
62
- @request['Path'] = lines.shift.match(PATH)[1].strip
63
-
64
- # extract query string values
65
- @request['Query'] = Addressable::URI.parse(@request['Path']).query_values ||= {}
66
-
67
- # extract remaining headers
68
- lines.each do |line|
69
- h = HEADER.match(line)
70
- @request[h[1].strip] = h[2].strip
71
- end
72
-
73
- # transform headers
74
- @request['Host'] = Addressable::URI.parse("ws://"+@request['Host'])
75
-
76
- if not websocket_connection?
77
- process_bad_request
78
- return false
79
- else
80
- @data = ''
81
- @state = :upgrade
82
- return true
83
- end
61
+ @handler = HandlerFactory.build(@data, @secure, @debug)
62
+ @data = ''
63
+ send_data @handler.handshake
64
+
65
+ @request = @handler.request
66
+ @state = :connected
67
+ @onopen.call if @onopen
68
+ return true
84
69
  rescue => e
85
70
  debug [:error, e]
86
71
  process_bad_request
87
72
  return false
88
73
  end
89
- elsif @data.match(/<policy-file-request\s*\/>/)
90
- send_flash_cross_domain_file
91
- return false
92
74
  end
93
-
94
- false
95
75
  end
96
76
 
97
77
  def process_bad_request
@@ -99,53 +79,76 @@ module EventMachine
99
79
  close_connection_after_writing
100
80
  end
101
81
 
102
- def websocket_connection?
103
- @request['Connection'] == 'Upgrade' and @request['Upgrade'] == 'WebSocket'
104
- end
105
-
106
- def send_upgrade
107
- location = "ws://#{@request['Host'].host}"
108
- location << ":#{@request['Host'].port}" if @request['Host'].port
109
- location << @request['Path']
110
-
111
- upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
112
- upgrade << "Upgrade: WebSocket\r\n"
113
- upgrade << "Connection: Upgrade\r\n"
114
- upgrade << "WebSocket-Origin: #{@request['Origin']}\r\n"
115
- upgrade << "WebSocket-Location: #{location}\r\n\r\n"
116
-
117
- # upgrade connection and notify client callback
118
- # about completed handshake
119
- debug [:upgrade_headers, upgrade]
120
- send_data upgrade
121
-
122
- @state = :connected
123
- @onopen.call if @onopen
124
-
125
- # stop dispatch, wait for messages
126
- false
127
- end
128
-
129
82
  def send_flash_cross_domain_file
130
83
  file = '<?xml version="1.0"?><cross-domain-policy><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>'
131
84
  debug [:cross_domain, file]
132
85
  send_data file
133
86
 
134
87
  # handle the cross-domain request transparently
135
- # no need to notif the user about this connection
88
+ # no need to notify the user about this connection
136
89
  @onclose = nil
137
90
  close_connection_after_writing
138
91
  end
139
92
 
140
93
  def process_message
141
- return if not @onmessage
142
94
  debug [:message, @data]
143
95
 
144
- # slice the message out of the buffer and pass in
145
- # for processing, and buffer data otherwise
146
- while msg = @data.slice!(/\000([^\377]*)\377/)
147
- msg.gsub!(/^\x00|\xff$/, '')
148
- @onmessage.call(msg)
96
+ # This algorithm comes straight from the spec
97
+ # http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-4.2
98
+
99
+ error = false
100
+
101
+ while !error
102
+ pointer = 0
103
+ frame_type = @data[pointer].to_i
104
+ pointer += 1
105
+
106
+ if (frame_type & 0x80) == 0x80
107
+ # If the high-order bit of the /frame type/ byte is set
108
+ length = 0
109
+
110
+ loop do
111
+ b = @data[pointer].to_i
112
+ return false unless b
113
+ pointer += 1
114
+ b_v = b & 0x7F
115
+ length = length * 128 + b_v
116
+ break unless (b & 0x80) == 0x80
117
+ end
118
+
119
+ if @data[pointer+length-1] == nil
120
+ debug [:buffer_incomplete, @data.inspect]
121
+ # Incomplete data - leave @data to accumulate
122
+ error = true
123
+ else
124
+ # Straight from spec - I'm sure this isn't crazy...
125
+ # 6. Read /length/ bytes.
126
+ # 7. Discard the read bytes.
127
+ @data = @data[(pointer+length)..-1]
128
+
129
+ # If the /frame type/ is 0xFF and the /length/ was 0, then close
130
+ if length == 0
131
+ send_data("\xff\x00")
132
+ @state = :closing
133
+ close_connection_after_writing
134
+ else
135
+ error = true
136
+ end
137
+ end
138
+ else
139
+ # If the high-order bit of the /frame type/ byte is _not_ set
140
+ msg = @data.slice!(/^\x00([^\xff]*)\xff/)
141
+ if msg
142
+ msg.gsub!(/^\x00|\xff$/, '')
143
+ if @state == :closing
144
+ debug [:ignored_message, msg]
145
+ else
146
+ @onmessage.call(msg) if @onmessage
147
+ end
148
+ else
149
+ error = true
150
+ end
151
+ end
149
152
  end
150
153
 
151
154
  false
@@ -162,16 +165,6 @@ module EventMachine
162
165
  debug [:send, data]
163
166
  send_data("\x00#{data}\xff")
164
167
  end
165
-
166
- private
167
-
168
- def debug(*data)
169
- if @debug
170
- require 'pp'
171
- pp data
172
- puts
173
- end
174
- end
175
168
  end
176
169
  end
177
170
  end
@@ -0,0 +1,17 @@
1
+ module EventMachine
2
+ module WebSocket
3
+ module Debugger
4
+
5
+ private
6
+
7
+ def debug(*data)
8
+ if @debug
9
+ require 'pp'
10
+ pp data
11
+ puts
12
+ end
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ module EventMachine
2
+ module WebSocket
3
+ class HandshakeError < RuntimeError; end
4
+
5
+ class Handler
6
+ include Debugger
7
+
8
+ attr_reader :request
9
+
10
+ def initialize(request, response, debug = false)
11
+ @request = request
12
+ @response = response
13
+ @debug = debug
14
+ end
15
+
16
+ def handshake
17
+ # Implemented in subclass
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module EventMachine
2
+ module WebSocket
3
+ class Handler75 < Handler
4
+ def handshake
5
+ location = "#{@request['Host'].scheme}://#{@request['Host'].host}"
6
+ location << ":#{@request['Host'].port}" if @request['Host'].port
7
+ location << @request['Path']
8
+
9
+ upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
10
+ upgrade << "Upgrade: WebSocket\r\n"
11
+ upgrade << "Connection: Upgrade\r\n"
12
+ upgrade << "WebSocket-Origin: #{@request['Origin']}\r\n"
13
+ upgrade << "WebSocket-Location: #{location}\r\n\r\n"
14
+
15
+ debug [:upgrade_headers, upgrade]
16
+
17
+ return upgrade
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,61 @@
1
+ require 'digest/md5'
2
+
3
+ module EventMachine
4
+ module WebSocket
5
+ class Handler76 < Handler
6
+ # "\377\000" is octet version and "\xff\x00" is hex version
7
+ TERMINATE_STRING = "\xff\x00"
8
+
9
+ def handshake
10
+ challenge_response = solve_challange(
11
+ @request['Sec-WebSocket-Key1'],
12
+ @request['Sec-WebSocket-Key2'],
13
+ @request['Third-Key']
14
+ )
15
+
16
+ location = "#{@request['Host'].scheme}://#{@request['Host'].host}"
17
+ location << ":#{@request['Host'].port}" if @request['Host'].port
18
+ location << @request['Path']
19
+
20
+ upgrade = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
21
+ upgrade << "Upgrade: WebSocket\r\n"
22
+ upgrade << "Connection: Upgrade\r\n"
23
+ upgrade << "Sec-WebSocket-Location: #{location}\r\n"
24
+ upgrade << "Sec-WebSocket-Origin: #{@request['Origin']}\r\n"
25
+ if protocol = @request['Sec-WebSocket-Protocol']
26
+ validate_protocol!(protocol)
27
+ upgrade << "Sec-WebSocket-Protocol: #{protocol}\r\n"
28
+ end
29
+ upgrade << "\r\n"
30
+ upgrade << challenge_response
31
+
32
+ debug [:upgrade_headers, upgrade]
33
+
34
+ return upgrade
35
+ end
36
+
37
+ private
38
+
39
+ def solve_challange(first, second, third)
40
+ # Refer to 5.2 4-9 of the draft 76
41
+ sum = [(extract_nums(first) / count_spaces(first))].pack("N*") +
42
+ [(extract_nums(second) / count_spaces(second))].pack("N*") +
43
+ third
44
+ Digest::MD5.digest(sum)
45
+ end
46
+
47
+ def extract_nums(string)
48
+ string.scan(/[0-9]/).join.to_i
49
+ end
50
+
51
+ def count_spaces(string)
52
+ string.scan(/ /).size
53
+ end
54
+
55
+ def validate_protocol!(protocol)
56
+ raise HandshakeError, "Invalid WebSocket-Protocol: empty" if protocol.empty?
57
+ # TODO: Validate characters
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,52 @@
1
+ module EventMachine
2
+ module WebSocket
3
+ class HandlerFactory
4
+ PATH = /^(\w+) (\/[^\s]*) HTTP\/1\.1$/
5
+ HEADER = /^([^:]+):\s*([^$]+)/
6
+
7
+ def self.build(data, secure = false, debug = false)
8
+ request = {}
9
+ response = nil
10
+
11
+ lines = data.split("\r\n")
12
+
13
+ # extract request path
14
+ first_line = lines.shift.match(PATH)
15
+ request['Method'] = first_line[1].strip
16
+ request['Path'] = first_line[2].strip
17
+
18
+ unless request["Method"] == "GET"
19
+ raise HandshakeError, "Must be GET request"
20
+ end
21
+
22
+ # extract query string values
23
+ request['Query'] = Addressable::URI.parse(request['Path']).query_values ||= {}
24
+ # extract remaining headers
25
+ lines.each do |line|
26
+ h = HEADER.match(line)
27
+ request[h[1].strip] = h[2].strip if h
28
+ end
29
+ request['Third-Key'] = lines.last
30
+
31
+ unless request['Connection'] == 'Upgrade' and request['Upgrade'] == 'WebSocket'
32
+ raise HandshakeError, "Connection and Upgrade headers required"
33
+ end
34
+
35
+ # transform headers
36
+ protocol = (secure ? "wss" : "ws")
37
+ request['Host'] = Addressable::URI.parse("#{protocol}://"+request['Host'])
38
+
39
+ version = request['Sec-WebSocket-Key1'] ? 76 : 75
40
+
41
+ case version
42
+ when 75
43
+ Handler75.new(request, response, debug)
44
+ when 76
45
+ Handler76.new(request, response, debug)
46
+ else
47
+ raise "Must not happen"
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -20,4 +20,4 @@ module EventMachine
20
20
  EventMachine.stop
21
21
  end
22
22
  end
23
- end
23
+ end
@@ -3,4 +3,63 @@ require 'spec'
3
3
  require 'pp'
4
4
  require 'em-http'
5
5
 
6
- require 'lib/em-websocket'
6
+ require 'lib/em-websocket'
7
+
8
+ class FakeWebSocketClient < EM::Connection
9
+ attr_writer :onopen, :onclose, :onmessage
10
+ attr_reader :handshake_response, :packets
11
+
12
+ def initialize
13
+ @state = :new
14
+ @packets = []
15
+ end
16
+
17
+ def receive_data(data)
18
+ # puts "RECEIVE DATA #{data}"
19
+ if @state == :new
20
+ @handshake_response = data
21
+ @onopen.call if @onopen
22
+ @state = :open
23
+ else
24
+ @onmessage.call if @onmessage
25
+ @packets << data
26
+ end
27
+ end
28
+
29
+ def send(data)
30
+ send_data("\x00#{data}\xff")
31
+ end
32
+
33
+ def unbind
34
+ @onclose.call if @onclose
35
+ end
36
+ end
37
+
38
+ def failed
39
+ EventMachine.stop
40
+ fail
41
+ end
42
+
43
+ def format_request(r)
44
+ data = "#{r[:method]} #{r[:path]} HTTP/1.1\r\n"
45
+ header_lines = r[:headers].map { |k,v| "#{k}: #{v}" }
46
+ data << [header_lines, '', r[:body]].join("\r\n")
47
+ data
48
+ end
49
+
50
+ def format_response(r)
51
+ data = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
52
+ header_lines = r[:headers].map { |k,v| "#{k}: #{v}" }
53
+ data << [header_lines, '', r[:body]].join("\r\n")
54
+ data
55
+ end
56
+
57
+ def handler(request, secure = false)
58
+ EM::WebSocket::HandlerFactory.build(format_request(request), secure)
59
+ end
60
+
61
+ def send_handshake(response)
62
+ simple_matcher do |given|
63
+ given.handshake.lines.sort == format_response(response).lines.sort
64
+ end
65
+ end
@@ -0,0 +1,102 @@
1
+ require 'spec/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
+ end
@@ -0,0 +1,117 @@
1
+ require 'spec/helper'
2
+
3
+ describe "EventMachine::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/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(EM::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(EM::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(EM::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(EM::WebSocket::HandshakeError)
116
+ end
117
+ end
@@ -2,11 +2,6 @@ require 'spec/helper'
2
2
 
3
3
  describe EventMachine::WebSocket do
4
4
 
5
- def failed
6
- EventMachine.stop
7
- fail
8
- end
9
-
10
5
  it "should automatically complete WebSocket handshake" do
11
6
  EM.run do
12
7
  MSG = "Hello World!"
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 0
8
- - 6
9
- version: 0.0.6
7
+ - 1
8
+ - 1
9
+ version: 0.1.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Ilya Grigorik
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-04-18 00:00:00 -04:00
17
+ date: 2010-06-13 00:00:00 -04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -72,6 +72,7 @@ files:
72
72
  - README.rdoc
73
73
  - Rakefile
74
74
  - VERSION
75
+ - em-websocket.gemspec
75
76
  - examples/echo.rb
76
77
  - examples/js/FABridge.js
77
78
  - examples/js/WebSocketMain.swf
@@ -81,8 +82,15 @@ files:
81
82
  - examples/test.html
82
83
  - lib/em-websocket.rb
83
84
  - lib/em-websocket/connection.rb
85
+ - lib/em-websocket/debugger.rb
86
+ - lib/em-websocket/handler.rb
87
+ - lib/em-websocket/handler75.rb
88
+ - lib/em-websocket/handler76.rb
89
+ - lib/em-websocket/handler_factory.rb
84
90
  - lib/em-websocket/websocket.rb
85
91
  - spec/helper.rb
92
+ - spec/integration/integration_spec.rb
93
+ - spec/unit/handler_spec.rb
86
94
  - spec/websocket_spec.rb
87
95
  has_rdoc: true
88
96
  homepage: http://github.com/igrigorik/em-websocket
@@ -116,6 +124,8 @@ specification_version: 3
116
124
  summary: EventMachine based WebSocket server
117
125
  test_files:
118
126
  - spec/helper.rb
127
+ - spec/integration/integration_spec.rb
128
+ - spec/unit/handler_spec.rb
119
129
  - spec/websocket_spec.rb
120
130
  - examples/echo.rb
121
131
  - examples/multicast.rb