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