websocket 1.2.3 → 1.2.4
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -0
- data/.travis.yml +14 -3
- data/CHANGELOG.md +4 -0
- data/Gemfile +0 -4
- data/README.md +1 -0
- data/Rakefile +6 -3
- data/lib/websocket/error.rb +6 -0
- data/lib/websocket/frame/base.rb +2 -2
- data/lib/websocket/frame/data.rb +1 -1
- data/lib/websocket/frame/handler/base.rb +2 -2
- data/lib/websocket/frame/handler/handler03.rb +12 -12
- data/lib/websocket/frame/handler/handler07.rb +8 -8
- data/lib/websocket/frame/handler/handler75.rb +6 -6
- data/lib/websocket/frame/outgoing.rb +1 -1
- data/lib/websocket/handshake/base.rb +13 -3
- data/lib/websocket/handshake/client.rb +15 -14
- data/lib/websocket/handshake/handler/client04.rb +12 -2
- data/lib/websocket/handshake/handler/client75.rb +15 -0
- data/lib/websocket/handshake/handler/client76.rb +11 -2
- data/lib/websocket/handshake/handler/server04.rb +8 -2
- data/lib/websocket/handshake/handler/server75.rb +7 -1
- data/lib/websocket/handshake/handler/server76.rb +10 -6
- data/lib/websocket/handshake/server.rb +5 -4
- data/lib/websocket/version.rb +1 -1
- data/spec/frame/outgoing_03_spec.rb +1 -1
- data/spec/frame/outgoing_04_spec.rb +1 -1
- data/spec/handshake/client_04_spec.rb +41 -0
- data/spec/handshake/client_75_spec.rb +25 -0
- data/spec/handshake/client_76_spec.rb +25 -0
- data/spec/handshake/server_04_spec.rb +32 -1
- data/spec/handshake/server_75_spec.rb +22 -0
- data/spec/handshake/server_76_spec.rb +22 -0
- data/spec/support/handshake_requests.rb +3 -3
- data/spec/support/incoming_frames.rb +15 -13
- data/websocket.gemspec +5 -0
- metadata +60 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4bf1d6b28aadb7224c53978094d179673364550a
|
4
|
+
data.tar.gz: 0d376d27450259b680f585c8ff9dc56a7d3e5210
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ab8110e8e84a36c606da58933b48b89e9df724dc145b4ac4d7cbe36f51c6c56f02ece9ad077103be2fba3d4c617c19692f0a2e1934037a39bf50bb47c81c6c1
|
7
|
+
data.tar.gz: 7239ff151df05fb67c67a713c6485377a1e0ab13446a945cef84497979aab255c18a09eaa794cc068da59e44d9f7f536af6d8bfe8dc75cd8fa3d70d1b9f43dcf
|
data/.rubocop.yml
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
AllCops:
|
2
|
+
DisplayCopNames: true
|
3
|
+
|
1
4
|
# Target: 15
|
2
5
|
Metrics/AbcSize:
|
3
6
|
Max: 24
|
@@ -5,6 +8,10 @@ Metrics/AbcSize:
|
|
5
8
|
- lib/websocket/frame/handler/handler75.rb
|
6
9
|
- spec/**/*
|
7
10
|
|
11
|
+
Metrics/BlockLength:
|
12
|
+
Exclude:
|
13
|
+
- spec/**/*
|
14
|
+
|
8
15
|
Metrics/ClassLength:
|
9
16
|
Enabled: false
|
10
17
|
|
data/.travis.yml
CHANGED
@@ -1,12 +1,23 @@
|
|
1
1
|
language: ruby
|
2
|
-
script: "bundle exec rake
|
2
|
+
script: "bundle exec rake"
|
3
3
|
rvm:
|
4
4
|
- 1.9.3
|
5
5
|
- 2.0
|
6
6
|
- 2.1
|
7
7
|
- 2.2
|
8
|
-
-
|
9
|
-
- jruby-19mode
|
8
|
+
- 2.3
|
10
9
|
- ruby-head
|
10
|
+
- jruby
|
11
|
+
- jruby-head
|
12
|
+
- rbx
|
13
|
+
- rbx-head
|
14
|
+
|
15
|
+
matrix:
|
16
|
+
allow_failures:
|
17
|
+
- rvm: ruby-head
|
18
|
+
- rvm: jruby-head
|
19
|
+
- rvm: rbx
|
20
|
+
- rvm: rbx-head
|
21
|
+
|
11
22
|
before_install:
|
12
23
|
- gem install bundler
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
Universal Ruby library to handle WebSocket protocol. It focuses on providing abstraction layer over [WebSocket API](http://dev.w3.org/html5/websockets/) instead of providing server or client functionality.
|
4
4
|
|
5
5
|
[](http://badge.fury.io/rb/websocket)
|
6
|
+
[](https://rubygems.org/gems/websocket)
|
6
7
|
[](http://travis-ci.org/imanel/websocket-ruby)
|
7
8
|
[](https://codeclimate.com/github/imanel/websocket-ruby)
|
8
9
|
|
data/Rakefile
CHANGED
@@ -1,14 +1,17 @@
|
|
1
1
|
require 'bundler'
|
2
|
-
Bundler::GemHelper.install_tasks
|
3
|
-
|
4
2
|
require 'rspec/core/rake_task'
|
3
|
+
require 'rubocop/rake_task'
|
4
|
+
|
5
|
+
Bundler::GemHelper.install_tasks
|
5
6
|
|
6
7
|
RSpec::Core::RakeTask.new do |t|
|
7
8
|
t.rspec_opts = ['-c', '-f progress']
|
8
9
|
t.pattern = 'spec/**/*_spec.rb'
|
9
10
|
end
|
10
11
|
|
11
|
-
|
12
|
+
RuboCop::RakeTask.new
|
13
|
+
|
14
|
+
task default: [:spec, :rubocop]
|
12
15
|
|
13
16
|
namespace :autobahn do
|
14
17
|
desc 'Run autobahn tests for client'
|
data/lib/websocket/error.rb
CHANGED
@@ -99,6 +99,12 @@ module WebSocket
|
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
|
+
class UnsupportedProtocol < ::WebSocket::Error::Handshake
|
103
|
+
def message
|
104
|
+
:unsupported_protocol
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
102
108
|
class InvalidStatusCode < ::WebSocket::Error::Handshake
|
103
109
|
def message
|
104
110
|
:invalid_status_code
|
data/lib/websocket/frame/base.rb
CHANGED
@@ -36,7 +36,7 @@ module WebSocket
|
|
36
36
|
|
37
37
|
# Implement in submodules
|
38
38
|
def supported_frames
|
39
|
-
|
39
|
+
raise NotImplementedError
|
40
40
|
end
|
41
41
|
|
42
42
|
# Recreate inspect as #to_s was overwritten
|
@@ -58,7 +58,7 @@ module WebSocket
|
|
58
58
|
when 4 then Handler::Handler04.new(self)
|
59
59
|
when 5..6 then Handler::Handler05.new(self)
|
60
60
|
when 7..13 then Handler::Handler07.new(self)
|
61
|
-
else
|
61
|
+
else raise WebSocket::Error::Frame::UnknownVersion
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
data/lib/websocket/frame/data.rb
CHANGED
@@ -16,7 +16,7 @@ module WebSocket
|
|
16
16
|
|
17
17
|
# Extract mask from 4 first bytes according to spec
|
18
18
|
def set_mask
|
19
|
-
|
19
|
+
raise WebSocket::Error::Frame::MaskTooShort if bytesize < 4
|
20
20
|
@masking_key = self[0..3].bytes.to_a
|
21
21
|
end
|
22
22
|
|
@@ -9,13 +9,13 @@ module WebSocket
|
|
9
9
|
# Convert data to raw frame ready to send to client
|
10
10
|
# @return [String] Encoded frame
|
11
11
|
def encode_frame
|
12
|
-
|
12
|
+
raise NotImplementedError
|
13
13
|
end
|
14
14
|
|
15
15
|
# Convert raw data to decoded frame
|
16
16
|
# @return [WebSocket::Frame::Incoming] Frame if found, nil otherwise
|
17
17
|
def decode_frame
|
18
|
-
|
18
|
+
raise NotImplementedError
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
@@ -13,10 +13,10 @@ module WebSocket
|
|
13
13
|
pong: 3,
|
14
14
|
text: 4,
|
15
15
|
binary: 5
|
16
|
-
}
|
16
|
+
}.freeze
|
17
17
|
|
18
18
|
# Hash of frame opcodes and it's names
|
19
|
-
FRAME_TYPES_INVERSE = FRAME_TYPES.invert
|
19
|
+
FRAME_TYPES_INVERSE = FRAME_TYPES.invert.freeze
|
20
20
|
|
21
21
|
# @see WebSocket::Frame::Base#supported_frames
|
22
22
|
def supported_frames
|
@@ -50,7 +50,7 @@ module WebSocket
|
|
50
50
|
elsif frame_type == :continuation
|
51
51
|
return decode_finish_continuation_frame(application_data)
|
52
52
|
else
|
53
|
-
|
53
|
+
raise(WebSocket::Error::Frame::InvalidPayloadEncoding) if frame_type == :text && !application_data.valid_encoding?
|
54
54
|
return @frame.class.new(version: @frame.version, type: frame_type, data: application_data, decoded: true)
|
55
55
|
end
|
56
56
|
end
|
@@ -74,7 +74,7 @@ module WebSocket
|
|
74
74
|
# @return [Integer] opcode or nil
|
75
75
|
# @raise [WebSocket::Error] if frame opcode is not known
|
76
76
|
def type_to_opcode(frame_type)
|
77
|
-
FRAME_TYPES[frame_type] ||
|
77
|
+
FRAME_TYPES[frame_type] || raise(WebSocket::Error::Frame::UnknownFrameType)
|
78
78
|
end
|
79
79
|
|
80
80
|
# Convert frame opcode to type name
|
@@ -82,7 +82,7 @@ module WebSocket
|
|
82
82
|
# @return [Symbol] Frame type name or nil
|
83
83
|
# @raise [WebSocket::Error] if frame type name is not known
|
84
84
|
def opcode_to_type(opcode)
|
85
|
-
FRAME_TYPES_INVERSE[opcode] ||
|
85
|
+
FRAME_TYPES_INVERSE[opcode] || raise(WebSocket::Error::Frame::UnknownOpcode)
|
86
86
|
end
|
87
87
|
|
88
88
|
def encode_header
|
@@ -117,7 +117,7 @@ module WebSocket
|
|
117
117
|
frame_length = header_length + payload_length
|
118
118
|
frame_length += 4 if mask
|
119
119
|
|
120
|
-
|
120
|
+
raise(WebSocket::Error::Frame::TooLong) if frame_length > WebSocket.max_frame_size
|
121
121
|
|
122
122
|
# Check buffer size
|
123
123
|
return unless buffer_exists?(frame_length) # Buffer incomplete
|
@@ -135,13 +135,13 @@ module WebSocket
|
|
135
135
|
def decode_first_byte
|
136
136
|
first_byte = @frame.data.getbyte(0)
|
137
137
|
|
138
|
-
|
138
|
+
raise(WebSocket::Error::Frame::ReservedBitUsed) if first_byte & 0b01110000 != 0b00000000
|
139
139
|
|
140
140
|
more = ((first_byte & 0b10000000) == 0b10000000) ^ fin
|
141
141
|
frame_type = opcode_to_type first_byte & 0b00001111
|
142
142
|
|
143
|
-
|
144
|
-
|
143
|
+
raise(WebSocket::Error::Frame::FragmentedControlFrame) if more && control_frame?(frame_type)
|
144
|
+
raise(WebSocket::Error::Frame::DataFrameInsteadContinuation) if data_frame?(frame_type) && !@application_data_buffer.nil?
|
145
145
|
|
146
146
|
[more, frame_type]
|
147
147
|
end
|
@@ -152,7 +152,7 @@ module WebSocket
|
|
152
152
|
mask = @frame.incoming_masking? && (second_byte & 0b10000000) == 0b10000000
|
153
153
|
length = second_byte & 0b01111111
|
154
154
|
|
155
|
-
|
155
|
+
raise(WebSocket::Error::Frame::ControlFramePayloadTooLong) if length > 125 && control_frame?(frame_type)
|
156
156
|
|
157
157
|
header_length, payload_length = decode_payload_length(length)
|
158
158
|
|
@@ -202,10 +202,10 @@ module WebSocket
|
|
202
202
|
end
|
203
203
|
|
204
204
|
def decode_finish_continuation_frame(application_data)
|
205
|
-
|
205
|
+
raise(WebSocket::Error::Frame::UnexpectedContinuationFrame) unless @frame_type
|
206
206
|
@application_data_buffer << application_data
|
207
207
|
# Test valid UTF-8 encoding
|
208
|
-
|
208
|
+
raise(WebSocket::Error::Frame::InvalidPayloadEncoding) if @frame_type == :text && !@application_data_buffer.valid_encoding?
|
209
209
|
message = @frame.class.new(version: @frame.version, type: @frame_type, data: @application_data_buffer, decoded: true)
|
210
210
|
@application_data_buffer = nil
|
211
211
|
@frame_type = nil
|
@@ -12,15 +12,15 @@ module WebSocket
|
|
12
12
|
close: 8,
|
13
13
|
ping: 9,
|
14
14
|
pong: 10
|
15
|
-
}
|
15
|
+
}.freeze
|
16
16
|
|
17
17
|
# Hash of frame opcodes and it's names
|
18
|
-
FRAME_TYPES_INVERSE = FRAME_TYPES.invert
|
18
|
+
FRAME_TYPES_INVERSE = FRAME_TYPES.invert.freeze
|
19
19
|
|
20
20
|
def encode_frame
|
21
21
|
if @frame.type == :close
|
22
22
|
code = @frame.code || 1000
|
23
|
-
|
23
|
+
raise WebSocket::Error::Frame::UnknownCloseCode unless valid_code?(code)
|
24
24
|
@frame.data = Data.new([code].pack('n') + @frame.data.to_s)
|
25
25
|
@frame.code = nil
|
26
26
|
end
|
@@ -32,8 +32,8 @@ module WebSocket
|
|
32
32
|
if close_code?(result)
|
33
33
|
code = result.data.slice!(0..1)
|
34
34
|
result.code = code.unpack('n').first
|
35
|
-
|
36
|
-
|
35
|
+
raise WebSocket::Error::Frame::UnknownCloseCode unless valid_code?(result.code)
|
36
|
+
raise WebSocket::Error::Frame::InvalidPayloadEncoding unless valid_encoding?(result.data)
|
37
37
|
end
|
38
38
|
result
|
39
39
|
end
|
@@ -41,7 +41,7 @@ module WebSocket
|
|
41
41
|
private
|
42
42
|
|
43
43
|
def valid_code?(code)
|
44
|
-
[1000, 1001, 1002, 1003, 1007, 1008, 1009, 1010, 1011].include?(code) || (3000..4999).
|
44
|
+
[1000, 1001, 1002, 1003, 1007, 1008, 1009, 1010, 1011].include?(code) || (3000..4999).cover?(code)
|
45
45
|
end
|
46
46
|
|
47
47
|
def valid_encoding?(data)
|
@@ -61,7 +61,7 @@ module WebSocket
|
|
61
61
|
# @return [Integer] opcode or nil
|
62
62
|
# @raise [WebSocket::Error] if frame opcode is not known
|
63
63
|
def type_to_opcode(frame_type)
|
64
|
-
FRAME_TYPES[frame_type] ||
|
64
|
+
FRAME_TYPES[frame_type] || raise(WebSocket::Error::Frame::UnknownFrameType)
|
65
65
|
end
|
66
66
|
|
67
67
|
# Convert frame opcode to type name
|
@@ -69,7 +69,7 @@ module WebSocket
|
|
69
69
|
# @return [Symbol] Frame type name or nil
|
70
70
|
# @raise [WebSocket::Error] if frame type name is not known
|
71
71
|
def opcode_to_type(opcode)
|
72
|
-
FRAME_TYPES_INVERSE[opcode] ||
|
72
|
+
FRAME_TYPES_INVERSE[opcode] || raise(WebSocket::Error::Frame::UnknownOpcode)
|
73
73
|
end
|
74
74
|
end
|
75
75
|
end
|
@@ -17,13 +17,13 @@ module WebSocket
|
|
17
17
|
ary = ["\x00", @frame.data, "\xff"]
|
18
18
|
ary.map { |s| s.encode('UTF-8', 'UTF-8', invalid: :replace) }
|
19
19
|
ary.join
|
20
|
-
else
|
20
|
+
else raise WebSocket::Error::Frame::UnknownFrameType
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
# @see WebSocket::Frame::Handler::Base#decode_frame
|
25
25
|
def decode_frame
|
26
|
-
return if @frame.data.size
|
26
|
+
return if @frame.data.size.zero?
|
27
27
|
|
28
28
|
pointer = 0
|
29
29
|
frame_type = @frame.data.getbyte(pointer)
|
@@ -42,7 +42,7 @@ module WebSocket
|
|
42
42
|
break unless (b & 0x80) == 0x80
|
43
43
|
end
|
44
44
|
|
45
|
-
|
45
|
+
raise WebSocket::Error::Frame::TooLong if length > ::WebSocket.max_frame_size
|
46
46
|
|
47
47
|
unless @frame.data.getbyte(pointer + length - 1).nil?
|
48
48
|
# Straight from spec - I'm sure this isn't crazy...
|
@@ -51,17 +51,17 @@ module WebSocket
|
|
51
51
|
@frame.instance_variable_set '@data', @frame.data[(pointer + length)..-1]
|
52
52
|
|
53
53
|
# If the /frame type/ is 0xFF and the /length/ was 0, then close
|
54
|
-
if length
|
54
|
+
if length.zero?
|
55
55
|
@frame.class.new(version: @frame.version, type: :close, decoded: true)
|
56
56
|
end
|
57
57
|
end
|
58
58
|
else
|
59
59
|
# If the high-order bit of the /frame type/ byte is _not_ set
|
60
60
|
|
61
|
-
|
61
|
+
raise WebSocket::Error::Frame::Invalid if @frame.data.getbyte(0) != 0x00
|
62
62
|
|
63
63
|
# Addition to the spec to protect against malicious requests
|
64
|
-
|
64
|
+
raise WebSocket::Error::Frame::TooLong if @frame.data.size > ::WebSocket.max_frame_size
|
65
65
|
|
66
66
|
msg = @frame.data.slice!(/\A\x00[^\xff]*\xff/)
|
67
67
|
if msg
|
@@ -24,7 +24,7 @@ module WebSocket
|
|
24
24
|
|
25
25
|
# Return raw frame formatted for sending.
|
26
26
|
def to_s
|
27
|
-
|
27
|
+
raise WebSocket::Error::Frame::UnknownFrameType unless supported?
|
28
28
|
@handler.encode_frame
|
29
29
|
end
|
30
30
|
rescue_method :to_s
|
@@ -5,7 +5,8 @@ module WebSocket
|
|
5
5
|
include ExceptionHandler
|
6
6
|
|
7
7
|
attr_reader :host, :port, :path, :query,
|
8
|
-
:state, :version, :secure,
|
8
|
+
:state, :version, :secure,
|
9
|
+
:headers, :protocols
|
9
10
|
|
10
11
|
# Initialize new WebSocket Handshake and set it's state to :new
|
11
12
|
def initialize(args = {})
|
@@ -16,6 +17,7 @@ module WebSocket
|
|
16
17
|
|
17
18
|
@data = ''
|
18
19
|
@headers ||= {}
|
20
|
+
@protocols ||= []
|
19
21
|
end
|
20
22
|
|
21
23
|
# @abstract Add data to handshake
|
@@ -52,7 +54,7 @@ module WebSocket
|
|
52
54
|
|
53
55
|
# @abstract Should send data after parsing is finished?
|
54
56
|
def should_respond?
|
55
|
-
|
57
|
+
raise NotImplementedError
|
56
58
|
end
|
57
59
|
|
58
60
|
# Data left from parsing. Sometimes data that doesn't belong to handshake are added - use this method to retrieve them.
|
@@ -104,7 +106,15 @@ module WebSocket
|
|
104
106
|
|
105
107
|
lines.each do |line|
|
106
108
|
h = HEADER.match(line)
|
107
|
-
|
109
|
+
next unless h # Skip any invalid headers
|
110
|
+
key = h[1].strip.downcase
|
111
|
+
val = h[2].strip
|
112
|
+
# If the header is already set and refers to the websocket protocol, append the new value
|
113
|
+
if @headers.key?(key) && key =~ /^(sec-)?websocket-protocol$/
|
114
|
+
@headers[key] << ", #{val}"
|
115
|
+
else
|
116
|
+
@headers[key] = val
|
117
|
+
end
|
108
118
|
end
|
109
119
|
|
110
120
|
@state = :finished
|
@@ -38,16 +38,17 @@ module WebSocket
|
|
38
38
|
#
|
39
39
|
# @param [Hash] args Arguments for client
|
40
40
|
#
|
41
|
-
# @option args [String]
|
42
|
-
# @option args [String]
|
43
|
-
# @option args [String]
|
44
|
-
# @option args [Integer]
|
45
|
-
# @option args [String]
|
46
|
-
# @option args [Boolean]
|
47
|
-
# @option args [String]
|
48
|
-
# @option args [String]
|
49
|
-
# @option args [
|
50
|
-
# @option args [
|
41
|
+
# @option args [String] :host Host of request. Required if no :url param was provided.
|
42
|
+
# @option args [String] :origin Origin of request. Optional, should be used mostly by browsers. Default: nil
|
43
|
+
# @option args [String] :path Path of request. Should start with '/'. Default: '/'
|
44
|
+
# @option args [Integer] :port Port of request. Default: nil
|
45
|
+
# @option args [String] :query. Query for request. Should be in format "aaa=bbb&ccc=ddd"
|
46
|
+
# @option args [Boolean] :secure Defines protocol to use. If true then wss://, otherwise ws://. This option will not change default port - it should be handled by programmer.
|
47
|
+
# @option args [String] :url URL of request. Must by in format like ws://example.com/path?query=true. Every part of this url will be overriden by more specific arguments.
|
48
|
+
# @option args [String] :uri Alias to :url
|
49
|
+
# @option args [Array<String>] :protocols An array of supported sub-protocols
|
50
|
+
# @option args [Integer] :version Version of WebSocket to use. Default: 13 (this is version from RFC)
|
51
|
+
# @option args [Hash] :headers HTTP headers to use in the handshake
|
51
52
|
#
|
52
53
|
# @example
|
53
54
|
# Websocket::Handshake::Client.new(url: "ws://example.com/path?query=true")
|
@@ -66,7 +67,7 @@ module WebSocket
|
|
66
67
|
@path = '/' if @path.nil? || @path.empty?
|
67
68
|
@version ||= DEFAULT_VERSION
|
68
69
|
|
69
|
-
|
70
|
+
raise WebSocket::Error::Handshake::NoHostProvided unless @host
|
70
71
|
|
71
72
|
include_version
|
72
73
|
end
|
@@ -107,7 +108,7 @@ module WebSocket
|
|
107
108
|
when 1..3 then Handler::Client01.new(self)
|
108
109
|
when 4..10 then Handler::Client04.new(self)
|
109
110
|
when 11..17 then Handler::Client11.new(self)
|
110
|
-
else
|
111
|
+
else raise WebSocket::Error::Handshake::UnknownVersion
|
111
112
|
end
|
112
113
|
end
|
113
114
|
|
@@ -118,9 +119,9 @@ module WebSocket
|
|
118
119
|
# @return [Boolean] True if parsed correctly. False otherwise
|
119
120
|
def parse_first_line(line)
|
120
121
|
line_parts = line.match(FIRST_LINE)
|
121
|
-
|
122
|
+
raise WebSocket::Error::Handshake::InvalidHeader unless line_parts
|
122
123
|
status = line_parts[1]
|
123
|
-
|
124
|
+
raise WebSocket::Error::Handshake::InvalidStatusCode unless status == '101'
|
124
125
|
end
|
125
126
|
end
|
126
127
|
end
|
@@ -7,7 +7,7 @@ module WebSocket
|
|
7
7
|
class Client04 < Client
|
8
8
|
# @see WebSocket::Handshake::Base#valid?
|
9
9
|
def valid?
|
10
|
-
super && verify_accept
|
10
|
+
super && verify_accept && verify_protocol
|
11
11
|
end
|
12
12
|
|
13
13
|
private
|
@@ -25,6 +25,7 @@ module WebSocket
|
|
25
25
|
keys << ['Sec-WebSocket-Origin', @handshake.origin] if @handshake.origin
|
26
26
|
keys << ['Sec-WebSocket-Version', @handshake.version]
|
27
27
|
keys << ['Sec-WebSocket-Key', key]
|
28
|
+
keys << ['Sec-WebSocket-Protocol', @handshake.protocols.join(', ')] if @handshake.protocols.any?
|
28
29
|
keys
|
29
30
|
end
|
30
31
|
|
@@ -43,7 +44,16 @@ module WebSocket
|
|
43
44
|
# Verify if received header Sec-WebSocket-Accept matches generated one.
|
44
45
|
# @return [Boolean] True if accept is matching. False otherwise(appropriate error is set)
|
45
46
|
def verify_accept
|
46
|
-
|
47
|
+
raise WebSocket::Error::Handshake::InvalidAuthentication unless @handshake.headers['sec-websocket-accept'] == accept
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
# Verify if received header Sec-WebSocket-Protocol matches with one of the sent ones
|
52
|
+
# @return [Boolean] True if matching. False otherwise(appropriate error is set)
|
53
|
+
def verify_protocol
|
54
|
+
return true if @handshake.protocols.empty?
|
55
|
+
protos = @handshake.headers['sec-websocket-protocol'].split(/ *, */) & @handshake.protocols
|
56
|
+
raise WebSocket::Error::Handshake::UnsupportedProtocol if protos.empty?
|
47
57
|
true
|
48
58
|
end
|
49
59
|
end
|
@@ -2,6 +2,11 @@ module WebSocket
|
|
2
2
|
module Handshake
|
3
3
|
module Handler
|
4
4
|
class Client75 < Client
|
5
|
+
# @see WebSocket::Handshake::Base#valid?
|
6
|
+
def valid?
|
7
|
+
super && verify_protocol
|
8
|
+
end
|
9
|
+
|
5
10
|
private
|
6
11
|
|
7
12
|
# @see WebSocket::Handshake::Handler::Base#handshake_keys
|
@@ -14,9 +19,19 @@ module WebSocket
|
|
14
19
|
host += ":#{@handshake.port}" if @handshake.port
|
15
20
|
keys << ['Host', host]
|
16
21
|
keys << ['Origin', @handshake.origin] if @handshake.origin
|
22
|
+
keys << ['WebSocket-Protocol', @handshake.protocols.first] if @handshake.protocols.any?
|
17
23
|
keys += super
|
18
24
|
keys
|
19
25
|
end
|
26
|
+
|
27
|
+
# Verify if received header WebSocket-Protocol matches with the sent one
|
28
|
+
# @return [Boolean] True if matching. False otherwise(appropriate error is set)
|
29
|
+
def verify_protocol
|
30
|
+
return true if @handshake.protocols.empty?
|
31
|
+
invalid = @handshake.headers['websocket-protocol'].strip != @handshake.protocols.first
|
32
|
+
raise WebSocket::Error::Handshake::UnsupportedProtocol if invalid
|
33
|
+
true
|
34
|
+
end
|
20
35
|
end
|
21
36
|
end
|
22
37
|
end
|
@@ -6,7 +6,7 @@ module WebSocket
|
|
6
6
|
class Client76 < Client75
|
7
7
|
# @see WebSocket::Handshake::Base#valid?
|
8
8
|
def valid?
|
9
|
-
super && verify_challenge
|
9
|
+
super && verify_challenge && verify_protocol
|
10
10
|
end
|
11
11
|
|
12
12
|
private
|
@@ -62,7 +62,7 @@ module WebSocket
|
|
62
62
|
# Verify if challenge sent by server match generated one
|
63
63
|
# @return [Boolena] True if challenge matches, false otherwise(sets appropriate error)
|
64
64
|
def verify_challenge
|
65
|
-
|
65
|
+
raise WebSocket::Error::Handshake::InvalidAuthentication unless @handshake.leftovers == challenge
|
66
66
|
true
|
67
67
|
end
|
68
68
|
|
@@ -93,6 +93,15 @@ module WebSocket
|
|
93
93
|
def generate_key3
|
94
94
|
[rand(0x100000000)].pack('N') + [rand(0x100000000)].pack('N')
|
95
95
|
end
|
96
|
+
|
97
|
+
# Verify if received header Sec-WebSocket-Protocol matches with the sent one
|
98
|
+
# @return [Boolean] True if matching. False otherwise(appropriate error is set)
|
99
|
+
def verify_protocol
|
100
|
+
return true if @handshake.protocols.empty?
|
101
|
+
invalid = @handshake.headers['sec-websocket-protocol'].strip != @handshake.protocols.first
|
102
|
+
raise WebSocket::Error::Handshake::UnsupportedProtocol if invalid
|
103
|
+
true
|
104
|
+
end
|
96
105
|
end
|
97
106
|
end
|
98
107
|
end
|
@@ -23,7 +23,7 @@ module WebSocket
|
|
23
23
|
%w(Upgrade websocket),
|
24
24
|
%w(Connection Upgrade),
|
25
25
|
['Sec-WebSocket-Accept', signature]
|
26
|
-
]
|
26
|
+
] + protocol
|
27
27
|
end
|
28
28
|
|
29
29
|
# Signature of response, created from client request Sec-WebSocket-Key
|
@@ -35,13 +35,19 @@ module WebSocket
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def verify_key
|
38
|
-
|
38
|
+
raise WebSocket::Error::Handshake::InvalidAuthentication unless key
|
39
39
|
true
|
40
40
|
end
|
41
41
|
|
42
42
|
def key
|
43
43
|
@handshake.headers['sec-websocket-key']
|
44
44
|
end
|
45
|
+
|
46
|
+
def protocol
|
47
|
+
return [] unless @handshake.headers.key?('sec-websocket-protocol')
|
48
|
+
protos = @handshake.headers['sec-websocket-protocol'].split(/ *, */) & @handshake.protocols
|
49
|
+
[['Sec-WebSocket-Protocol', protos.first]]
|
50
|
+
end
|
45
51
|
end
|
46
52
|
end
|
47
53
|
end
|
@@ -16,7 +16,13 @@ module WebSocket
|
|
16
16
|
%w(Connection Upgrade),
|
17
17
|
['WebSocket-Origin', @handshake.headers['origin']],
|
18
18
|
['WebSocket-Location', @handshake.uri]
|
19
|
-
]
|
19
|
+
] + protocol
|
20
|
+
end
|
21
|
+
|
22
|
+
def protocol
|
23
|
+
return [] unless @handshake.headers.key?('websocket-protocol')
|
24
|
+
proto = @handshake.headers['websocket-protocol']
|
25
|
+
[['WebSocket-Protocol', @handshake.protocols.include?(proto) ? proto : nil]]
|
20
26
|
end
|
21
27
|
end
|
22
28
|
end
|
@@ -28,7 +28,7 @@ module WebSocket
|
|
28
28
|
%w(Connection Upgrade),
|
29
29
|
['Sec-WebSocket-Origin', @handshake.headers['origin']],
|
30
30
|
['Sec-WebSocket-Location', @handshake.uri]
|
31
|
-
]
|
31
|
+
] + protocol
|
32
32
|
end
|
33
33
|
|
34
34
|
# @see WebSocket::Handshake::Handler::Base#finishing_line
|
@@ -36,8 +36,6 @@ module WebSocket
|
|
36
36
|
@finishing_line ||= challenge_response
|
37
37
|
end
|
38
38
|
|
39
|
-
private
|
40
|
-
|
41
39
|
# Response to client challenge from request Sec-WebSocket-Key1, Sec-WebSocket-Key2 and leftovers
|
42
40
|
# @return [String] Challenge response or nil if error occured
|
43
41
|
def challenge_response
|
@@ -60,17 +58,23 @@ module WebSocket
|
|
60
58
|
|
61
59
|
spaces = string.scan(/ /).size
|
62
60
|
# As per 5.2.5, abort the connection if spaces are zero.
|
63
|
-
|
61
|
+
raise WebSocket::Error::Handshake::InvalidAuthentication if spaces.zero?
|
64
62
|
|
65
63
|
# As per 5.2.6, abort if numbers is not an integral multiple of spaces
|
66
|
-
|
64
|
+
raise WebSocket::Error::Handshake::InvalidAuthentication if numbers % spaces != 0
|
67
65
|
|
68
66
|
quotient = numbers / spaces
|
69
67
|
|
70
|
-
|
68
|
+
raise WebSocket::Error::Handshake::InvalidAuthentication if quotient > 2**32 - 1
|
71
69
|
|
72
70
|
quotient
|
73
71
|
end
|
72
|
+
|
73
|
+
def protocol
|
74
|
+
return [] unless @handshake.headers.key?('sec-websocket-protocol')
|
75
|
+
proto = @handshake.headers['sec-websocket-protocol']
|
76
|
+
[['Sec-WebSocket-Protocol', @handshake.protocols.include?(proto) ? proto : nil]]
|
77
|
+
end
|
74
78
|
end
|
75
79
|
end
|
76
80
|
end
|
@@ -35,6 +35,7 @@ module WebSocket
|
|
35
35
|
# @param [Hash] args Arguments for server
|
36
36
|
#
|
37
37
|
# @option args [Boolean] :secure If true then server will use wss:// protocol
|
38
|
+
# @option args [Array<String>] :protocols an array of supported sub-protocols
|
38
39
|
#
|
39
40
|
# @example
|
40
41
|
# Websocket::Handshake::Server.new(secure: true)
|
@@ -70,7 +71,7 @@ module WebSocket
|
|
70
71
|
# @example
|
71
72
|
# @handshake.from_rack(env)
|
72
73
|
def from_rack(env)
|
73
|
-
@headers = env.select { |key, _value| key
|
74
|
+
@headers = env.select { |key, _value| key.start_with? 'HTTP_' }.each_with_object({}) do |tuple, memo|
|
74
75
|
key, value = tuple
|
75
76
|
memo[key.gsub(/\AHTTP_/, '').tr('_', '-').downcase] = value
|
76
77
|
end
|
@@ -153,7 +154,7 @@ module WebSocket
|
|
153
154
|
when 75 then Handler::Server75.new(self)
|
154
155
|
when 76, 0..3 then Handler::Server76.new(self)
|
155
156
|
when 4..17 then Handler::Server04.new(self)
|
156
|
-
else
|
157
|
+
else raise WebSocket::Error::Handshake::UnknownVersion
|
157
158
|
end
|
158
159
|
end
|
159
160
|
|
@@ -164,9 +165,9 @@ module WebSocket
|
|
164
165
|
# @return [Boolean] True if parsed correctly. False otherwise
|
165
166
|
def parse_first_line(line)
|
166
167
|
line_parts = line.match(PATH)
|
167
|
-
|
168
|
+
raise WebSocket::Error::Handshake::InvalidHeader unless line_parts
|
168
169
|
method = line_parts[1].strip
|
169
|
-
|
170
|
+
raise WebSocket::Error::Handshake::GetRequestRequired unless method == 'GET'
|
170
171
|
|
171
172
|
resource_name = line_parts[2].strip
|
172
173
|
@path, @query = resource_name.split('?', 2)
|
data/lib/websocket/version.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
4
|
RSpec.describe 'Outgoing frame draft 03' do
|
5
|
-
let(:version) {
|
5
|
+
let(:version) { 3 }
|
6
6
|
let(:frame) { WebSocket::Frame::Outgoing.new(version: version, data: decoded_text, type: frame_type) }
|
7
7
|
let(:decoded_text) { '' }
|
8
8
|
let(:encoded_text) { "\x04\x00" }
|
@@ -2,7 +2,7 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
4
|
RSpec.describe 'Outgoing frame draft 04' do
|
5
|
-
let(:version) {
|
5
|
+
let(:version) { 4 }
|
6
6
|
let(:frame) { WebSocket::Frame::Outgoing.new(version: version, data: decoded_text, type: frame_type) }
|
7
7
|
let(:decoded_text) { '' }
|
8
8
|
let(:encoded_text) { "\x84\x00" }
|
@@ -17,4 +17,45 @@ RSpec.describe 'Client draft 4 handshake' do
|
|
17
17
|
expect(handshake).not_to be_valid
|
18
18
|
expect(handshake.error).to eql(:invalid_handshake_authentication)
|
19
19
|
end
|
20
|
+
|
21
|
+
context 'protocol header specified' do
|
22
|
+
let(:handshake) { WebSocket::Handshake::Client.new(uri: 'ws://example.com/demo', origin: 'http://example.com', version: version, protocols: protocols) }
|
23
|
+
|
24
|
+
context 'single protocol requested' do
|
25
|
+
let(:protocols) { %w(binary) }
|
26
|
+
|
27
|
+
it 'returns a valid handshake' do
|
28
|
+
@request_params = { headers: { 'Sec-WebSocket-Protocol' => 'binary' } }
|
29
|
+
handshake << server_response
|
30
|
+
|
31
|
+
expect(handshake).to be_finished
|
32
|
+
expect(handshake).to be_valid
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'multiple protocols requested' do
|
37
|
+
let(:protocols) { %w(binary xmpp) }
|
38
|
+
|
39
|
+
it 'returns with a valid handshake' do
|
40
|
+
@request_params = { headers: { 'Sec-WebSocket-Protocol' => 'xmpp' } }
|
41
|
+
handshake << server_response
|
42
|
+
|
43
|
+
expect(handshake).to be_finished
|
44
|
+
expect(handshake).to be_valid
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'unsupported protocol requested' do
|
49
|
+
let(:protocols) { %w(binary xmpp) }
|
50
|
+
|
51
|
+
it 'fails with an unsupported protocol error' do
|
52
|
+
@request_params = { headers: { 'Sec-WebSocket-Protocol' => 'generic' } }
|
53
|
+
handshake << server_response
|
54
|
+
|
55
|
+
expect(handshake).to be_finished
|
56
|
+
expect(handshake).not_to be_valid
|
57
|
+
expect(handshake.error).to eql(:unsupported_protocol)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
20
61
|
end
|
@@ -8,4 +8,29 @@ RSpec.describe 'Client draft 75 handshake' do
|
|
8
8
|
let(:server_response) { server_handshake_75(@request_params || {}) }
|
9
9
|
|
10
10
|
it_should_behave_like 'all client drafts'
|
11
|
+
|
12
|
+
context 'protocol header specified' do
|
13
|
+
let(:handshake) { WebSocket::Handshake::Client.new(uri: 'ws://example.com/demo', origin: 'http://example.com', version: version, protocols: %w(binary)) }
|
14
|
+
|
15
|
+
context 'supported' do
|
16
|
+
it 'returns a valid handshake' do
|
17
|
+
@request_params = { headers: { 'WebSocket-Protocol' => 'binary' } }
|
18
|
+
handshake << server_response
|
19
|
+
|
20
|
+
expect(handshake).to be_finished
|
21
|
+
expect(handshake).to be_valid
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'unsupported' do
|
26
|
+
it 'fails with an unsupported protocol error' do
|
27
|
+
@request_params = { headers: { 'WebSocket-Protocol' => 'xmpp' } }
|
28
|
+
handshake << server_response
|
29
|
+
|
30
|
+
expect(handshake).to be_finished
|
31
|
+
expect(handshake).not_to be_valid
|
32
|
+
expect(handshake.error).to eql(:unsupported_protocol)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
11
36
|
end
|
@@ -17,4 +17,29 @@ RSpec.describe 'Client draft 76 handshake' do
|
|
17
17
|
expect(handshake).not_to be_valid
|
18
18
|
expect(handshake.error).to eql(:invalid_handshake_authentication)
|
19
19
|
end
|
20
|
+
|
21
|
+
context 'protocol header specified' do
|
22
|
+
let(:handshake) { WebSocket::Handshake::Client.new(uri: 'ws://example.com/demo', origin: 'http://example.com', version: version, protocols: %w(binary)) }
|
23
|
+
|
24
|
+
context 'supported' do
|
25
|
+
it 'returns a valid handshake' do
|
26
|
+
@request_params = { headers: { 'Sec-WebSocket-Protocol' => 'binary' } }
|
27
|
+
handshake << server_response
|
28
|
+
|
29
|
+
expect(handshake).to be_finished
|
30
|
+
expect(handshake).to be_valid
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'unsupported' do
|
35
|
+
it 'fails with an unsupported protocol error' do
|
36
|
+
@request_params = { headers: { 'Sec-WebSocket-Protocol' => 'xmpp' } }
|
37
|
+
handshake << server_response
|
38
|
+
|
39
|
+
expect(handshake).to be_finished
|
40
|
+
expect(handshake).not_to be_valid
|
41
|
+
expect(handshake.error).to eql(:unsupported_protocol)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
20
45
|
end
|
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
RSpec.describe 'Server draft 04 handshake' do
|
4
4
|
let(:handshake) { WebSocket::Handshake::Server.new }
|
5
|
-
let(:version) {
|
5
|
+
let(:version) { 4 }
|
6
6
|
let(:client_request) { client_handshake_04(@request_params || {}) }
|
7
7
|
let(:server_response) { server_handshake_04(@request_params || {}) }
|
8
8
|
|
@@ -15,4 +15,35 @@ RSpec.describe 'Server draft 04 handshake' do
|
|
15
15
|
expect(handshake).not_to be_valid
|
16
16
|
expect(handshake.error).to eql(:invalid_handshake_authentication)
|
17
17
|
end
|
18
|
+
|
19
|
+
context 'protocol header specified' do
|
20
|
+
let(:handshake) { WebSocket::Handshake::Server.new(protocols: %w(binary xmpp)) }
|
21
|
+
|
22
|
+
context 'single protocol requested' do
|
23
|
+
it 'returns with the same protocol' do
|
24
|
+
@request_params = { headers: { 'Sec-WebSocket-Protocol' => 'binary' } }
|
25
|
+
handshake << client_request
|
26
|
+
|
27
|
+
expect(handshake.to_s).to match('Sec-WebSocket-Protocol: binary')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'multiple protocols requested' do
|
32
|
+
it 'returns with the first supported protocol' do
|
33
|
+
@request_params = { headers: { 'Sec-WebSocket-Protocol' => 'xmpp, binary' } }
|
34
|
+
handshake << client_request
|
35
|
+
|
36
|
+
expect(handshake.to_s).to match('Sec-WebSocket-Protocol: xmpp')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'unsupported protocol requested' do
|
41
|
+
it 'reutrns with an empty protocol header' do
|
42
|
+
@request_params = { headers: { 'Sec-WebSocket-Protocol' => 'generic' } }
|
43
|
+
handshake << client_request
|
44
|
+
|
45
|
+
expect(handshake.to_s).to match("Sec-WebSocket-Protocol: \r\n")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
18
49
|
end
|
@@ -8,4 +8,26 @@ RSpec.describe 'Server draft 75 handshake' do
|
|
8
8
|
let(:server_response) { server_handshake_75(@request_params || {}) }
|
9
9
|
|
10
10
|
it_should_behave_like 'all server drafts'
|
11
|
+
|
12
|
+
context 'protocol header specified' do
|
13
|
+
let(:handshake) { WebSocket::Handshake::Server.new(protocols: %w(binary)) }
|
14
|
+
|
15
|
+
context 'supported' do
|
16
|
+
it 'returns with the same protocol' do
|
17
|
+
@request_params = { headers: { 'WebSocket-Protocol' => 'binary' } }
|
18
|
+
handshake << client_request
|
19
|
+
|
20
|
+
expect(handshake.to_s).to match('WebSocket-Protocol: binary')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'unsupported' do
|
25
|
+
it 'returns with an empty protocol header' do
|
26
|
+
@request_params = { headers: { 'WebSocket-Protocol' => 'xmpp' } }
|
27
|
+
handshake << client_request
|
28
|
+
|
29
|
+
expect(handshake.to_s).to match("WebSocket-Protocol: \r\n")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
11
33
|
end
|
@@ -43,4 +43,26 @@ RSpec.describe 'Server draft 76 handshake' do
|
|
43
43
|
expect(handshake).not_to be_valid
|
44
44
|
expect(handshake.error).to eql(:invalid_handshake_authentication)
|
45
45
|
end
|
46
|
+
|
47
|
+
context 'protocol header specified' do
|
48
|
+
let(:handshake) { WebSocket::Handshake::Server.new(protocols: %w(binary)) }
|
49
|
+
|
50
|
+
context 'supported' do
|
51
|
+
it 'returns with the same protocol' do
|
52
|
+
@request_params = { headers: { 'Sec-WebSocket-Protocol' => 'binary' } }
|
53
|
+
handshake << client_request
|
54
|
+
|
55
|
+
expect(handshake.to_s).to match('Sec-WebSocket-Protocol: binary')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'unsupported' do
|
60
|
+
it 'returns with an empty protocol header' do
|
61
|
+
@request_params = { headers: { 'Sec-WebSocket-Protocol' => 'xmpp' } }
|
62
|
+
handshake << client_request
|
63
|
+
|
64
|
+
expect(handshake.to_s).to match("Sec-WebSocket-Protocol: \r\n")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
46
68
|
end
|
@@ -14,7 +14,7 @@ def server_handshake_75(args = {})
|
|
14
14
|
HTTP/1.1 101 Web Socket Protocol Handshake\r
|
15
15
|
Upgrade: WebSocket\r
|
16
16
|
Connection: Upgrade\r
|
17
|
-
WebSocket-Origin: http://example.com\r
|
17
|
+
#{(args[:headers] || {}).map { |key, value| "#{key}: #{value}\r\n" }.join('')}WebSocket-Origin: http://example.com\r
|
18
18
|
WebSocket-Location: ws#{args[:secure] ? 's' : ''}://#{args[:host] || 'example.com'}#{":#{args[:port]}" if args[:port]}#{args[:path] || '/demo'}\r
|
19
19
|
\r
|
20
20
|
EOF
|
@@ -40,7 +40,7 @@ def server_handshake_76(args = {})
|
|
40
40
|
HTTP/1.1 101 WebSocket Protocol Handshake\r
|
41
41
|
Upgrade: WebSocket\r
|
42
42
|
Connection: Upgrade\r
|
43
|
-
Sec-WebSocket-Origin: http://example.com\r
|
43
|
+
#{(args[:headers] || {}).map { |key, value| "#{key}: #{value}\r\n" }.join('')}Sec-WebSocket-Origin: http://example.com\r
|
44
44
|
Sec-WebSocket-Location: ws#{args[:secure] ? 's' : ''}://#{args[:host] || 'example.com'}#{":#{args[:port]}" if args[:port]}#{args[:path] || '/demo'}\r
|
45
45
|
\r
|
46
46
|
#{args[:challenge] || "8jKS'y:G*Co,Wxa-"}
|
@@ -66,7 +66,7 @@ def server_handshake_04(args = {})
|
|
66
66
|
HTTP/1.1 101 Switching Protocols\r
|
67
67
|
Upgrade: websocket\r
|
68
68
|
Connection: Upgrade\r
|
69
|
-
Sec-WebSocket-Accept: #{args[:accept] || 's3pPLMBiTxaQ9kYGzzhZRbK+xOo='}\r
|
69
|
+
#{(args[:headers] || {}).map { |key, value| "#{key}: #{value}\r\n" }.join('')}Sec-WebSocket-Accept: #{args[:accept] || 's3pPLMBiTxaQ9kYGzzhZRbK+xOo='}\r
|
70
70
|
\r
|
71
71
|
EOF
|
72
72
|
end
|
@@ -38,19 +38,21 @@ RSpec.shared_examples_for 'valid_incoming_frame' do
|
|
38
38
|
after(:each) { WebSocket.should_raise = false }
|
39
39
|
|
40
40
|
it 'should have specified number of returned frames' do
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
41
|
+
if error
|
42
|
+
expect do
|
43
|
+
decoded_text_array.each_with_index do |da, index|
|
44
|
+
n = subject.next
|
45
|
+
expect(n).not_to be_nil, "Should return frame for #{da}, #{frame_type_array[index]}"
|
46
|
+
expect(n.class).to eql(WebSocket::Frame::Incoming), "Should be WebSocket::Frame::Incoming, #{n} received instead"
|
47
|
+
end
|
48
|
+
expect(subject.next).to be_nil
|
49
|
+
if error.is_a?(Class)
|
50
|
+
expect(subject.error).to eql(error.new.message)
|
51
|
+
else
|
52
|
+
expect(subject.error).to eql(error)
|
53
|
+
end
|
54
|
+
end.to raise_error(error)
|
55
|
+
end
|
54
56
|
end
|
55
57
|
end
|
56
58
|
end
|
data/websocket.gemspec
CHANGED
@@ -17,4 +17,9 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
19
19
|
s.require_paths = ['lib']
|
20
|
+
|
21
|
+
s.add_development_dependency 'rake'
|
22
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
23
|
+
s.add_development_dependency 'rspec-its'
|
24
|
+
s.add_development_dependency 'rubocop'
|
20
25
|
end
|
metadata
CHANGED
@@ -1,15 +1,71 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: websocket
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bernard Potocki
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
11
|
+
date: 2017-01-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec-its
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
13
69
|
description: Universal Ruby library to handle WebSocket protocol
|
14
70
|
email:
|
15
71
|
- bernard.potocki@imanel.org
|
@@ -109,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
165
|
version: '0'
|
110
166
|
requirements: []
|
111
167
|
rubyforge_project:
|
112
|
-
rubygems_version: 2.5.
|
168
|
+
rubygems_version: 2.5.2
|
113
169
|
signing_key:
|
114
170
|
specification_version: 4
|
115
171
|
summary: Universal Ruby library to handle WebSocket protocol
|