websocket 1.2.3 → 1.2.9

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.
Files changed (76) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +17 -0
  3. data/.github/workflows/publish.yml +17 -0
  4. data/.rubocop.yml +35 -3
  5. data/.travis.yml +14 -7
  6. data/CHANGELOG.md +24 -0
  7. data/Gemfile +12 -4
  8. data/README.md +3 -2
  9. data/Rakefile +8 -3
  10. data/lib/websocket.rb +4 -1
  11. data/lib/websocket/error.rb +8 -0
  12. data/lib/websocket/exception_handler.rb +3 -1
  13. data/lib/websocket/frame.rb +2 -0
  14. data/lib/websocket/frame/base.rb +5 -9
  15. data/lib/websocket/frame/data.rb +5 -2
  16. data/lib/websocket/frame/handler.rb +2 -0
  17. data/lib/websocket/frame/handler/base.rb +6 -4
  18. data/lib/websocket/frame/handler/handler03.rb +23 -16
  19. data/lib/websocket/frame/handler/handler04.rb +1 -0
  20. data/lib/websocket/frame/handler/handler05.rb +1 -0
  21. data/lib/websocket/frame/handler/handler07.rb +10 -9
  22. data/lib/websocket/frame/handler/handler75.rb +8 -7
  23. data/lib/websocket/frame/incoming.rb +2 -0
  24. data/lib/websocket/frame/incoming/client.rb +2 -0
  25. data/lib/websocket/frame/incoming/server.rb +2 -0
  26. data/lib/websocket/frame/outgoing.rb +3 -1
  27. data/lib/websocket/frame/outgoing/client.rb +2 -0
  28. data/lib/websocket/frame/outgoing/server.rb +2 -0
  29. data/lib/websocket/handshake.rb +2 -0
  30. data/lib/websocket/handshake/base.rb +26 -13
  31. data/lib/websocket/handshake/client.rb +17 -14
  32. data/lib/websocket/handshake/handler.rb +2 -0
  33. data/lib/websocket/handshake/handler/base.rb +2 -0
  34. data/lib/websocket/handshake/handler/client.rb +11 -0
  35. data/lib/websocket/handshake/handler/client01.rb +2 -0
  36. data/lib/websocket/handshake/handler/client04.rb +15 -4
  37. data/lib/websocket/handshake/handler/client11.rb +2 -0
  38. data/lib/websocket/handshake/handler/client75.rb +18 -2
  39. data/lib/websocket/handshake/handler/client76.rb +11 -5
  40. data/lib/websocket/handshake/handler/server.rb +2 -0
  41. data/lib/websocket/handshake/handler/server04.rb +12 -4
  42. data/lib/websocket/handshake/handler/server75.rb +21 -5
  43. data/lib/websocket/handshake/handler/server76.rb +14 -16
  44. data/lib/websocket/handshake/server.rb +7 -4
  45. data/lib/websocket/nice_inspect.rb +12 -0
  46. data/lib/websocket/version.rb +3 -1
  47. data/spec/frame/incoming_03_spec.rb +20 -17
  48. data/spec/frame/incoming_04_spec.rb +20 -17
  49. data/spec/frame/incoming_05_spec.rb +22 -19
  50. data/spec/frame/incoming_07_spec.rb +24 -21
  51. data/spec/frame/incoming_75_spec.rb +13 -10
  52. data/spec/frame/incoming_common_spec.rb +18 -8
  53. data/spec/frame/masking_spec.rb +3 -1
  54. data/spec/frame/outgoing_03_spec.rb +13 -10
  55. data/spec/frame/outgoing_04_spec.rb +13 -10
  56. data/spec/frame/outgoing_05_spec.rb +12 -9
  57. data/spec/frame/outgoing_07_spec.rb +13 -10
  58. data/spec/frame/outgoing_75_spec.rb +8 -5
  59. data/spec/frame/outgoing_common_spec.rb +11 -4
  60. data/spec/handshake/client_04_spec.rb +46 -3
  61. data/spec/handshake/client_11_spec.rb +5 -3
  62. data/spec/handshake/client_75_spec.rb +28 -1
  63. data/spec/handshake/client_76_spec.rb +30 -3
  64. data/spec/handshake/server_04_spec.rb +37 -4
  65. data/spec/handshake/server_75_spec.rb +25 -1
  66. data/spec/handshake/server_76_spec.rb +33 -9
  67. data/spec/spec_helper.rb +2 -2
  68. data/spec/support/all_client_drafts.rb +23 -21
  69. data/spec/support/all_server_drafts.rb +21 -16
  70. data/spec/support/frames_base.rb +2 -0
  71. data/spec/support/handshake_requests.rb +19 -17
  72. data/spec/support/incoming_frames.rb +46 -25
  73. data/spec/support/outgoing_frames.rb +30 -8
  74. data/spec/support/overwrites.rb +2 -0
  75. data/websocket.gemspec +4 -1
  76. metadata +7 -5
@@ -1,4 +1,5 @@
1
1
  # encoding: binary
2
+ # frozen_string_literal: true
2
3
 
3
4
  module WebSocket
4
5
  module Frame
@@ -1,4 +1,5 @@
1
1
  # encoding: binary
2
+ # frozen_string_literal: true
2
3
 
3
4
  module WebSocket
4
5
  module Frame
@@ -1,4 +1,5 @@
1
1
  # encoding: binary
2
+ # frozen_string_literal: true
2
3
 
3
4
  module WebSocket
4
5
  module Frame
@@ -12,15 +13,15 @@ module WebSocket
12
13
  close: 8,
13
14
  ping: 9,
14
15
  pong: 10
15
- }
16
+ }.freeze
16
17
 
17
18
  # Hash of frame opcodes and it's names
18
- FRAME_TYPES_INVERSE = FRAME_TYPES.invert
19
+ FRAME_TYPES_INVERSE = FRAME_TYPES.invert.freeze
19
20
 
20
21
  def encode_frame
21
22
  if @frame.type == :close
22
23
  code = @frame.code || 1000
23
- fail WebSocket::Error::Frame::UnknownCloseCode unless valid_code?(code)
24
+ raise WebSocket::Error::Frame::UnknownCloseCode unless valid_code?(code)
24
25
  @frame.data = Data.new([code].pack('n') + @frame.data.to_s)
25
26
  @frame.code = nil
26
27
  end
@@ -32,8 +33,8 @@ module WebSocket
32
33
  if close_code?(result)
33
34
  code = result.data.slice!(0..1)
34
35
  result.code = code.unpack('n').first
35
- fail WebSocket::Error::Frame::UnknownCloseCode unless valid_code?(result.code)
36
- fail WebSocket::Error::Frame::InvalidPayloadEncoding unless valid_encoding?(result.data)
36
+ raise WebSocket::Error::Frame::UnknownCloseCode unless valid_code?(result.code)
37
+ raise WebSocket::Error::Frame::InvalidPayloadEncoding unless valid_encoding?(result.data)
37
38
  end
38
39
  result
39
40
  end
@@ -41,14 +42,14 @@ module WebSocket
41
42
  private
42
43
 
43
44
  def valid_code?(code)
44
- [1000, 1001, 1002, 1003, 1007, 1008, 1009, 1010, 1011].include?(code) || (3000..4999).include?(code)
45
+ [1000, 1001, 1002, 1003, 1007, 1008, 1009, 1010, 1011].include?(code) || (3000..4999).cover?(code)
45
46
  end
46
47
 
47
48
  def valid_encoding?(data)
48
49
  return true if data.nil?
49
50
  data.encode('UTF-8')
50
51
  true
51
- rescue
52
+ rescue StandardError
52
53
  false
53
54
  end
54
55
 
@@ -61,7 +62,7 @@ module WebSocket
61
62
  # @return [Integer] opcode or nil
62
63
  # @raise [WebSocket::Error] if frame opcode is not known
63
64
  def type_to_opcode(frame_type)
64
- FRAME_TYPES[frame_type] || fail(WebSocket::Error::Frame::UnknownFrameType)
65
+ FRAME_TYPES[frame_type] || raise(WebSocket::Error::Frame::UnknownFrameType)
65
66
  end
66
67
 
67
68
  # Convert frame opcode to type name
@@ -69,7 +70,7 @@ module WebSocket
69
70
  # @return [Symbol] Frame type name or nil
70
71
  # @raise [WebSocket::Error] if frame type name is not known
71
72
  def opcode_to_type(opcode)
72
- FRAME_TYPES_INVERSE[opcode] || fail(WebSocket::Error::Frame::UnknownOpcode)
73
+ FRAME_TYPES_INVERSE[opcode] || raise(WebSocket::Error::Frame::UnknownOpcode)
73
74
  end
74
75
  end
75
76
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: binary
2
+ # frozen_string_literal: true
2
3
 
3
4
  module WebSocket
4
5
  module Frame
@@ -6,7 +7,7 @@ module WebSocket
6
7
  class Handler75 < Base
7
8
  # @see WebSocket::Frame::Base#supported_frames
8
9
  def supported_frames
9
- [:text, :close]
10
+ %i[text close]
10
11
  end
11
12
 
12
13
  # @see WebSocket::Frame::Handler::Base#encode_frame
@@ -17,13 +18,13 @@ module WebSocket
17
18
  ary = ["\x00", @frame.data, "\xff"]
18
19
  ary.map { |s| s.encode('UTF-8', 'UTF-8', invalid: :replace) }
19
20
  ary.join
20
- else fail WebSocket::Error::Frame::UnknownFrameType
21
+ else raise WebSocket::Error::Frame::UnknownFrameType
21
22
  end
22
23
  end
23
24
 
24
25
  # @see WebSocket::Frame::Handler::Base#decode_frame
25
26
  def decode_frame
26
- return if @frame.data.size == 0
27
+ return if @frame.data.size.zero?
27
28
 
28
29
  pointer = 0
29
30
  frame_type = @frame.data.getbyte(pointer)
@@ -42,7 +43,7 @@ module WebSocket
42
43
  break unless (b & 0x80) == 0x80
43
44
  end
44
45
 
45
- fail WebSocket::Error::Frame::TooLong if length > ::WebSocket.max_frame_size
46
+ raise WebSocket::Error::Frame::TooLong if length > ::WebSocket.max_frame_size
46
47
 
47
48
  unless @frame.data.getbyte(pointer + length - 1).nil?
48
49
  # Straight from spec - I'm sure this isn't crazy...
@@ -51,17 +52,17 @@ module WebSocket
51
52
  @frame.instance_variable_set '@data', @frame.data[(pointer + length)..-1]
52
53
 
53
54
  # If the /frame type/ is 0xFF and the /length/ was 0, then close
54
- if length == 0
55
+ if length.zero?
55
56
  @frame.class.new(version: @frame.version, type: :close, decoded: true)
56
57
  end
57
58
  end
58
59
  else
59
60
  # If the high-order bit of the /frame type/ byte is _not_ set
60
61
 
61
- fail WebSocket::Error::Frame::Invalid if @frame.data.getbyte(0) != 0x00
62
+ raise WebSocket::Error::Frame::Invalid if @frame.data.getbyte(0) != 0x00
62
63
 
63
64
  # Addition to the spec to protect against malicious requests
64
- fail WebSocket::Error::Frame::TooLong if @frame.data.size > ::WebSocket.max_frame_size
65
+ raise WebSocket::Error::Frame::TooLong if @frame.data.size > ::WebSocket.max_frame_size
65
66
 
66
67
  msg = @frame.data.slice!(/\A\x00[^\xff]*\xff/)
67
68
  if msg
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebSocket
2
4
  module Frame
3
5
  # Construct or parse incoming WebSocket Frame.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebSocket
2
4
  module Frame
3
5
  class Incoming
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebSocket
2
4
  module Frame
3
5
  class Incoming
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebSocket
2
4
  module Frame
3
5
  # Construct or parse incoming WebSocket Frame.
@@ -24,7 +26,7 @@ module WebSocket
24
26
 
25
27
  # Return raw frame formatted for sending.
26
28
  def to_s
27
- fail WebSocket::Error::Frame::UnknownFrameType unless supported?
29
+ raise WebSocket::Error::Frame::UnknownFrameType unless supported?
28
30
  @handler.encode_frame
29
31
  end
30
32
  rescue_method :to_s
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebSocket
2
4
  module Frame
3
5
  class Outgoing
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebSocket
2
4
  module Frame
3
5
  class Outgoing
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebSocket
2
4
  module Handshake
3
5
  autoload :Base, "#{::WebSocket::ROOT}/websocket/handshake/base"
@@ -1,21 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebSocket
2
4
  module Handshake
3
5
  # @abstract Subclass and override to implement custom handshakes
4
6
  class Base
5
7
  include ExceptionHandler
8
+ include NiceInspect
6
9
 
7
10
  attr_reader :host, :port, :path, :query,
8
- :state, :version, :secure, :headers
11
+ :state, :version, :secure,
12
+ :headers, :protocols
9
13
 
10
14
  # Initialize new WebSocket Handshake and set it's state to :new
11
15
  def initialize(args = {})
12
- args.each { |k, v| instance_variable_set("@#{k}", v) }
16
+ args.each do |k, v|
17
+ value = begin
18
+ v.dup
19
+ rescue TypeError
20
+ v
21
+ end
22
+ instance_variable_set("@#{k}", value)
23
+ end
13
24
 
14
25
  @state = :new
15
26
  @handler = nil
16
27
 
17
- @data = ''
28
+ @data = String.new('')
18
29
  @headers ||= {}
30
+ @protocols ||= []
19
31
  end
20
32
 
21
33
  # @abstract Add data to handshake
@@ -30,13 +42,6 @@ module WebSocket
30
42
  end
31
43
  rescue_method :to_s, return: ''
32
44
 
33
- # Recreate inspect as #to_s was overwritten
34
- def inspect
35
- vars = instance_variables.map { |v| "#{v}=#{instance_variable_get(v).inspect}" }.join(', ')
36
- insp = Kernel.format("#{self.class}:0x%08x", __id__)
37
- "<#{insp} #{vars}>"
38
- end
39
-
40
45
  # Is parsing of data finished?
41
46
  # @return [Boolena] True if request was completely parsed or error occured. False otherwise
42
47
  def finished?
@@ -52,7 +57,7 @@ module WebSocket
52
57
 
53
58
  # @abstract Should send data after parsing is finished?
54
59
  def should_respond?
55
- fail NotImplementedError
60
+ raise NotImplementedError
56
61
  end
57
62
 
58
63
  # Data left from parsing. Sometimes data that doesn't belong to handshake are added - use this method to retrieve them.
@@ -66,7 +71,7 @@ module WebSocket
66
71
  # @example
67
72
  # @handshake.uri #=> "ws://example.com/path?query=true"
68
73
  def uri
69
- uri = secure ? 'wss://' : 'ws://'
74
+ uri = String.new(secure ? 'wss://' : 'ws://')
70
75
  uri << host
71
76
  uri << ":#{port}" if port
72
77
  uri << path
@@ -104,7 +109,15 @@ module WebSocket
104
109
 
105
110
  lines.each do |line|
106
111
  h = HEADER.match(line)
107
- @headers[h[1].strip.downcase] = h[2].strip if h
112
+ next unless h # Skip any invalid headers
113
+ key = h[1].strip.downcase
114
+ val = h[2].strip
115
+ # If the header is already set and refers to the websocket protocol, append the new value
116
+ if @headers.key?(key) && key =~ /^(sec-)?websocket-protocol$/
117
+ @headers[key] << ", #{val}"
118
+ else
119
+ @headers[key] = val
120
+ end
108
121
  end
109
122
 
110
123
  @state = :finished
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'uri'
2
4
 
3
5
  module WebSocket
@@ -38,16 +40,17 @@ module WebSocket
38
40
  #
39
41
  # @param [Hash] args Arguments for client
40
42
  #
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 [Integer] :version Version of WebSocket to use. Default: 13 (this is version from RFC)
50
- # @option args [Hash] :headers HTTP headers to use in the handshake
43
+ # @option args [String] :host Host of request. Required if no :url param was provided.
44
+ # @option args [String] :origin Origin of request. Optional, should be used mostly by browsers. Default: nil
45
+ # @option args [String] :path Path of request. Should start with '/'. Default: '/'
46
+ # @option args [Integer] :port Port of request. Default: nil
47
+ # @option args [String] :query. Query for request. Should be in format "aaa=bbb&ccc=ddd"
48
+ # @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.
49
+ # @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.
50
+ # @option args [String] :uri Alias to :url
51
+ # @option args [Array<String>] :protocols An array of supported sub-protocols
52
+ # @option args [Integer] :version Version of WebSocket to use. Default: 13 (this is version from RFC)
53
+ # @option args [Hash] :headers HTTP headers to use in the handshake
51
54
  #
52
55
  # @example
53
56
  # Websocket::Handshake::Client.new(url: "ws://example.com/path?query=true")
@@ -66,7 +69,7 @@ module WebSocket
66
69
  @path = '/' if @path.nil? || @path.empty?
67
70
  @version ||= DEFAULT_VERSION
68
71
 
69
- fail WebSocket::Error::Handshake::NoHostProvided unless @host
72
+ raise WebSocket::Error::Handshake::NoHostProvided unless @host
70
73
 
71
74
  include_version
72
75
  end
@@ -107,7 +110,7 @@ module WebSocket
107
110
  when 1..3 then Handler::Client01.new(self)
108
111
  when 4..10 then Handler::Client04.new(self)
109
112
  when 11..17 then Handler::Client11.new(self)
110
- else fail WebSocket::Error::Handshake::UnknownVersion
113
+ else raise WebSocket::Error::Handshake::UnknownVersion
111
114
  end
112
115
  end
113
116
 
@@ -118,9 +121,9 @@ module WebSocket
118
121
  # @return [Boolean] True if parsed correctly. False otherwise
119
122
  def parse_first_line(line)
120
123
  line_parts = line.match(FIRST_LINE)
121
- fail WebSocket::Error::Handshake::InvalidHeader unless line_parts
124
+ raise WebSocket::Error::Handshake::InvalidHeader unless line_parts
122
125
  status = line_parts[1]
123
- fail WebSocket::Error::Handshake::InvalidStatusCode unless status == '101'
126
+ raise WebSocket::Error::Handshake::InvalidStatusCode unless status == '101'
124
127
  end
125
128
  end
126
129
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebSocket
2
4
  module Handshake
3
5
  module Handler
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebSocket
2
4
  module Handshake
3
5
  module Handler
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebSocket
2
4
  module Handshake
3
5
  module Handler
@@ -15,6 +17,15 @@ module WebSocket
15
17
  def handshake_keys
16
18
  super + @handshake.headers.to_a
17
19
  end
20
+
21
+ # Verify if received header matches with one of the sent ones
22
+ # @return [Boolean] True if matching. False otherwise(appropriate error is set)
23
+ def verify_protocol
24
+ return true if supported_protocols.empty?
25
+ protos = provided_protocols & supported_protocols
26
+ raise WebSocket::Error::Handshake::UnsupportedProtocol if protos.empty?
27
+ true
28
+ end
18
29
  end
19
30
  end
20
31
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'digest/md5'
2
4
 
3
5
  module WebSocket
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'digest/sha1'
2
4
  require 'base64'
3
5
 
@@ -7,7 +9,7 @@ module WebSocket
7
9
  class Client04 < Client
8
10
  # @see WebSocket::Handshake::Base#valid?
9
11
  def valid?
10
- super && verify_accept
12
+ super && verify_accept && verify_protocol
11
13
  end
12
14
 
13
15
  private
@@ -15,8 +17,8 @@ module WebSocket
15
17
  # @see WebSocket::Handshake::Handler::Base#handshake_keys
16
18
  def handshake_keys
17
19
  keys = [
18
- %w(Upgrade websocket),
19
- %w(Connection Upgrade)
20
+ %w[Upgrade websocket],
21
+ %w[Connection Upgrade]
20
22
  ]
21
23
  host = @handshake.host
22
24
  host += ":#{@handshake.port}" if @handshake.port
@@ -25,6 +27,7 @@ module WebSocket
25
27
  keys << ['Sec-WebSocket-Origin', @handshake.origin] if @handshake.origin
26
28
  keys << ['Sec-WebSocket-Version', @handshake.version]
27
29
  keys << ['Sec-WebSocket-Key', key]
30
+ keys << ['Sec-WebSocket-Protocol', @handshake.protocols.join(', ')] if @handshake.protocols.any?
28
31
  keys
29
32
  end
30
33
 
@@ -43,9 +46,17 @@ module WebSocket
43
46
  # Verify if received header Sec-WebSocket-Accept matches generated one.
44
47
  # @return [Boolean] True if accept is matching. False otherwise(appropriate error is set)
45
48
  def verify_accept
46
- fail WebSocket::Error::Handshake::InvalidAuthentication unless @handshake.headers['sec-websocket-accept'] == accept
49
+ raise WebSocket::Error::Handshake::InvalidAuthentication unless @handshake.headers['sec-websocket-accept'] == accept
47
50
  true
48
51
  end
52
+
53
+ def supported_protocols
54
+ @handshake.protocols
55
+ end
56
+
57
+ def provided_protocols
58
+ @handshake.headers['sec-websocket-protocol'].to_s.split(/ *, */)
59
+ end
49
60
  end
50
61
  end
51
62
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebSocket
2
4
  module Handshake
3
5
  module Handler
@@ -1,22 +1,38 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebSocket
2
4
  module Handshake
3
5
  module Handler
4
6
  class Client75 < Client
7
+ # @see WebSocket::Handshake::Base#valid?
8
+ def valid?
9
+ super && verify_protocol
10
+ end
11
+
5
12
  private
6
13
 
7
14
  # @see WebSocket::Handshake::Handler::Base#handshake_keys
8
15
  def handshake_keys
9
16
  keys = [
10
- %w(Upgrade WebSocket),
11
- %w(Connection Upgrade)
17
+ %w[Upgrade WebSocket],
18
+ %w[Connection Upgrade]
12
19
  ]
13
20
  host = @handshake.host
14
21
  host += ":#{@handshake.port}" if @handshake.port
15
22
  keys << ['Host', host]
16
23
  keys << ['Origin', @handshake.origin] if @handshake.origin
24
+ keys << ['WebSocket-Protocol', @handshake.protocols.first] if @handshake.protocols.any?
17
25
  keys += super
18
26
  keys
19
27
  end
28
+
29
+ def supported_protocols
30
+ Array(@handshake.protocols.first)
31
+ end
32
+
33
+ def provided_protocols
34
+ Array(@handshake.headers['websocket-protocol'].to_s.strip)
35
+ end
20
36
  end
21
37
  end
22
38
  end