websocket 1.2.2 → 1.2.3
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 +37 -0
- data/.travis.yml +2 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +1 -1
- data/Rakefile +3 -3
- data/lib/websocket.rb +2 -3
- data/lib/websocket/error.rb +57 -25
- data/lib/websocket/exception_handler.rb +2 -14
- data/lib/websocket/frame.rb +0 -2
- data/lib/websocket/frame/base.rb +12 -13
- data/lib/websocket/frame/data.rb +1 -3
- data/lib/websocket/frame/handler.rb +0 -2
- data/lib/websocket/frame/handler/base.rb +2 -4
- data/lib/websocket/frame/handler/handler03.rb +149 -112
- data/lib/websocket/frame/handler/handler04.rb +3 -3
- data/lib/websocket/frame/handler/handler05.rb +3 -3
- data/lib/websocket/frame/handler/handler07.rb +8 -10
- data/lib/websocket/frame/handler/handler75.rb +10 -12
- data/lib/websocket/frame/incoming.rb +0 -2
- data/lib/websocket/frame/incoming/client.rb +0 -2
- data/lib/websocket/frame/incoming/server.rb +0 -2
- data/lib/websocket/frame/outgoing.rb +1 -3
- data/lib/websocket/frame/outgoing/client.rb +0 -2
- data/lib/websocket/frame/outgoing/server.rb +0 -2
- data/lib/websocket/handshake.rb +0 -2
- data/lib/websocket/handshake/base.rb +10 -9
- data/lib/websocket/handshake/client.rb +22 -35
- data/lib/websocket/handshake/handler.rb +0 -2
- data/lib/websocket/handshake/handler/base.rb +0 -2
- data/lib/websocket/handshake/handler/client.rb +0 -2
- data/lib/websocket/handshake/handler/client01.rb +0 -2
- data/lib/websocket/handshake/handler/client04.rb +3 -5
- data/lib/websocket/handshake/handler/client11.rb +0 -2
- data/lib/websocket/handshake/handler/client75.rb +2 -4
- data/lib/websocket/handshake/handler/client76.rb +6 -8
- data/lib/websocket/handshake/handler/server.rb +0 -1
- data/lib/websocket/handshake/handler/server04.rb +3 -5
- data/lib/websocket/handshake/handler/server75.rb +2 -4
- data/lib/websocket/handshake/handler/server76.rb +7 -9
- data/lib/websocket/handshake/server.rb +19 -24
- data/lib/websocket/version.rb +1 -1
- data/spec/frame/incoming_03_spec.rb +1 -2
- data/spec/frame/incoming_04_spec.rb +1 -2
- data/spec/frame/incoming_05_spec.rb +1 -2
- data/spec/frame/incoming_07_spec.rb +1 -2
- data/spec/frame/incoming_75_spec.rb +1 -2
- data/spec/support/all_client_drafts.rb +1 -1
- data/spec/support/all_server_drafts.rb +7 -10
- data/spec/support/frames_base.rb +0 -2
- data/spec/support/handshake_requests.rb +4 -4
- data/spec/support/outgoing_frames.rb +0 -1
- data/spec/support/overwrites.rb +0 -2
- data/websocket.gemspec +10 -10
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48ee0f12aaa5fd6aba76f7c2070a09f600c53204
|
4
|
+
data.tar.gz: d011719667dc6227eecf743a764d0f24f7bf17b2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 568b9cd4986500c8c9ddf5ec2d8f90b81b654fd6b10451480209ca505be73e0d8a24b11b10040aa7bd3211c84bf6c25154482655985a117705657e75eb302e0f
|
7
|
+
data.tar.gz: 3c3c8d8c324b480654891f7326f1cf7153dbf3f7b4838c1b3ad2bbe670ba25c57dc2485092b049f5d5fd35b1f131a63882a46b989f684fc40a3925fc3d2024bb
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# Target: 15
|
2
|
+
Metrics/AbcSize:
|
3
|
+
Max: 24
|
4
|
+
Exclude:
|
5
|
+
- lib/websocket/frame/handler/handler75.rb
|
6
|
+
- spec/**/*
|
7
|
+
|
8
|
+
Metrics/ClassLength:
|
9
|
+
Enabled: false
|
10
|
+
|
11
|
+
# Target: 6
|
12
|
+
Metrics/CyclomaticComplexity:
|
13
|
+
Max: 11
|
14
|
+
Exclude:
|
15
|
+
- spec/support/handshake_requests.rb
|
16
|
+
|
17
|
+
Metrics/LineLength:
|
18
|
+
Enabled: false
|
19
|
+
|
20
|
+
# Target: 10
|
21
|
+
Metrics/MethodLength:
|
22
|
+
Max: 18
|
23
|
+
Exclude:
|
24
|
+
- lib/websocket/frame/handler/handler75.rb
|
25
|
+
|
26
|
+
# Target: 7
|
27
|
+
Metrics/PerceivedComplexity:
|
28
|
+
Max: 8
|
29
|
+
Exclude:
|
30
|
+
- lib/websocket/frame/handler/handler75.rb
|
31
|
+
- spec/support/handshake_requests.rb
|
32
|
+
|
33
|
+
Style/Documentation:
|
34
|
+
Enabled: false
|
35
|
+
|
36
|
+
Style/TrivialAccessors:
|
37
|
+
AllowPredicates: true
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -4,19 +4,19 @@ Bundler::GemHelper.install_tasks
|
|
4
4
|
require 'rspec/core/rake_task'
|
5
5
|
|
6
6
|
RSpec::Core::RakeTask.new do |t|
|
7
|
-
t.rspec_opts = [
|
7
|
+
t.rspec_opts = ['-c', '-f progress']
|
8
8
|
t.pattern = 'spec/**/*_spec.rb'
|
9
9
|
end
|
10
10
|
|
11
11
|
task default: :spec
|
12
12
|
|
13
13
|
namespace :autobahn do
|
14
|
-
desc
|
14
|
+
desc 'Run autobahn tests for client'
|
15
15
|
task :client do
|
16
16
|
system('wstest --mode=fuzzingserver --spec=autobahn-client.json')
|
17
17
|
end
|
18
18
|
|
19
|
-
desc
|
19
|
+
desc 'Run autobahn tests for server'
|
20
20
|
task :server do
|
21
21
|
system('wstest --mode=fuzzingclient --spec=autobahn-server.json')
|
22
22
|
end
|
data/lib/websocket.rb
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
# @author Bernard "Imanel" Potocki
|
5
5
|
# @see http://github.com/imanel/websocket-ruby main repository
|
6
6
|
module WebSocket
|
7
|
-
|
8
7
|
# Default WebSocket version to use
|
9
8
|
DEFAULT_VERSION = 13
|
10
9
|
ROOT = File.expand_path(File.dirname(__FILE__))
|
@@ -34,11 +33,11 @@ module WebSocket
|
|
34
33
|
def self.should_raise=(val)
|
35
34
|
@should_raise = val
|
36
35
|
end
|
37
|
-
|
38
36
|
end
|
39
37
|
|
40
38
|
# Try loading websocket-native if available
|
41
39
|
begin
|
42
40
|
require 'websocket-native'
|
43
|
-
rescue LoadError
|
41
|
+
rescue LoadError => e
|
42
|
+
raise unless e.message =~ /websocket-native/
|
44
43
|
end
|
data/lib/websocket/error.rb
CHANGED
@@ -1,89 +1,121 @@
|
|
1
1
|
module WebSocket
|
2
2
|
class Error < RuntimeError
|
3
|
-
|
4
3
|
class Frame < ::WebSocket::Error
|
5
|
-
|
6
4
|
class ControlFramePayloadTooLong < ::WebSocket::Error::Frame
|
7
|
-
def message
|
5
|
+
def message
|
6
|
+
:control_frame_payload_too_long
|
7
|
+
end
|
8
8
|
end
|
9
9
|
|
10
10
|
class DataFrameInsteadContinuation < ::WebSocket::Error::Frame
|
11
|
-
def message
|
11
|
+
def message
|
12
|
+
:data_frame_instead_continuation
|
13
|
+
end
|
12
14
|
end
|
13
15
|
|
14
16
|
class FragmentedControlFrame < ::WebSocket::Error::Frame
|
15
|
-
def message
|
17
|
+
def message
|
18
|
+
:fragmented_control_frame
|
19
|
+
end
|
16
20
|
end
|
17
21
|
|
18
22
|
class Invalid < ::WebSocket::Error::Frame
|
19
|
-
def message
|
23
|
+
def message
|
24
|
+
:invalid_frame
|
25
|
+
end
|
20
26
|
end
|
21
27
|
|
22
28
|
class InvalidPayloadEncoding < ::WebSocket::Error::Frame
|
23
|
-
def message
|
29
|
+
def message
|
30
|
+
:invalid_payload_encoding
|
31
|
+
end
|
24
32
|
end
|
25
33
|
|
26
34
|
class MaskTooShort < ::WebSocket::Error::Frame
|
27
|
-
def message
|
35
|
+
def message
|
36
|
+
:mask_is_too_short
|
37
|
+
end
|
28
38
|
end
|
29
39
|
|
30
40
|
class ReservedBitUsed < ::WebSocket::Error::Frame
|
31
|
-
def message
|
41
|
+
def message
|
42
|
+
:reserved_bit_used
|
43
|
+
end
|
32
44
|
end
|
33
45
|
|
34
46
|
class TooLong < ::WebSocket::Error::Frame
|
35
|
-
def message
|
47
|
+
def message
|
48
|
+
:frame_too_long
|
49
|
+
end
|
36
50
|
end
|
37
51
|
|
38
52
|
class UnexpectedContinuationFrame < ::WebSocket::Error::Frame
|
39
|
-
def message
|
53
|
+
def message
|
54
|
+
:unexpected_continuation_frame
|
55
|
+
end
|
40
56
|
end
|
41
57
|
|
42
58
|
class UnknownFrameType < ::WebSocket::Error::Frame
|
43
|
-
def message
|
59
|
+
def message
|
60
|
+
:unknown_frame_type
|
61
|
+
end
|
44
62
|
end
|
45
63
|
|
46
64
|
class UnknownOpcode < ::WebSocket::Error::Frame
|
47
|
-
def message
|
65
|
+
def message
|
66
|
+
:unknown_opcode
|
67
|
+
end
|
48
68
|
end
|
49
69
|
|
50
70
|
class UnknownCloseCode < ::WebSocket::Error::Frame
|
51
|
-
def message
|
71
|
+
def message
|
72
|
+
:unknown_close_code
|
73
|
+
end
|
52
74
|
end
|
53
75
|
|
54
76
|
class UnknownVersion < ::WebSocket::Error::Frame
|
55
|
-
def message
|
77
|
+
def message
|
78
|
+
:unknown_protocol_version
|
79
|
+
end
|
56
80
|
end
|
57
|
-
|
58
81
|
end
|
59
82
|
|
60
83
|
class Handshake < ::WebSocket::Error
|
61
|
-
|
62
84
|
class GetRequestRequired < ::WebSocket::Error::Handshake
|
63
|
-
def message
|
85
|
+
def message
|
86
|
+
:get_request_required
|
87
|
+
end
|
64
88
|
end
|
65
89
|
|
66
90
|
class InvalidAuthentication < ::WebSocket::Error::Handshake
|
67
|
-
def message
|
91
|
+
def message
|
92
|
+
:invalid_handshake_authentication
|
93
|
+
end
|
68
94
|
end
|
69
95
|
|
70
96
|
class InvalidHeader < ::WebSocket::Error::Handshake
|
71
|
-
def message
|
97
|
+
def message
|
98
|
+
:invalid_header
|
99
|
+
end
|
72
100
|
end
|
73
101
|
|
74
102
|
class InvalidStatusCode < ::WebSocket::Error::Handshake
|
75
|
-
def message
|
103
|
+
def message
|
104
|
+
:invalid_status_code
|
105
|
+
end
|
76
106
|
end
|
77
107
|
|
78
108
|
class NoHostProvided < ::WebSocket::Error::Handshake
|
79
|
-
def message
|
109
|
+
def message
|
110
|
+
:no_host_provided
|
111
|
+
end
|
80
112
|
end
|
81
113
|
|
82
114
|
class UnknownVersion < ::WebSocket::Error::Handshake
|
83
|
-
def message
|
115
|
+
def message
|
116
|
+
:unknown_protocol_version
|
117
|
+
end
|
84
118
|
end
|
85
|
-
|
86
119
|
end
|
87
|
-
|
88
120
|
end
|
89
121
|
end
|
@@ -1,22 +1,12 @@
|
|
1
1
|
module WebSocket
|
2
2
|
module ExceptionHandler
|
3
|
-
|
4
|
-
attr_reader :error
|
3
|
+
attr_accessor :error
|
5
4
|
|
6
5
|
def self.included(base)
|
7
6
|
base.extend(ClassMethods)
|
8
7
|
end
|
9
8
|
|
10
|
-
private
|
11
|
-
|
12
|
-
# Changes state to error and sets error message
|
13
|
-
# @param [String] message Error message to set
|
14
|
-
def set_error(message)
|
15
|
-
@error = message
|
16
|
-
end
|
17
|
-
|
18
9
|
module ClassMethods
|
19
|
-
|
20
10
|
# Rescue from WebSocket::Error errors.
|
21
11
|
#
|
22
12
|
# @param [String] method_name Name of method that should be wrapped and rescued
|
@@ -28,15 +18,13 @@ module WebSocket
|
|
28
18
|
begin
|
29
19
|
send("#{method_name}_without_rescue", *args)
|
30
20
|
rescue WebSocket::Error => e
|
31
|
-
|
21
|
+
self.error = e.message.to_sym
|
32
22
|
WebSocket.should_raise ? raise : options[:return]
|
33
23
|
end
|
34
24
|
end
|
35
25
|
alias_method "#{method_name}_without_rescue", method_name
|
36
26
|
alias_method method_name, "#{method_name}_with_rescue"
|
37
27
|
end
|
38
|
-
|
39
28
|
end
|
40
|
-
|
41
29
|
end
|
42
30
|
end
|
data/lib/websocket/frame.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
module WebSocket
|
2
2
|
module Frame
|
3
|
-
|
4
3
|
autoload :Base, "#{::WebSocket::ROOT}/websocket/frame/base"
|
5
4
|
autoload :Data, "#{::WebSocket::ROOT}/websocket/frame/data"
|
6
5
|
autoload :Handler, "#{::WebSocket::ROOT}/websocket/frame/handler"
|
7
6
|
autoload :Incoming, "#{::WebSocket::ROOT}/websocket/frame/incoming"
|
8
7
|
autoload :Outgoing, "#{::WebSocket::ROOT}/websocket/frame/outgoing"
|
9
|
-
|
10
8
|
end
|
11
9
|
end
|
data/lib/websocket/frame/base.rb
CHANGED
@@ -26,7 +26,7 @@ module WebSocket
|
|
26
26
|
# Check if some errors occured
|
27
27
|
# @return [Boolean] True if error is set
|
28
28
|
def error?
|
29
|
-
|
29
|
+
!@error.nil?
|
30
30
|
end
|
31
31
|
|
32
32
|
# Is selected type supported for selected handler?
|
@@ -36,13 +36,13 @@ module WebSocket
|
|
36
36
|
|
37
37
|
# Implement in submodules
|
38
38
|
def supported_frames
|
39
|
-
|
39
|
+
fail NotImplementedError
|
40
40
|
end
|
41
41
|
|
42
42
|
# Recreate inspect as #to_s was overwritten
|
43
43
|
def inspect
|
44
|
-
vars =
|
45
|
-
insp = "#{self.class}:0x%08x"
|
44
|
+
vars = instance_variables.map { |v| "#{v}=#{instance_variable_get(v).inspect}" }.join(', ')
|
45
|
+
insp = Kernel.format("#{self.class}:0x%08x", __id__)
|
46
46
|
"<#{insp} #{vars}>"
|
47
47
|
end
|
48
48
|
|
@@ -52,16 +52,15 @@ module WebSocket
|
|
52
52
|
# @return [Boolean] false if protocol number is unknown, otherwise true
|
53
53
|
def include_version
|
54
54
|
@handler = case @version
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
55
|
+
when 75..76 then Handler::Handler75.new(self)
|
56
|
+
when 0..2 then Handler::Handler75.new(self)
|
57
|
+
when 3 then Handler::Handler03.new(self)
|
58
|
+
when 4 then Handler::Handler04.new(self)
|
59
|
+
when 5..6 then Handler::Handler05.new(self)
|
60
|
+
when 7..13 then Handler::Handler07.new(self)
|
61
|
+
else fail WebSocket::Error::Frame::UnknownVersion
|
62
|
+
end
|
63
63
|
end
|
64
|
-
|
65
64
|
end
|
66
65
|
end
|
67
66
|
end
|
data/lib/websocket/frame/data.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
module WebSocket
|
2
2
|
module Frame
|
3
3
|
class Data < String
|
4
|
-
|
5
4
|
def initialize(*args)
|
6
5
|
super(*convert_args(args))
|
7
6
|
end
|
@@ -17,7 +16,7 @@ module WebSocket
|
|
17
16
|
|
18
17
|
# Extract mask from 4 first bytes according to spec
|
19
18
|
def set_mask
|
20
|
-
|
19
|
+
fail WebSocket::Error::Frame::MaskTooShort if bytesize < 4
|
21
20
|
@masking_key = self[0..3].bytes.to_a
|
22
21
|
end
|
23
22
|
|
@@ -42,7 +41,6 @@ module WebSocket
|
|
42
41
|
end
|
43
42
|
result
|
44
43
|
end
|
45
|
-
|
46
44
|
end
|
47
45
|
end
|
48
46
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module WebSocket
|
2
2
|
module Frame
|
3
3
|
module Handler
|
4
|
-
|
5
4
|
autoload :Base, "#{::WebSocket::ROOT}/websocket/frame/handler/base"
|
6
5
|
|
7
6
|
autoload :Handler03, "#{::WebSocket::ROOT}/websocket/frame/handler/handler03"
|
@@ -9,7 +8,6 @@ module WebSocket
|
|
9
8
|
autoload :Handler05, "#{::WebSocket::ROOT}/websocket/frame/handler/handler05"
|
10
9
|
autoload :Handler07, "#{::WebSocket::ROOT}/websocket/frame/handler/handler07"
|
11
10
|
autoload :Handler75, "#{::WebSocket::ROOT}/websocket/frame/handler/handler75"
|
12
|
-
|
13
11
|
end
|
14
12
|
end
|
15
13
|
end
|
@@ -2,7 +2,6 @@ module WebSocket
|
|
2
2
|
module Frame
|
3
3
|
module Handler
|
4
4
|
class Base
|
5
|
-
|
6
5
|
def initialize(frame)
|
7
6
|
@frame = frame
|
8
7
|
end
|
@@ -10,13 +9,13 @@ module WebSocket
|
|
10
9
|
# Convert data to raw frame ready to send to client
|
11
10
|
# @return [String] Encoded frame
|
12
11
|
def encode_frame
|
13
|
-
|
12
|
+
fail NotImplementedError
|
14
13
|
end
|
15
14
|
|
16
15
|
# Convert raw data to decoded frame
|
17
16
|
# @return [WebSocket::Frame::Incoming] Frame if found, nil otherwise
|
18
17
|
def decode_frame
|
19
|
-
|
18
|
+
fail NotImplementedError
|
20
19
|
end
|
21
20
|
|
22
21
|
private
|
@@ -34,7 +33,6 @@ module WebSocket
|
|
34
33
|
def data_frame?(frame_type)
|
35
34
|
[:text, :binary].include?(frame_type)
|
36
35
|
end
|
37
|
-
|
38
36
|
end
|
39
37
|
end
|
40
38
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# encoding: binary
|
2
|
+
require 'securerandom'
|
2
3
|
|
3
4
|
module WebSocket
|
4
5
|
module Frame
|
5
6
|
module Handler
|
6
7
|
class Handler03 < Base
|
7
|
-
|
8
8
|
# Hash of frame names and it's opcodes
|
9
9
|
FRAME_TYPES = {
|
10
10
|
continuation: 0,
|
@@ -25,155 +25,192 @@ module WebSocket
|
|
25
25
|
|
26
26
|
# @see WebSocket::Frame::Handler::Base#encode_frame
|
27
27
|
def encode_frame
|
28
|
-
frame =
|
28
|
+
frame = if @frame.outgoing_masking?
|
29
|
+
masking_key = SecureRandom.random_bytes(4)
|
30
|
+
tmp_data = Data.new(masking_key + @frame.data)
|
31
|
+
tmp_data.set_mask
|
32
|
+
masking_key + tmp_data.getbytes(4, tmp_data.size)
|
33
|
+
else
|
34
|
+
@frame.data
|
35
|
+
end
|
36
|
+
|
37
|
+
encode_header + frame
|
38
|
+
end
|
39
|
+
|
40
|
+
# @see WebSocket::Frame::Handler::Base#decode_frame
|
41
|
+
def decode_frame
|
42
|
+
while @frame.data.size > 1
|
43
|
+
valid_header, more, frame_type, mask, payload_length = decode_header
|
44
|
+
return unless valid_header
|
45
|
+
|
46
|
+
application_data = decode_payload(payload_length, mask)
|
47
|
+
|
48
|
+
if more
|
49
|
+
decode_continuation_frame(application_data, frame_type)
|
50
|
+
elsif frame_type == :continuation
|
51
|
+
return decode_finish_continuation_frame(application_data)
|
52
|
+
else
|
53
|
+
fail(WebSocket::Error::Frame::InvalidPayloadEncoding) if frame_type == :text && !application_data.valid_encoding?
|
54
|
+
return @frame.class.new(version: @frame.version, type: frame_type, data: application_data, decoded: true)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
|
60
|
+
# Allow turning on or off masking
|
61
|
+
def masking?
|
62
|
+
false
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
29
66
|
|
30
|
-
|
31
|
-
|
32
|
-
|
67
|
+
# This allows flipping the more bit to fin for draft 04
|
68
|
+
def fin
|
69
|
+
false
|
70
|
+
end
|
71
|
+
|
72
|
+
# Convert frame type name to opcode
|
73
|
+
# @param [Symbol] frame_type Frame type name
|
74
|
+
# @return [Integer] opcode or nil
|
75
|
+
# @raise [WebSocket::Error] if frame opcode is not known
|
76
|
+
def type_to_opcode(frame_type)
|
77
|
+
FRAME_TYPES[frame_type] || fail(WebSocket::Error::Frame::UnknownFrameType)
|
78
|
+
end
|
33
79
|
|
80
|
+
# Convert frame opcode to type name
|
81
|
+
# @param [Integer] opcode Opcode
|
82
|
+
# @return [Symbol] Frame type name or nil
|
83
|
+
# @raise [WebSocket::Error] if frame type name is not known
|
84
|
+
def opcode_to_type(opcode)
|
85
|
+
FRAME_TYPES_INVERSE[opcode] || fail(WebSocket::Error::Frame::UnknownOpcode)
|
86
|
+
end
|
87
|
+
|
88
|
+
def encode_header
|
34
89
|
mask = @frame.outgoing_masking? ? 0b10000000 : 0b00000000
|
35
90
|
|
36
|
-
|
91
|
+
output = ''
|
92
|
+
output << (type_to_opcode(@frame.type) | (fin ? 0b10000000 : 0b00000000)) # since more, rsv1-3 are 0 and 0x80 for Draft 4
|
93
|
+
output << encode_payload_length(@frame.data.size, mask)
|
94
|
+
output
|
95
|
+
end
|
96
|
+
|
97
|
+
def encode_payload_length(length, mask)
|
98
|
+
output = ''
|
37
99
|
if length <= 125
|
38
|
-
|
39
|
-
frame << (byte2 | mask)
|
100
|
+
output << (length | mask) # since rsv4 is 0
|
40
101
|
elsif length < 65_536 # write 2 byte length
|
41
|
-
|
42
|
-
|
102
|
+
output << (126 | mask)
|
103
|
+
output << [length].pack('n')
|
43
104
|
else # write 8 byte length
|
44
|
-
|
45
|
-
|
105
|
+
output << (127 | mask)
|
106
|
+
output << [length >> 32, length & 0xFFFFFFFF].pack('NN')
|
46
107
|
end
|
47
|
-
|
48
|
-
if @frame.outgoing_masking?
|
49
|
-
masking_key = [rand(256).chr, rand(256).chr, rand(256).chr, rand(256).chr].join
|
50
|
-
tmp_data = Data.new([masking_key, @frame.data.to_s].join)
|
51
|
-
tmp_data.set_mask
|
52
|
-
frame << masking_key + tmp_data.getbytes(4, tmp_data.size)
|
53
|
-
else
|
54
|
-
frame << @frame.data
|
55
|
-
end
|
56
|
-
|
57
|
-
frame
|
108
|
+
output
|
58
109
|
end
|
59
110
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
111
|
+
def decode_header
|
112
|
+
more, frame_type = decode_first_byte
|
113
|
+
header_length, payload_length, mask = decode_second_byte(frame_type)
|
114
|
+
return unless header_length
|
64
115
|
|
65
|
-
|
116
|
+
# Compute the expected frame length
|
117
|
+
frame_length = header_length + payload_length
|
118
|
+
frame_length += 4 if mask
|
66
119
|
|
67
|
-
|
120
|
+
fail(WebSocket::Error::Frame::TooLong) if frame_length > WebSocket.max_frame_size
|
68
121
|
|
69
|
-
|
70
|
-
|
71
|
-
pointer += 1
|
122
|
+
# Check buffer size
|
123
|
+
return unless buffer_exists?(frame_length) # Buffer incomplete
|
72
124
|
|
73
|
-
|
74
|
-
|
125
|
+
# Remove frame header
|
126
|
+
@frame.data.slice!(0...header_length)
|
75
127
|
|
76
|
-
|
77
|
-
|
128
|
+
[true, more, frame_type, mask, payload_length]
|
129
|
+
end
|
78
130
|
|
79
|
-
|
131
|
+
def buffer_exists?(buffer_number)
|
132
|
+
!@frame.data.getbyte(buffer_number - 1).nil?
|
133
|
+
end
|
80
134
|
|
81
|
-
|
135
|
+
def decode_first_byte
|
136
|
+
first_byte = @frame.data.getbyte(0)
|
82
137
|
|
83
|
-
|
84
|
-
when 127 # Length defined by 8 bytes
|
85
|
-
# Check buffer size
|
86
|
-
return if @frame.data.getbyte(pointer + 8 - 1) == nil # Buffer incomplete
|
138
|
+
fail(WebSocket::Error::Frame::ReservedBitUsed) if first_byte & 0b01110000 != 0b00000000
|
87
139
|
|
88
|
-
|
89
|
-
|
90
|
-
l = @frame.data.getbytes(pointer + 4, 4).unpack('N').first
|
91
|
-
pointer += 8
|
92
|
-
l
|
93
|
-
when 126 # Length defined by 2 bytes
|
94
|
-
# Check buffer size
|
95
|
-
return if @frame.data.getbyte(pointer + 2 - 1) == nil # Buffer incomplete
|
140
|
+
more = ((first_byte & 0b10000000) == 0b10000000) ^ fin
|
141
|
+
frame_type = opcode_to_type first_byte & 0b00001111
|
96
142
|
|
97
|
-
|
98
|
-
|
99
|
-
l
|
100
|
-
else
|
101
|
-
length
|
102
|
-
end
|
143
|
+
fail(WebSocket::Error::Frame::FragmentedControlFrame) if more && control_frame?(frame_type)
|
144
|
+
fail(WebSocket::Error::Frame::DataFrameInsteadContinuation) if data_frame?(frame_type) && !@application_data_buffer.nil?
|
103
145
|
|
104
|
-
|
105
|
-
|
106
|
-
frame_length += 4 if mask
|
146
|
+
[more, frame_type]
|
147
|
+
end
|
107
148
|
|
108
|
-
|
149
|
+
def decode_second_byte(frame_type)
|
150
|
+
second_byte = @frame.data.getbyte(1)
|
109
151
|
|
110
|
-
|
111
|
-
|
152
|
+
mask = @frame.incoming_masking? && (second_byte & 0b10000000) == 0b10000000
|
153
|
+
length = second_byte & 0b01111111
|
112
154
|
|
113
|
-
|
114
|
-
@frame.data.slice!(0...pointer)
|
115
|
-
pointer = 0
|
155
|
+
fail(WebSocket::Error::Frame::ControlFramePayloadTooLong) if length > 125 && control_frame?(frame_type)
|
116
156
|
|
117
|
-
|
118
|
-
@frame.data.set_mask if mask
|
119
|
-
pointer += 4 if mask
|
120
|
-
application_data = @frame.data.getbytes(pointer, payload_length)
|
121
|
-
application_data.force_encoding('UTF-8')
|
122
|
-
pointer += payload_length
|
123
|
-
@frame.data.unset_mask if mask
|
157
|
+
header_length, payload_length = decode_payload_length(length)
|
124
158
|
|
125
|
-
|
126
|
-
|
159
|
+
[header_length, payload_length, mask]
|
160
|
+
end
|
127
161
|
|
128
|
-
|
162
|
+
def decode_payload_length(length)
|
163
|
+
case length
|
164
|
+
when 127 # Length defined by 8 bytes
|
165
|
+
# Check buffer size
|
166
|
+
return unless buffer_exists?(10) # Buffer incomplete
|
129
167
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
message = @frame.class.new(version: @frame.version, type: @frame_type, data: @application_data_buffer, decoded: true)
|
141
|
-
@application_data_buffer = nil
|
142
|
-
@frame_type = nil
|
143
|
-
return message
|
144
|
-
else
|
145
|
-
raise(WebSocket::Error::Frame::InvalidPayloadEncoding) if frame_type == :text && !application_data.valid_encoding?
|
146
|
-
return @frame.class.new(version: @frame.version, type: frame_type, data: application_data, decoded: true)
|
147
|
-
end
|
148
|
-
end
|
168
|
+
# Only using the last 4 bytes for now, till I work out how to
|
169
|
+
# unpack 8 bytes. I'm sure 4GB frames will do for now :)
|
170
|
+
[10, @frame.data.getbytes(6, 4).unpack('N').first]
|
171
|
+
when 126 # Length defined by 2 bytes
|
172
|
+
# Check buffer size
|
173
|
+
return unless buffer_exists?(4) # Buffer incomplete
|
174
|
+
|
175
|
+
[4, @frame.data.getbytes(2, 2).unpack('n').first]
|
176
|
+
else
|
177
|
+
[2, length]
|
149
178
|
end
|
150
|
-
return nil
|
151
179
|
end
|
152
180
|
|
153
|
-
|
154
|
-
|
181
|
+
def decode_payload(payload_length, mask)
|
182
|
+
pointer = 0
|
155
183
|
|
156
|
-
|
184
|
+
# Read application data (unmasked if required)
|
185
|
+
@frame.data.set_mask if mask
|
186
|
+
pointer += 4 if mask
|
187
|
+
payload = @frame.data.getbytes(pointer, payload_length)
|
188
|
+
payload.force_encoding('UTF-8')
|
189
|
+
pointer += payload_length
|
190
|
+
@frame.data.unset_mask if mask
|
157
191
|
|
158
|
-
|
159
|
-
|
192
|
+
# Throw away data up to pointer
|
193
|
+
@frame.data.slice!(0...pointer)
|
160
194
|
|
161
|
-
|
162
|
-
# @param [Symbol] frame_type Frame type name
|
163
|
-
# @return [Integer] opcode or nil
|
164
|
-
# @raise [WebSocket::Error] if frame opcode is not known
|
165
|
-
def type_to_opcode(frame_type)
|
166
|
-
FRAME_TYPES[frame_type] || raise(WebSocket::Error::Frame::UnknownFrameType)
|
195
|
+
payload
|
167
196
|
end
|
168
197
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
def opcode_to_type(opcode)
|
174
|
-
FRAME_TYPES_INVERSE[opcode] || raise(WebSocket::Error::Frame::UnknownOpcode)
|
198
|
+
def decode_continuation_frame(application_data, frame_type)
|
199
|
+
@application_data_buffer ||= ''
|
200
|
+
@application_data_buffer << application_data
|
201
|
+
@frame_type ||= frame_type
|
175
202
|
end
|
176
203
|
|
204
|
+
def decode_finish_continuation_frame(application_data)
|
205
|
+
fail(WebSocket::Error::Frame::UnexpectedContinuationFrame) unless @frame_type
|
206
|
+
@application_data_buffer << application_data
|
207
|
+
# Test valid UTF-8 encoding
|
208
|
+
fail(WebSocket::Error::Frame::InvalidPayloadEncoding) if @frame_type == :text && !@application_data_buffer.valid_encoding?
|
209
|
+
message = @frame.class.new(version: @frame.version, type: @frame_type, data: @application_data_buffer, decoded: true)
|
210
|
+
@application_data_buffer = nil
|
211
|
+
@frame_type = nil
|
212
|
+
message
|
213
|
+
end
|
177
214
|
end
|
178
215
|
end
|
179
216
|
end
|