websocket 1.2.3 → 1.2.9

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