websocket 1.1.4 → 1.2.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.
Files changed (55) hide show
  1. data/.travis.yml +2 -6
  2. data/CHANGELOG.md +7 -0
  3. data/Gemfile +2 -1
  4. data/README.md +10 -6
  5. data/Rakefile +1 -1
  6. data/lib/websocket.rb +1 -1
  7. data/lib/websocket/exception_handler.rb +6 -0
  8. data/lib/websocket/frame/base.rb +1 -1
  9. data/lib/websocket/frame/data.rb +11 -9
  10. data/lib/websocket/frame/handler.rb +1 -1
  11. data/lib/websocket/frame/handler/handler03.rb +17 -17
  12. data/lib/websocket/frame/handler/handler07.rb +8 -8
  13. data/lib/websocket/frame/handler/handler75.rb +8 -7
  14. data/lib/websocket/frame/incoming.rb +1 -1
  15. data/lib/websocket/frame/outgoing.rb +1 -1
  16. data/lib/websocket/handshake/base.rb +7 -7
  17. data/lib/websocket/handshake/client.rb +5 -3
  18. data/lib/websocket/handshake/handler/base.rb +5 -5
  19. data/lib/websocket/handshake/handler/client.rb +6 -1
  20. data/lib/websocket/handshake/handler/client04.rb +7 -6
  21. data/lib/websocket/handshake/handler/client75.rb +5 -4
  22. data/lib/websocket/handshake/handler/client76.rb +5 -5
  23. data/lib/websocket/handshake/handler/server04.rb +11 -6
  24. data/lib/websocket/handshake/handler/server75.rb +5 -5
  25. data/lib/websocket/handshake/handler/server76.rb +9 -9
  26. data/lib/websocket/handshake/server.rb +25 -24
  27. data/lib/websocket/version.rb +1 -1
  28. data/spec/frame/incoming_03_spec.rb +25 -25
  29. data/spec/frame/incoming_04_spec.rb +25 -25
  30. data/spec/frame/incoming_05_spec.rb +29 -29
  31. data/spec/frame/incoming_07_spec.rb +31 -31
  32. data/spec/frame/incoming_75_spec.rb +13 -13
  33. data/spec/frame/incoming_common_spec.rb +12 -13
  34. data/spec/frame/masking_spec.rb +10 -10
  35. data/spec/frame/outgoing_03_spec.rb +17 -17
  36. data/spec/frame/outgoing_04_spec.rb +17 -17
  37. data/spec/frame/outgoing_05_spec.rb +17 -17
  38. data/spec/frame/outgoing_07_spec.rb +19 -19
  39. data/spec/frame/outgoing_75_spec.rb +9 -9
  40. data/spec/frame/outgoing_common_spec.rb +7 -8
  41. data/spec/handshake/client_04_spec.rb +9 -9
  42. data/spec/handshake/client_75_spec.rb +2 -2
  43. data/spec/handshake/client_76_spec.rb +9 -9
  44. data/spec/handshake/server_04_spec.rb +5 -5
  45. data/spec/handshake/server_75_spec.rb +1 -1
  46. data/spec/handshake/server_76_spec.rb +21 -21
  47. data/spec/spec_helper.rb +4 -1
  48. data/spec/support/all_client_drafts.rb +62 -52
  49. data/spec/support/all_server_drafts.rb +49 -49
  50. data/spec/support/handshake_requests.rb +16 -16
  51. data/spec/support/incoming_frames.rb +28 -28
  52. data/spec/support/outgoing_frames.rb +10 -10
  53. data/websocket.gemspec +1 -1
  54. metadata +42 -22
  55. checksums.yaml +0 -7
@@ -10,11 +10,11 @@ module WebSocket
10
10
 
11
11
  # @see WebSocket::Handshake::Base#to_s
12
12
  def to_s
13
- result = [ header_line ]
13
+ result = [header_line]
14
14
  handshake_keys.each do |key|
15
15
  result << key.join(': ')
16
16
  end
17
- result << ""
17
+ result << ''
18
18
  result << finishing_line
19
19
  result.join("\r\n")
20
20
  end
@@ -28,11 +28,11 @@ module WebSocket
28
28
  # Set first line of text representation according to specification.
29
29
  # @return [String] First line of HTTP header
30
30
  def header_line
31
- ""
31
+ ''
32
32
  end
33
33
 
34
34
  # Set handshake headers. Provided as array because some protocol version require specific order of fields.
35
- # @return [Array] List of headers as arrays [ key, value ]
35
+ # @return [Array] List of headers as arrays [key, value]
36
36
  def handshake_keys
37
37
  []
38
38
  end
@@ -40,7 +40,7 @@ module WebSocket
40
40
  # Set data to send after headers. In most cases it will be blank data.
41
41
  # @return [String] data
42
42
  def finishing_line
43
- ""
43
+ ''
44
44
  end
45
45
 
46
46
  end
@@ -8,10 +8,15 @@ module WebSocket
8
8
  # @see WebSocket::Handshake::Handler::Base#header_line
9
9
  def header_line
10
10
  path = @handshake.path
11
- path += "?" + @handshake.query if @handshake.query
11
+ path += '?' + @handshake.query if @handshake.query
12
12
  "GET #{path} HTTP/1.1"
13
13
  end
14
14
 
15
+ # @see WebSocket::Handshake::Handler::Base#header_handshake_keys
16
+ def handshake_keys
17
+ super + @handshake.headers.to_a
18
+ end
19
+
15
20
  end
16
21
  end
17
22
  end
@@ -16,15 +16,16 @@ module WebSocket
16
16
  # @see WebSocket::Handshake::Handler::Base#handshake_keys
17
17
  def handshake_keys
18
18
  keys = [
19
- ["Upgrade", "websocket"],
20
- ["Connection", "Upgrade"]
19
+ ['Upgrade', 'websocket'],
20
+ ['Connection', 'Upgrade']
21
21
  ]
22
22
  host = @handshake.host
23
23
  host += ":#{@handshake.port}" if @handshake.port
24
- keys << ["Host", host]
25
- keys << ["Sec-WebSocket-Origin", @handshake.origin] if @handshake.origin
26
- keys << ["Sec-WebSocket-Version", @handshake.version ]
27
- keys << ["Sec-WebSocket-Key", key]
24
+ keys << ['Host', host]
25
+ keys += super
26
+ keys << ['Sec-WebSocket-Origin', @handshake.origin] if @handshake.origin
27
+ keys << ['Sec-WebSocket-Version', @handshake.version]
28
+ keys << ['Sec-WebSocket-Key', key]
28
29
  keys
29
30
  end
30
31
 
@@ -8,13 +8,14 @@ module WebSocket
8
8
  # @see WebSocket::Handshake::Handler::Base#handshake_keys
9
9
  def handshake_keys
10
10
  keys = [
11
- ["Upgrade", "WebSocket"],
12
- ["Connection", "Upgrade"]
11
+ ['Upgrade', 'WebSocket'],
12
+ ['Connection', 'Upgrade']
13
13
  ]
14
14
  host = @handshake.host
15
15
  host += ":#{@handshake.port}" if @handshake.port
16
- keys << ["Host", host]
17
- keys << ["Origin", @handshake.origin] if @handshake.origin
16
+ keys << ['Host', host]
17
+ keys << ['Origin', @handshake.origin] if @handshake.origin
18
+ keys += super
18
19
  keys
19
20
  end
20
21
 
@@ -53,8 +53,8 @@ module WebSocket
53
53
  def challenge
54
54
  return @challenge if defined?(@challenge)
55
55
  key1 && key2
56
- sum = [@key1_number].pack("N*") +
57
- [@key2_number].pack("N*") +
56
+ sum = [@key1_number].pack('N*') +
57
+ [@key2_number].pack('N*') +
58
58
  key3
59
59
 
60
60
  @challenge = Digest::MD5.digest(sum)
@@ -63,7 +63,7 @@ module WebSocket
63
63
  # Verify if challenge sent by server match generated one
64
64
  # @return [Boolena] True if challenge matches, false otherwise(sets appropriate error)
65
65
  def verify_challenge
66
- raise WebSocket::Error::Handshake::InvalidAuthentication unless @handshake.instance_variable_get('@leftovers') == challenge
66
+ raise WebSocket::Error::Handshake::InvalidAuthentication unless @handshake.leftovers == challenge
67
67
  true
68
68
  end
69
69
 
@@ -85,14 +85,14 @@ module WebSocket
85
85
  end
86
86
  spaces.times() do
87
87
  pos = 1 + rand(key.size - 1)
88
- key[pos...pos] = " "
88
+ key[pos...pos] = ' '
89
89
  end
90
90
  return key
91
91
  end
92
92
 
93
93
  # Generate third key
94
94
  def generate_key3
95
- [rand(0x100000000)].pack("N") + [rand(0x100000000)].pack("N")
95
+ [rand(0x100000000)].pack('N') + [rand(0x100000000)].pack('N')
96
96
  end
97
97
 
98
98
  end
@@ -15,28 +15,33 @@ module WebSocket
15
15
 
16
16
  # @see WebSocket::Handshake::Handler::Base#header_line
17
17
  def header_line
18
- "HTTP/1.1 101 Switching Protocols"
18
+ 'HTTP/1.1 101 Switching Protocols'
19
19
  end
20
20
 
21
21
  # @see WebSocket::Handshake::Handler::Base#handshake_keys
22
22
  def handshake_keys
23
23
  [
24
- ["Upgrade", "websocket"],
25
- ["Connection", "Upgrade"],
26
- ["Sec-WebSocket-Accept", signature]
24
+ ['Upgrade', 'websocket'],
25
+ ['Connection', 'Upgrade'],
26
+ ['Sec-WebSocket-Accept', signature]
27
27
  ]
28
28
  end
29
29
 
30
30
  # Signature of response, created from client request Sec-WebSocket-Key
31
31
  # @return [String] signature
32
32
  def signature
33
- return unless key = @handshake.headers['sec-websocket-key']
33
+ return unless key
34
34
  string_to_sign = "#{key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
35
35
  Base64.encode64(Digest::SHA1.digest(string_to_sign)).chomp
36
36
  end
37
37
 
38
38
  def verify_key
39
- (@handshake.headers['sec-websocket-key'] ? true : raise(WebSocket::Error::Handshake::InvalidAuthentication))
39
+ raise WebSocket::Error::Handshake::InvalidAuthentication unless key
40
+ true
41
+ end
42
+
43
+ def key
44
+ @handshake.headers['sec-websocket-key']
40
45
  end
41
46
 
42
47
  end
@@ -7,16 +7,16 @@ module WebSocket
7
7
 
8
8
  # @see WebSocket::Handshake::Handler::Base#header_line
9
9
  def header_line
10
- "HTTP/1.1 101 Web Socket Protocol Handshake"
10
+ 'HTTP/1.1 101 Web Socket Protocol Handshake'
11
11
  end
12
12
 
13
13
  # @see WebSocket::Handshake::Handler::Base#handshake_keys
14
14
  def handshake_keys
15
15
  [
16
- ["Upgrade", "WebSocket"],
17
- ["Connection", "Upgrade"],
18
- ["WebSocket-Origin", @handshake.headers['origin']],
19
- ["WebSocket-Location", @handshake.uri]
16
+ ['Upgrade', 'WebSocket'],
17
+ ['Connection', 'Upgrade'],
18
+ ['WebSocket-Origin', @handshake.headers['origin']],
19
+ ['WebSocket-Location', @handshake.uri]
20
20
  ]
21
21
  end
22
22
 
@@ -19,16 +19,16 @@ module WebSocket
19
19
 
20
20
  # @see WebSocket::Handshake::Handler::Base#header_line
21
21
  def header_line
22
- "HTTP/1.1 101 WebSocket Protocol Handshake"
22
+ 'HTTP/1.1 101 WebSocket Protocol Handshake'
23
23
  end
24
24
 
25
25
  # @see WebSocket::Handshake::Handler::Base#handshake_keys
26
26
  def handshake_keys
27
27
  [
28
- ["Upgrade", "WebSocket"],
29
- ["Connection", "Upgrade"],
30
- ["Sec-WebSocket-Origin", @handshake.headers['origin']],
31
- ["Sec-WebSocket-Location", @handshake.uri]
28
+ ['Upgrade', 'WebSocket'],
29
+ ['Connection', 'Upgrade'],
30
+ ['Sec-WebSocket-Origin', @handshake.headers['origin']],
31
+ ['Sec-WebSocket-Location', @handshake.uri]
32
32
  ]
33
33
  end
34
34
 
@@ -45,10 +45,10 @@ module WebSocket
45
45
  # Refer to 5.2 4-9 of the draft 76
46
46
  first = numbers_over_spaces(@handshake.headers['sec-websocket-key1'].to_s)
47
47
  second = numbers_over_spaces(@handshake.headers['sec-websocket-key2'].to_s)
48
- third = @handshake.instance_variable_get('@leftovers').strip
48
+ third = @handshake.leftovers.strip
49
49
 
50
- sum = [first].pack("N*") +
51
- [second].pack("N*") +
50
+ sum = [first].pack('N*') +
51
+ [second].pack('N*') +
52
52
  third
53
53
  Digest::MD5.digest(sum)
54
54
  end
@@ -68,7 +68,7 @@ module WebSocket
68
68
 
69
69
  quotient = numbers / spaces
70
70
 
71
- raise WebSocket::Error::Handshake::InvalidAuthentication if quotient > 2**32-1
71
+ raise WebSocket::Error::Handshake::InvalidAuthentication if quotient > 2**32 - 1
72
72
 
73
73
  return quotient
74
74
  end
@@ -38,7 +38,7 @@ module WebSocket
38
38
  # @option args [Boolean] :secure If true then server will use wss:// protocol
39
39
  #
40
40
  # @example
41
- # Websocket::Handshake::Server.new(:secure => true)
41
+ # Websocket::Handshake::Server.new(secure: true)
42
42
  def initialize(args = {})
43
43
  super
44
44
  @secure = !!args[:secure]
@@ -61,9 +61,7 @@ module WebSocket
61
61
  # EOF
62
62
  def <<(data)
63
63
  @data << data
64
- if parse_data
65
- set_version
66
- end
64
+ set_version if parse_data
67
65
  end
68
66
  rescue_method :<<
69
67
 
@@ -73,31 +71,34 @@ module WebSocket
73
71
  # @example
74
72
  # @handshake.from_rack(env)
75
73
  def from_rack(env)
76
- @headers = env.select {|key, value|
74
+ @headers = env.select do |key, value|
77
75
  key =~ /\AHTTP_/
78
- }.inject({}) {|memo, tuple|
76
+ end.reduce({}) do |memo, tuple|
79
77
  key, value = tuple
80
78
  memo[key.gsub(/\AHTTP_/, '').gsub('_', '-').downcase] = value
81
79
  memo
82
- }
80
+ end
81
+
82
+ @path = env['REQUEST_PATH']
83
+ @query = env['QUERY_STRING']
83
84
 
84
- @path = env["REQUEST_PATH"]
85
- @query = env["QUERY_STRING"]
85
+ set_version
86
86
 
87
87
  # Passenger is blocking on read
88
88
  # Unicorn doesn't support readpartial
89
89
  # Maybe someone is providing even plain string?
90
90
  # Better safe than sorry...
91
- input = env['rack.input']
92
- @leftovers = if input.respond_to?(:readpartial)
93
- input.readpartial
94
- elsif input.respond_to?(:read)
95
- input.read
96
- else
97
- input.to_s
98
- end
91
+ if @version == 76
92
+ input = env['rack.input']
93
+ @leftovers = if input.respond_to?(:readpartial)
94
+ input.readpartial
95
+ elsif input.respond_to?(:read)
96
+ input.read
97
+ else
98
+ input.to_s
99
+ end
100
+ end
99
101
 
100
- set_version
101
102
  @state = :finished
102
103
  end
103
104
 
@@ -112,8 +113,8 @@ module WebSocket
112
113
  # @handshake.from_hash(hash)
113
114
  def from_hash(hash)
114
115
  @headers = hash[:headers] || {}
115
- @path = hash[:path] || "/"
116
- @query = hash[:query] || ""
116
+ @path = hash[:path] || '/'
117
+ @query = hash[:query] || ''
117
118
  @leftovers = hash[:body]
118
119
 
119
120
  set_version
@@ -129,13 +130,13 @@ module WebSocket
129
130
  # Host of server according to client header
130
131
  # @return [String] host
131
132
  def host
132
- @headers["host"].to_s.split(":")[0].to_s
133
+ @headers['host'].to_s.split(':')[0].to_s
133
134
  end
134
135
 
135
136
  # Port of server according to client header
136
137
  # @return [String] port
137
138
  def port
138
- @headers["host"].to_s.split(":")[1]
139
+ @headers['host'].to_s.split(':')[1]
139
140
  end
140
141
 
141
142
  private
@@ -144,7 +145,7 @@ module WebSocket
144
145
  def set_version
145
146
  @version = @headers['sec-websocket-version'].to_i if @headers['sec-websocket-version']
146
147
  @version ||= @headers['sec-websocket-draft'].to_i if @headers['sec-websocket-draft']
147
- @version ||= 76 if @leftovers != ""
148
+ @version ||= 76 if @headers['sec-websocket-key1']
148
149
  @version ||= 75
149
150
  include_version
150
151
  end
@@ -169,7 +170,7 @@ module WebSocket
169
170
  line_parts = line.match(PATH)
170
171
  raise WebSocket::Error::Handshake::InvalidHeader unless line_parts
171
172
  method = line_parts[1].strip
172
- raise WebSocket::Error::Handshake::GetRequestRequired unless method == "GET"
173
+ raise WebSocket::Error::Handshake::GetRequestRequired unless method == 'GET'
173
174
 
174
175
  resource_name = line_parts[2].strip
175
176
  @path, @query = resource_name.split('?', 2)
@@ -1,3 +1,3 @@
1
1
  module WebSocket
2
- VERSION = '1.1.4'
2
+ VERSION = '1.2.0'
3
3
  end
@@ -1,9 +1,9 @@
1
1
  # encoding: binary
2
2
  require 'spec_helper'
3
3
 
4
- describe 'Incoming frame draft 03' do
4
+ RSpec.describe 'Incoming frame draft 03' do
5
5
  let(:version) { 3 }
6
- let(:frame) { WebSocket::Frame::Incoming.new(:version => version, :data => encoded_text) }
6
+ let(:frame) { WebSocket::Frame::Incoming.new(version: version, data: encoded_text) }
7
7
  let(:encoded_text) { nil }
8
8
  let(:decoded_text) { nil }
9
9
  let(:frame_type) { nil }
@@ -12,86 +12,86 @@ describe 'Incoming frame draft 03' do
12
12
 
13
13
  it_should_behave_like 'valid_incoming_frame'
14
14
 
15
- context "should properly decode close frame" do
15
+ context 'should properly decode close frame' do
16
16
  let(:encoded_text) { "\x01\x05" + decoded_text }
17
17
  let(:frame_type) { :close }
18
- let(:decoded_text) { "Hello" }
18
+ let(:decoded_text) { 'Hello' }
19
19
 
20
20
  it_should_behave_like 'valid_incoming_frame'
21
21
  end
22
22
 
23
- context "should properly decode ping frame" do
23
+ context 'should properly decode ping frame' do
24
24
  let(:encoded_text) { "\x02\x05" + decoded_text }
25
25
  let(:frame_type) { :ping }
26
- let(:decoded_text) { "Hello" }
26
+ let(:decoded_text) { 'Hello' }
27
27
 
28
28
  it_should_behave_like 'valid_incoming_frame'
29
29
  end
30
30
 
31
- context "should properly decode pong frame" do
31
+ context 'should properly decode pong frame' do
32
32
  let(:encoded_text) { "\x03\x05" + decoded_text }
33
33
  let(:frame_type) { :pong }
34
- let(:decoded_text) { "Hello" }
34
+ let(:decoded_text) { 'Hello' }
35
35
 
36
36
  it_should_behave_like 'valid_incoming_frame'
37
37
  end
38
38
 
39
- context "should properly decode text frame" do
39
+ context 'should properly decode text frame' do
40
40
  let(:encoded_text) { "\x04\x05" + decoded_text }
41
- let(:decoded_text) { "Hello" }
41
+ let(:decoded_text) { 'Hello' }
42
42
  let(:frame_type) { :text }
43
43
 
44
44
  it_should_behave_like 'valid_incoming_frame'
45
45
  end
46
46
 
47
- context "should properly decode text frame with continuation" do
47
+ context 'should properly decode text frame with continuation' do
48
48
  let(:encoded_text) { "\x84\x03Hel\x00\x02lo" }
49
49
  let(:frame_type) { :text }
50
- let(:decoded_text) { "Hello" }
50
+ let(:decoded_text) { 'Hello' }
51
51
 
52
52
  it_should_behave_like 'valid_incoming_frame'
53
53
  end
54
54
 
55
- context "should properly decode text frame in between of continuation" do
55
+ context 'should properly decode text frame in between of continuation' do
56
56
  let(:encoded_text) { "\x84\x03Hel\x03\x03abc\x00\x02lo" }
57
57
  let(:frame_type) { [:pong, :text] }
58
- let(:decoded_text) { ["abc", "Hello"] }
58
+ let(:decoded_text) { ['abc', 'Hello'] }
59
59
 
60
60
  it_should_behave_like 'valid_incoming_frame'
61
61
  end
62
62
 
63
- context "should not return unfinished more frame" do
63
+ context 'should not return unfinished more frame' do
64
64
  let(:encoded_text) { "\x84\x03Hel\x03\x03abc" }
65
65
  let(:frame_type) { :pong }
66
- let(:decoded_text) { "abc" }
66
+ let(:decoded_text) { 'abc' }
67
67
 
68
68
  it_should_behave_like 'valid_incoming_frame'
69
69
  end
70
70
 
71
- context "should properly decode 256 bytes binary frame" do
71
+ context 'should properly decode 256 bytes binary frame' do
72
72
  let(:encoded_text) { "\x05\x7E\x01\x00" + decoded_text }
73
73
  let(:frame_type) { :binary }
74
- let(:decoded_text) { "a" * 256 }
74
+ let(:decoded_text) { 'a' * 256 }
75
75
 
76
76
  it_should_behave_like 'valid_incoming_frame'
77
77
  end
78
78
 
79
- context "should properly decode 64KiB binary frame" do
79
+ context 'should properly decode 64KiB binary frame' do
80
80
  let(:encoded_text) { "\x05\x7F\x00\x00\x00\x00\x00\x01\x00\x00" + decoded_text }
81
81
  let(:frame_type) { :binary }
82
- let(:decoded_text) { "a" * 65536 }
82
+ let(:decoded_text) { 'a' * 65_536 }
83
83
 
84
84
  it_should_behave_like 'valid_incoming_frame'
85
85
  end
86
86
 
87
- context "should wait with incomplete frame" do
87
+ context 'should wait with incomplete frame' do
88
88
  let(:encoded_text) { "\x04\x06Hello" }
89
89
  let(:decoded_text) { nil }
90
90
 
91
91
  it_should_behave_like 'valid_incoming_frame'
92
92
  end
93
93
 
94
- context "should raise error with invalid opcode" do
94
+ context 'should raise error with invalid opcode' do
95
95
  let(:encoded_text) { "\x09\x05Hello" }
96
96
  let(:decoded_text) { nil }
97
97
  let(:error) { WebSocket::Error::Frame::UnknownOpcode }
@@ -99,15 +99,15 @@ describe 'Incoming frame draft 03' do
99
99
  it_should_behave_like 'valid_incoming_frame'
100
100
  end
101
101
 
102
- context "should raise error with too long frame" do
103
- let(:encoded_text) { "\x04\x7F" + "a" * WebSocket.max_frame_size }
102
+ context 'should raise error with too long frame' do
103
+ let(:encoded_text) { "\x04\x7F" + 'a' * WebSocket.max_frame_size }
104
104
  let(:decoded_text) { nil }
105
105
  let(:error) { WebSocket::Error::Frame::TooLong }
106
106
 
107
107
  it_should_behave_like 'valid_incoming_frame'
108
108
  end
109
109
 
110
- context "should raise error with continuation frame without more frame earlier" do
110
+ context 'should raise error with continuation frame without more frame earlier' do
111
111
  let(:encoded_text) { "\x00\x05Hello" }
112
112
  let(:decoded_text) { nil }
113
113
  let(:error) { WebSocket::Error::Frame::UnexpectedContinuationFrame }