websocket 1.2.2 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +37 -0
  3. data/.travis.yml +2 -0
  4. data/CHANGELOG.md +5 -0
  5. data/Gemfile +1 -1
  6. data/Rakefile +3 -3
  7. data/lib/websocket.rb +2 -3
  8. data/lib/websocket/error.rb +57 -25
  9. data/lib/websocket/exception_handler.rb +2 -14
  10. data/lib/websocket/frame.rb +0 -2
  11. data/lib/websocket/frame/base.rb +12 -13
  12. data/lib/websocket/frame/data.rb +1 -3
  13. data/lib/websocket/frame/handler.rb +0 -2
  14. data/lib/websocket/frame/handler/base.rb +2 -4
  15. data/lib/websocket/frame/handler/handler03.rb +149 -112
  16. data/lib/websocket/frame/handler/handler04.rb +3 -3
  17. data/lib/websocket/frame/handler/handler05.rb +3 -3
  18. data/lib/websocket/frame/handler/handler07.rb +8 -10
  19. data/lib/websocket/frame/handler/handler75.rb +10 -12
  20. data/lib/websocket/frame/incoming.rb +0 -2
  21. data/lib/websocket/frame/incoming/client.rb +0 -2
  22. data/lib/websocket/frame/incoming/server.rb +0 -2
  23. data/lib/websocket/frame/outgoing.rb +1 -3
  24. data/lib/websocket/frame/outgoing/client.rb +0 -2
  25. data/lib/websocket/frame/outgoing/server.rb +0 -2
  26. data/lib/websocket/handshake.rb +0 -2
  27. data/lib/websocket/handshake/base.rb +10 -9
  28. data/lib/websocket/handshake/client.rb +22 -35
  29. data/lib/websocket/handshake/handler.rb +0 -2
  30. data/lib/websocket/handshake/handler/base.rb +0 -2
  31. data/lib/websocket/handshake/handler/client.rb +0 -2
  32. data/lib/websocket/handshake/handler/client01.rb +0 -2
  33. data/lib/websocket/handshake/handler/client04.rb +3 -5
  34. data/lib/websocket/handshake/handler/client11.rb +0 -2
  35. data/lib/websocket/handshake/handler/client75.rb +2 -4
  36. data/lib/websocket/handshake/handler/client76.rb +6 -8
  37. data/lib/websocket/handshake/handler/server.rb +0 -1
  38. data/lib/websocket/handshake/handler/server04.rb +3 -5
  39. data/lib/websocket/handshake/handler/server75.rb +2 -4
  40. data/lib/websocket/handshake/handler/server76.rb +7 -9
  41. data/lib/websocket/handshake/server.rb +19 -24
  42. data/lib/websocket/version.rb +1 -1
  43. data/spec/frame/incoming_03_spec.rb +1 -2
  44. data/spec/frame/incoming_04_spec.rb +1 -2
  45. data/spec/frame/incoming_05_spec.rb +1 -2
  46. data/spec/frame/incoming_07_spec.rb +1 -2
  47. data/spec/frame/incoming_75_spec.rb +1 -2
  48. data/spec/support/all_client_drafts.rb +1 -1
  49. data/spec/support/all_server_drafts.rb +7 -10
  50. data/spec/support/frames_base.rb +0 -2
  51. data/spec/support/handshake_requests.rb +4 -4
  52. data/spec/support/outgoing_frames.rb +0 -1
  53. data/spec/support/overwrites.rb +0 -2
  54. data/websocket.gemspec +10 -10
  55. metadata +4 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7ca690c7a42466b8cb035271b77010bdc7bf8a19
4
- data.tar.gz: 206ca9afa7b5606cb08b85ef66a8f186342d37b3
3
+ metadata.gz: 48ee0f12aaa5fd6aba76f7c2070a09f600c53204
4
+ data.tar.gz: d011719667dc6227eecf743a764d0f24f7bf17b2
5
5
  SHA512:
6
- metadata.gz: 6b846fa2663e5f85415adc35e2aa39e0e7b21f844b444546c432a6b630c79d4aa4aabf5dbaf996c6334b80e1296b81a35a0935a51a9d0cfa55d6a748fb1762b7
7
- data.tar.gz: cbf090a638401a094b7432f7a634ba7bc2f8fcd77b06e0407a8d2490f3236858368d619a6acf17296a715824c239b1c51beb5577597af91133e6c6472338c659
6
+ metadata.gz: 568b9cd4986500c8c9ddf5ec2d8f90b81b654fd6b10451480209ca505be73e0d8a24b11b10040aa7bd3211c84bf6c25154482655985a117705657e75eb302e0f
7
+ data.tar.gz: 3c3c8d8c324b480654891f7326f1cf7153dbf3f7b4838c1b3ad2bbe670ba25c57dc2485092b049f5d5fd35b1f131a63882a46b989f684fc40a3925fc3d2024bb
@@ -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
@@ -8,3 +8,5 @@ rvm:
8
8
  - rbx-2
9
9
  - jruby-19mode
10
10
  - ruby-head
11
+ before_install:
12
+ - gem install bundler
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.3
4
+
5
+ - fix for draft 76 when challenge might sometimes fail
6
+ - multiple small optimizations
7
+
3
8
  ## 1.2.2
4
9
 
5
10
  - fix handshake for draft 11+ sending Sec-WebSocket-Origin instead of Origin
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source "http://rubygems.org"
1
+ source 'http://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
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 = ["-c", "-f progress"]
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 "Run autobahn tests for client"
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 "Run autobahn tests for server"
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
@@ -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
@@ -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; :control_frame_payload_too_long; end
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; :data_frame_instead_continuation; end
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; :fragmented_control_frame; end
17
+ def message
18
+ :fragmented_control_frame
19
+ end
16
20
  end
17
21
 
18
22
  class Invalid < ::WebSocket::Error::Frame
19
- def message; :invalid_frame; end
23
+ def message
24
+ :invalid_frame
25
+ end
20
26
  end
21
27
 
22
28
  class InvalidPayloadEncoding < ::WebSocket::Error::Frame
23
- def message; :invalid_payload_encoding; end
29
+ def message
30
+ :invalid_payload_encoding
31
+ end
24
32
  end
25
33
 
26
34
  class MaskTooShort < ::WebSocket::Error::Frame
27
- def message; :mask_is_too_short; end
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; :reserved_bit_used; end
41
+ def message
42
+ :reserved_bit_used
43
+ end
32
44
  end
33
45
 
34
46
  class TooLong < ::WebSocket::Error::Frame
35
- def message; :frame_too_long; end
47
+ def message
48
+ :frame_too_long
49
+ end
36
50
  end
37
51
 
38
52
  class UnexpectedContinuationFrame < ::WebSocket::Error::Frame
39
- def message; :unexpected_continuation_frame; end
53
+ def message
54
+ :unexpected_continuation_frame
55
+ end
40
56
  end
41
57
 
42
58
  class UnknownFrameType < ::WebSocket::Error::Frame
43
- def message; :unknown_frame_type; end
59
+ def message
60
+ :unknown_frame_type
61
+ end
44
62
  end
45
63
 
46
64
  class UnknownOpcode < ::WebSocket::Error::Frame
47
- def message; :unknown_opcode; end
65
+ def message
66
+ :unknown_opcode
67
+ end
48
68
  end
49
69
 
50
70
  class UnknownCloseCode < ::WebSocket::Error::Frame
51
- def message; :unknown_close_code; end
71
+ def message
72
+ :unknown_close_code
73
+ end
52
74
  end
53
75
 
54
76
  class UnknownVersion < ::WebSocket::Error::Frame
55
- def message; :unknown_protocol_version; end
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; :get_request_required; end
85
+ def message
86
+ :get_request_required
87
+ end
64
88
  end
65
89
 
66
90
  class InvalidAuthentication < ::WebSocket::Error::Handshake
67
- def message; :invalid_handshake_authentication; end
91
+ def message
92
+ :invalid_handshake_authentication
93
+ end
68
94
  end
69
95
 
70
96
  class InvalidHeader < ::WebSocket::Error::Handshake
71
- def message; :invalid_header; end
97
+ def message
98
+ :invalid_header
99
+ end
72
100
  end
73
101
 
74
102
  class InvalidStatusCode < ::WebSocket::Error::Handshake
75
- def message; :invalid_status_code; end
103
+ def message
104
+ :invalid_status_code
105
+ end
76
106
  end
77
107
 
78
108
  class NoHostProvided < ::WebSocket::Error::Handshake
79
- def message; :no_host_provided; end
109
+ def message
110
+ :no_host_provided
111
+ end
80
112
  end
81
113
 
82
114
  class UnknownVersion < ::WebSocket::Error::Handshake
83
- def message; :unknown_protocol_version; end
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
- set_error(e.message.to_sym)
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
@@ -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
@@ -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
- !!@error
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
- raise NotImplementedError
39
+ fail NotImplementedError
40
40
  end
41
41
 
42
42
  # Recreate inspect as #to_s was overwritten
43
43
  def inspect
44
- vars = self.instance_variables.map { |v| "#{v}=#{instance_variable_get(v).inspect}" }.join(', ')
45
- insp = "#{self.class}:0x%08x" % (self.__id__ * 2)
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
- 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 raise WebSocket::Error::Frame::UnknownVersion
62
- end
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
@@ -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
- raise WebSocket::Error::Frame::MaskTooShort if bytesize < 4
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
- raise NotImplementedError
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
- raise NotImplementedError
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
- opcode = type_to_opcode(@frame.type)
31
- byte1 = opcode | (fin ? 0b10000000 : 0b00000000) # since more, rsv1-3 are 0 and 0x80 for Draft 4
32
- frame << byte1
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
- length = @frame.data.size
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
- byte2 = length # since rsv4 is 0
39
- frame << (byte2 | mask)
100
+ output << (length | mask) # since rsv4 is 0
40
101
  elsif length < 65_536 # write 2 byte length
41
- frame << (126 | mask)
42
- frame << [length].pack('n')
102
+ output << (126 | mask)
103
+ output << [length].pack('n')
43
104
  else # write 8 byte length
44
- frame << (127 | mask)
45
- frame << [length >> 32, length & 0xFFFFFFFF].pack('NN')
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
- # @see WebSocket::Frame::Handler::Base#decode_frame
61
- def decode_frame
62
- while @frame.data.size > 1
63
- pointer = 0
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
- more = ((@frame.data.getbyte(pointer) & 0b10000000) == 0b10000000) ^ fin
116
+ # Compute the expected frame length
117
+ frame_length = header_length + payload_length
118
+ frame_length += 4 if mask
66
119
 
67
- raise(WebSocket::Error::Frame::ReservedBitUsed) if @frame.data.getbyte(pointer) & 0b01110000 != 0b00000000
120
+ fail(WebSocket::Error::Frame::TooLong) if frame_length > WebSocket.max_frame_size
68
121
 
69
- opcode = @frame.data.getbyte(pointer) & 0b00001111
70
- frame_type = opcode_to_type(opcode)
71
- pointer += 1
122
+ # Check buffer size
123
+ return unless buffer_exists?(frame_length) # Buffer incomplete
72
124
 
73
- raise(WebSocket::Error::Frame::FragmentedControlFrame) if more && control_frame?(frame_type)
74
- raise(WebSocket::Error::Frame::DataFrameInsteadContinuation) if data_frame?(frame_type) && !@application_data_buffer.nil?
125
+ # Remove frame header
126
+ @frame.data.slice!(0...header_length)
75
127
 
76
- mask = @frame.incoming_masking? && (@frame.data.getbyte(pointer) & 0b10000000) == 0b10000000
77
- length = @frame.data.getbyte(pointer) & 0b01111111
128
+ [true, more, frame_type, mask, payload_length]
129
+ end
78
130
 
79
- raise(WebSocket::Error::Frame::ControlFramePayloadTooLong) if length > 125 && control_frame?(frame_type)
131
+ def buffer_exists?(buffer_number)
132
+ !@frame.data.getbyte(buffer_number - 1).nil?
133
+ end
80
134
 
81
- pointer += 1
135
+ def decode_first_byte
136
+ first_byte = @frame.data.getbyte(0)
82
137
 
83
- payload_length = case length
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
- # Only using the last 4 bytes for now, till I work out how to
89
- # unpack 8 bytes. I'm sure 4GB frames will do for now :)
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
- l = @frame.data.getbytes(pointer, 2).unpack('n').first
98
- pointer += 2
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
- # Compute the expected frame length
105
- frame_length = pointer + payload_length
106
- frame_length += 4 if mask
146
+ [more, frame_type]
147
+ end
107
148
 
108
- raise(WebSocket::Error::Frame::TooLong) if frame_length > WebSocket.max_frame_size
149
+ def decode_second_byte(frame_type)
150
+ second_byte = @frame.data.getbyte(1)
109
151
 
110
- # Check buffer size
111
- return if @frame.data.getbyte(frame_length - 1) == nil # Buffer incomplete
152
+ mask = @frame.incoming_masking? && (second_byte & 0b10000000) == 0b10000000
153
+ length = second_byte & 0b01111111
112
154
 
113
- # Remove frame header
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
- # Read application data (unmasked if required)
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
- # Throw away data up to pointer
126
- @frame.data.slice!(0...pointer)
159
+ [header_length, payload_length, mask]
160
+ end
127
161
 
128
- raise(WebSocket::Error::Frame::UnexpectedContinuationFrame) if frame_type == :continuation && !@frame_type
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
- if more
131
- @application_data_buffer ||= ''
132
- @application_data_buffer << application_data
133
- @frame_type ||= frame_type
134
- else
135
- # Message is complete
136
- if frame_type == :continuation
137
- @application_data_buffer << application_data
138
- # Test valid UTF-8 encoding
139
- raise(WebSocket::Error::Frame::InvalidPayloadEncoding) if @frame_type == :text && !@application_data_buffer.valid_encoding?
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
- # Allow turning on or off masking
154
- def masking?; false; end
181
+ def decode_payload(payload_length, mask)
182
+ pointer = 0
155
183
 
156
- private
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
- # This allows flipping the more bit to fin for draft 04
159
- def fin; false; end
192
+ # Throw away data up to pointer
193
+ @frame.data.slice!(0...pointer)
160
194
 
161
- # Convert frame type name to opcode
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
- # Convert frame opcode to type name
170
- # @param [Integer] opcode Opcode
171
- # @return [Symbol] Frame type name or nil
172
- # @raise [WebSocket::Error] if frame type name is not known
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