em-websocket 0.0.6 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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