websocket 1.2.3 → 1.2.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/websocket.svg)](http://badge.fury.io/rb/websocket)
|
6
|
+
[![Gem Downloads](https://img.shields.io/gem/dt/websocket.svg?maxAge=2592000)](https://rubygems.org/gems/websocket)
|
6
7
|
[![Travis CI](https://travis-ci.org/imanel/websocket-ruby.png)](http://travis-ci.org/imanel/websocket-ruby)
|
7
8
|
[![Code Climate](https://codeclimate.com/github/imanel/websocket-ruby.png)](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
|