websocket 1.1.4 → 1.2.0

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 (55) hide show
  1. data/.travis.yml +2 -6
  2. data/CHANGELOG.md +7 -0
  3. data/Gemfile +2 -1
  4. data/README.md +10 -6
  5. data/Rakefile +1 -1
  6. data/lib/websocket.rb +1 -1
  7. data/lib/websocket/exception_handler.rb +6 -0
  8. data/lib/websocket/frame/base.rb +1 -1
  9. data/lib/websocket/frame/data.rb +11 -9
  10. data/lib/websocket/frame/handler.rb +1 -1
  11. data/lib/websocket/frame/handler/handler03.rb +17 -17
  12. data/lib/websocket/frame/handler/handler07.rb +8 -8
  13. data/lib/websocket/frame/handler/handler75.rb +8 -7
  14. data/lib/websocket/frame/incoming.rb +1 -1
  15. data/lib/websocket/frame/outgoing.rb +1 -1
  16. data/lib/websocket/handshake/base.rb +7 -7
  17. data/lib/websocket/handshake/client.rb +5 -3
  18. data/lib/websocket/handshake/handler/base.rb +5 -5
  19. data/lib/websocket/handshake/handler/client.rb +6 -1
  20. data/lib/websocket/handshake/handler/client04.rb +7 -6
  21. data/lib/websocket/handshake/handler/client75.rb +5 -4
  22. data/lib/websocket/handshake/handler/client76.rb +5 -5
  23. data/lib/websocket/handshake/handler/server04.rb +11 -6
  24. data/lib/websocket/handshake/handler/server75.rb +5 -5
  25. data/lib/websocket/handshake/handler/server76.rb +9 -9
  26. data/lib/websocket/handshake/server.rb +25 -24
  27. data/lib/websocket/version.rb +1 -1
  28. data/spec/frame/incoming_03_spec.rb +25 -25
  29. data/spec/frame/incoming_04_spec.rb +25 -25
  30. data/spec/frame/incoming_05_spec.rb +29 -29
  31. data/spec/frame/incoming_07_spec.rb +31 -31
  32. data/spec/frame/incoming_75_spec.rb +13 -13
  33. data/spec/frame/incoming_common_spec.rb +12 -13
  34. data/spec/frame/masking_spec.rb +10 -10
  35. data/spec/frame/outgoing_03_spec.rb +17 -17
  36. data/spec/frame/outgoing_04_spec.rb +17 -17
  37. data/spec/frame/outgoing_05_spec.rb +17 -17
  38. data/spec/frame/outgoing_07_spec.rb +19 -19
  39. data/spec/frame/outgoing_75_spec.rb +9 -9
  40. data/spec/frame/outgoing_common_spec.rb +7 -8
  41. data/spec/handshake/client_04_spec.rb +9 -9
  42. data/spec/handshake/client_75_spec.rb +2 -2
  43. data/spec/handshake/client_76_spec.rb +9 -9
  44. data/spec/handshake/server_04_spec.rb +5 -5
  45. data/spec/handshake/server_75_spec.rb +1 -1
  46. data/spec/handshake/server_76_spec.rb +21 -21
  47. data/spec/spec_helper.rb +4 -1
  48. data/spec/support/all_client_drafts.rb +62 -52
  49. data/spec/support/all_server_drafts.rb +49 -49
  50. data/spec/support/handshake_requests.rb +16 -16
  51. data/spec/support/incoming_frames.rb +28 -28
  52. data/spec/support/outgoing_frames.rb +10 -10
  53. data/websocket.gemspec +1 -1
  54. metadata +42 -22
  55. checksums.yaml +0 -7
data/.travis.yml CHANGED
@@ -1,14 +1,10 @@
1
1
  language: ruby
2
2
  script: "bundle exec rake spec"
3
3
  rvm:
4
- - 1.8.7
5
- - 1.9.2
6
4
  - 1.9.3
7
5
  - 2.0.0
8
6
  - 2.1.1
9
- - ree
10
- - rbx
11
- - jruby-18mode
7
+ - 2.1.2
8
+ - rbx-2
12
9
  - jruby-19mode
13
10
  - ruby-head
14
- - jruby-head
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.0
4
+
5
+ - Remove support for Ruby 1.8
6
+ - Add support for sending custom headers for Client
7
+ - Better detection and handling of draft 76
8
+ - Multiple small fixes and optimizations
9
+
3
10
  ## 1.1.4
4
11
 
5
12
  - verify valid close codes according to spec
data/Gemfile CHANGED
@@ -3,4 +3,5 @@ source "http://rubygems.org"
3
3
  gemspec
4
4
 
5
5
  gem 'rake'
6
- gem 'rspec', '~> 2.11'
6
+ gem 'rspec', '~> 3.0.0'
7
+ gem 'rspec-its'
data/README.md CHANGED
@@ -1,10 +1,13 @@
1
1
  # WebSocket Ruby
2
2
 
3
- - Travis CI build: [![](https://travis-ci.org/imanel/websocket-ruby.png)](http://travis-ci.org/imanel/websocket-ruby)
4
- - Autobahn tests: [server](http://imanel.github.com/websocket-ruby/autobahn/server/), [client](http://imanel.github.com/websocket-ruby/autobahn/client/)
5
-
6
3
  Universal Ruby library to handle WebSocket protocol. It focuses on providing abstraction layer over [WebSocket API](http://dev.w3.org/html5/websockets/) instead of providing server or client functionality.
7
4
 
5
+ [![Gem Version](https://badge.fury.io/rb/websocket.svg)](http://badge.fury.io/rb/websocket)
6
+ [![Travis CI](https://travis-ci.org/imanel/websocket-ruby.png)](http://travis-ci.org/imanel/websocket-ruby)
7
+ [![Code Climate](https://codeclimate.com/github/imanel/websocket-ruby.png)](https://codeclimate.com/github/imanel/websocket-ruby)
8
+
9
+ **Autobahn tests:** [server](http://imanel.github.com/websocket-ruby/autobahn/server/), [client](http://imanel.github.com/websocket-ruby/autobahn/client/)
10
+
8
11
  Currently WebSocket Ruby supports all existing drafts of WebSocket, which include:
9
12
 
10
13
  - [hixie-75](http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75)
@@ -59,13 +62,14 @@ EOF
59
62
  ## Client handshake
60
63
 
61
64
  ``` ruby
62
- @handshake = WebSocket::Handshake::Client.new(:url => 'ws://example.com')
65
+ @handshake = WebSocket::Handshake::Client.new(url: 'ws://example.com', headers: { 'Cookie' => 'SESSIONID=1234' })
63
66
 
64
67
  # Create request
65
68
  @handshake.to_s # GET /demo HTTP/1.1
66
69
  # Upgrade: websocket
67
70
  # Connection: Upgrade
68
71
  # Host: example.com
72
+ # Cookie: SESSIONID=1234
69
73
  # Sec-WebSocket-Origin: http://example.com
70
74
  # Sec-WebSocket-Version: 13
71
75
  # Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
@@ -90,11 +94,11 @@ EOF
90
94
 
91
95
  ``` ruby
92
96
  # Prepare frame for sending
93
- frame = WebSocket::Frame::Outgoing::Server.new(:version => @handshake.version, :data => "Hello", :type => :text)
97
+ frame = WebSocket::Frame::Outgoing::Server.new(version: @handshake.version, data: "Hello", type: :text)
94
98
  frame.to_s # "\x81\x05\x48\x65\x6c\x6c\x6f"
95
99
 
96
100
  # Parse incoming frames
97
- frame = WebSocket::Frame::Incoming::Server.new(:version => @handshake.version)
101
+ frame = WebSocket::Frame::Incoming::Server.new(version: @handshake.version)
98
102
  frame << "\x81\x05\x48\x65\x6c\x6c\x6f\x81\x06\x77\x6f\x72\x6c\x64\x21"
99
103
  frame.next # "Hello"
100
104
  frame.next # "world!""
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ RSpec::Core::RakeTask.new do |t|
8
8
  t.pattern = 'spec/**/*_spec.rb'
9
9
  end
10
10
 
11
- task :default => :spec
11
+ task default: :spec
12
12
 
13
13
  namespace :autobahn do
14
14
  desc "Run autobahn tests for client"
data/lib/websocket.rb CHANGED
@@ -39,6 +39,6 @@ end
39
39
 
40
40
  # Try loading websocket-native if available
41
41
  begin
42
- require "websocket-native"
42
+ require 'websocket-native'
43
43
  rescue LoadError
44
44
  end
@@ -17,6 +17,12 @@ module WebSocket
17
17
 
18
18
  module ClassMethods
19
19
 
20
+ # Rescue from WebSocket::Error errors.
21
+ #
22
+ # @param [String] method_name Name of method that should be wrapped and rescued
23
+ # @param [Hash] options Options for rescue
24
+ #
25
+ # @options options [Any] :return Value that should be returned instead of raised error
20
26
  def rescue_method(method_name, options = {})
21
27
  define_method "#{method_name}_with_rescue" do |*args|
22
28
  begin
@@ -41,7 +41,7 @@ module WebSocket
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(", ")
44
+ vars = self.instance_variables.map { |v| "#{v}=#{instance_variable_get(v).inspect}" }.join(', ')
45
45
  insp = "#{self.class}:0x%08x" % (self.__id__ * 2)
46
46
  "<#{insp} #{vars}>"
47
47
  end
@@ -3,35 +3,37 @@ module WebSocket
3
3
  class Data < String
4
4
 
5
5
  def initialize(*args)
6
- super *args.each { |arg| arg.force_encoding('ASCII-8BIT') if respond_to?(:force_encoding) }
6
+ super(*convert_args(args))
7
7
  end
8
8
 
9
9
  def <<(*args)
10
- super *args.each { |arg| arg.force_encoding('ASCII-8BIT') if respond_to?(:force_encoding) }
10
+ super(*convert_args(args))
11
11
  end
12
12
 
13
+ # Convert all arguments to ASCII-8BIT for easier traversing
14
+ def convert_args(args)
15
+ args.each { |arg| arg.force_encoding('ASCII-8BIT') }
16
+ end
17
+
18
+ # Extract mask from 4 first bytes according to spec
13
19
  def set_mask
14
20
  raise WebSocket::Error::Frame::MaskTooShort if bytesize < 4
15
21
  @masking_key = self[0..3].bytes.to_a
16
22
  end
17
23
 
24
+ # Remove mask flag - it will still be present in payload
18
25
  def unset_mask
19
26
  @masking_key = nil
20
27
  end
21
28
 
29
+ # Extract `count` bytes starting from `start_index` and unmask it if needed.
22
30
  def getbytes(start_index, count)
23
31
  data = self[start_index, count]
24
32
  data = mask(data.bytes.to_a, @masking_key).pack('C*') if @masking_key
25
33
  data
26
34
  end
27
35
 
28
- # Required for support of Ruby 1.8
29
- unless new.respond_to?(:getbyte)
30
- def getbyte(index)
31
- self[index]
32
- end
33
- end
34
-
36
+ # Mask whole payload using mask key
35
37
  def mask(payload, mask)
36
38
  return mask_native(payload, mask) if respond_to?(:mask_native)
37
39
  result = []
@@ -2,7 +2,7 @@ module WebSocket
2
2
  module Frame
3
3
  module Handler
4
4
 
5
- autoload :Base, "#{::WebSocket::ROOT}/websocket/frame/handler/base"
5
+ autoload :Base, "#{::WebSocket::ROOT}/websocket/frame/handler/base"
6
6
 
7
7
  autoload :Handler03, "#{::WebSocket::ROOT}/websocket/frame/handler/handler03"
8
8
  autoload :Handler04, "#{::WebSocket::ROOT}/websocket/frame/handler/handler04"
@@ -7,12 +7,12 @@ module WebSocket
7
7
 
8
8
  # Hash of frame names and it's opcodes
9
9
  FRAME_TYPES = {
10
- :continuation => 0,
11
- :close => 1,
12
- :ping => 2,
13
- :pong => 3,
14
- :text => 4,
15
- :binary => 5
10
+ continuation: 0,
11
+ close: 1,
12
+ ping: 2,
13
+ pong: 3,
14
+ text: 4,
15
+ binary: 5
16
16
  }
17
17
 
18
18
  # Hash of frame opcodes and it's names
@@ -37,12 +37,12 @@ module WebSocket
37
37
  if length <= 125
38
38
  byte2 = length # since rsv4 is 0
39
39
  frame << (byte2 | mask)
40
- elsif length < 65536 # write 2 byte length
40
+ elsif length < 65_536 # write 2 byte length
41
41
  frame << (126 | mask)
42
42
  frame << [length].pack('n')
43
43
  else # write 8 byte length
44
44
  frame << (127 | mask)
45
- frame << [length >> 32, length & 0xFFFFFFFF].pack("NN")
45
+ frame << [length >> 32, length & 0xFFFFFFFF].pack('NN')
46
46
  end
47
47
 
48
48
  if @frame.outgoing_masking?
@@ -83,16 +83,16 @@ module WebSocket
83
83
  payload_length = case length
84
84
  when 127 # Length defined by 8 bytes
85
85
  # Check buffer size
86
- return if @frame.data.getbyte(pointer+8-1) == nil # Buffer incomplete
86
+ return if @frame.data.getbyte(pointer + 8 - 1) == nil # Buffer incomplete
87
87
 
88
88
  # Only using the last 4 bytes for now, till I work out how to
89
89
  # unpack 8 bytes. I'm sure 4GB frames will do for now :)
90
- l = @frame.data.getbytes(pointer+4, 4).unpack('N').first
90
+ l = @frame.data.getbytes(pointer + 4, 4).unpack('N').first
91
91
  pointer += 8
92
92
  l
93
93
  when 126 # Length defined by 2 bytes
94
94
  # Check buffer size
95
- return if @frame.data.getbyte(pointer+2-1) == nil # Buffer incomplete
95
+ return if @frame.data.getbyte(pointer + 2 - 1) == nil # Buffer incomplete
96
96
 
97
97
  l = @frame.data.getbytes(pointer, 2).unpack('n').first
98
98
  pointer += 2
@@ -108,7 +108,7 @@ module WebSocket
108
108
  raise(WebSocket::Error::Frame::TooLong) if frame_length > WebSocket.max_frame_size
109
109
 
110
110
  # Check buffer size
111
- return if @frame.data.getbyte(frame_length-1) == nil # Buffer incomplete
111
+ return if @frame.data.getbyte(frame_length - 1) == nil # Buffer incomplete
112
112
 
113
113
  # Remove frame header
114
114
  @frame.data.slice!(0...pointer)
@@ -118,7 +118,7 @@ module WebSocket
118
118
  @frame.data.set_mask if mask
119
119
  pointer += 4 if mask
120
120
  application_data = @frame.data.getbytes(pointer, payload_length)
121
- application_data.force_encoding('UTF-8') if application_data.respond_to?(:force_encoding)
121
+ application_data.force_encoding('UTF-8')
122
122
  pointer += payload_length
123
123
  @frame.data.unset_mask if mask
124
124
 
@@ -136,14 +136,14 @@ module WebSocket
136
136
  if frame_type == :continuation
137
137
  @application_data_buffer << application_data
138
138
  # Test valid UTF-8 encoding
139
- raise(WebSocket::Error::Frame::InvalidPayloadEncoding) if @frame_type == :text && @application_data_buffer.respond_to?(:valid_encoding?) && !@application_data_buffer.valid_encoding?
140
- message = @frame.class.new(:version => @frame.version, :type => @frame_type, :data => @application_data_buffer, :decoded => true)
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
141
  @application_data_buffer = nil
142
142
  @frame_type = nil
143
143
  return message
144
144
  else
145
- raise(WebSocket::Error::Frame::InvalidPayloadEncoding) if frame_type == :text && application_data.respond_to?(:valid_encoding?) && !application_data.valid_encoding?
146
- return @frame.class.new(:version => @frame.version, :type => frame_type, :data => application_data, :decoded => true)
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
147
  end
148
148
  end
149
149
  end
@@ -7,12 +7,12 @@ module WebSocket
7
7
 
8
8
  # Hash of frame names and it's opcodes
9
9
  FRAME_TYPES = {
10
- :continuation => 0,
11
- :text => 1,
12
- :binary => 2,
13
- :close => 8,
14
- :ping => 9,
15
- :pong => 10,
10
+ continuation: 0,
11
+ text: 1,
12
+ binary: 2,
13
+ close: 8,
14
+ ping: 9,
15
+ pong: 10,
16
16
  }
17
17
 
18
18
  # Hash of frame opcodes and it's names
@@ -42,11 +42,11 @@ module WebSocket
42
42
  private
43
43
 
44
44
  def valid_code?(code)
45
- [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).include?(code)
46
46
  end
47
47
 
48
48
  def valid_encoding?(data)
49
- return true if data.nil? || !data.respond_to?(:encode)
49
+ return true if data.nil?
50
50
  data.encode('UTF-8')
51
51
  true
52
52
  rescue
@@ -16,8 +16,9 @@ module WebSocket
16
16
  when :close then "\xff\x00"
17
17
  when :text then
18
18
  ary = ["\x00", @frame.data, "\xff"]
19
- ary.collect{ |s| s.encode('UTF-8', 'UTF-8', :invalid => :replace) if s.respond_to?(:encode) }
19
+ ary.map { |s| s.encode('UTF-8', 'UTF-8', invalid: :replace) }
20
20
  ary.join
21
+ else raise WebSocket::Error::Frame::UnknownFrameType
21
22
  end
22
23
  end
23
24
 
@@ -34,7 +35,7 @@ module WebSocket
34
35
  length = 0
35
36
 
36
37
  loop do
37
- return if !@frame.data.getbyte(pointer)
38
+ return unless @frame.data.getbyte(pointer)
38
39
  b = @frame.data.getbyte(pointer)
39
40
  pointer += 1
40
41
  b_v = b & 0x7F
@@ -44,15 +45,15 @@ module WebSocket
44
45
 
45
46
  raise WebSocket::Error::Frame::TooLong if length > ::WebSocket.max_frame_size
46
47
 
47
- unless @frame.data.getbyte(pointer+length-1) == nil
48
+ unless @frame.data.getbyte(pointer + length - 1) == nil
48
49
  # Straight from spec - I'm sure this isn't crazy...
49
50
  # 6. Read /length/ bytes.
50
51
  # 7. Discard the read bytes.
51
- @frame.instance_variable_set '@data', @frame.data[(pointer+length)..-1]
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
55
  if length == 0
55
- @frame.class.new(:version => @frame.version, :type => :close, :decoded => true)
56
+ @frame.class.new(version: @frame.version, type: :close, decoded: true)
56
57
  end
57
58
  end
58
59
  else
@@ -66,8 +67,8 @@ module WebSocket
66
67
  msg = @frame.data.slice!(/\A\x00[^\xff]*\xff/)
67
68
  if msg
68
69
  msg.gsub!(/\A\x00|\xff\z/, '')
69
- msg.force_encoding('UTF-8') if msg.respond_to?(:force_encoding)
70
- @frame.class.new(:version => @frame.version, :type => :text, :data => msg, :decoded => true)
70
+ msg.force_encoding('UTF-8')
71
+ @frame.class.new(version: @frame.version, type: :text, data: msg, decoded: true)
71
72
  end
72
73
  end
73
74
  end
@@ -4,7 +4,7 @@ module WebSocket
4
4
  # @note You should NEVER use this class directly - use Client or Server subclasses instead, as they contain additional frame options(i.e. Client-side masking in draft 04)
5
5
  #
6
6
  # @example
7
- # frame = WebSocket::Frame::Incoming::Server.new(:version => @handshake.version)
7
+ # frame = WebSocket::Frame::Incoming::Server.new(version: @handshake.version)
8
8
  # frame << "\x81\x05\x48\x65\x6c\x6c\x6f\x81\x06\x77\x6f\x72\x6c\x64\x21"
9
9
  # frame.next # "Hello"
10
10
  # frame.next # "world!""
@@ -4,7 +4,7 @@ module WebSocket
4
4
  # @note You should NEVER use this class directly - use Client or Server subclasses instead, as they contain additional frame options(i.e. Client-side masking in draft 04)
5
5
  #
6
6
  # @example
7
- # frame = WebSocket::Frame::Outgoing::Server.new(:version => @handshake.version, :data => "Hello", :type => :text)
7
+ # frame = WebSocket::Frame::Outgoing::Server.new(version: @handshake.version, data: "Hello", type: :text)
8
8
  # frame.to_s # "\x81\x05\x48\x65\x6c\x6c\x6f"
9
9
  class Outgoing < Base
10
10
 
@@ -12,7 +12,7 @@ module WebSocket
12
12
  @state = :new
13
13
  @handler = nil
14
14
 
15
- @data = ""
15
+ @data = ''
16
16
  @headers = {}
17
17
  end
18
18
 
@@ -24,13 +24,13 @@ module WebSocket
24
24
  # Return textual representation of handshake request or response
25
25
  # @return [String] text of response
26
26
  def to_s
27
- @handler ? @handler.to_s : ""
27
+ @handler ? @handler.to_s : ''
28
28
  end
29
- rescue_method :to_s, :return => ""
29
+ rescue_method :to_s, return: ''
30
30
 
31
31
  # Recreate inspect as #to_s was overwritten
32
32
  def inspect
33
- vars = self.instance_variables.map{|v| "#{v}=#{instance_variable_get(v).inspect}"}.join(", ")
33
+ vars = self.instance_variables.map { |v| "#{v}=#{instance_variable_get(v).inspect}" }.join(', ')
34
34
  insp = "#{self.class}:0x%08x" % (self.__id__ * 2)
35
35
  "<#{insp} #{vars}>"
36
36
  end
@@ -46,7 +46,7 @@ module WebSocket
46
46
  def valid?
47
47
  finished? && @error == nil && @handler && @handler.valid?
48
48
  end
49
- rescue_method :valid?, :return => false
49
+ rescue_method :valid?, return: false
50
50
 
51
51
  # @abstract Should send data after parsing is finished?
52
52
  def should_respond?
@@ -56,7 +56,7 @@ module WebSocket
56
56
  # Data left from parsing. Sometimes data that doesn't belong to handshake are added - use this method to retrieve them.
57
57
  # @return [String] String if some data are available. Nil otherwise
58
58
  def leftovers
59
- @leftovers.split("\n", reserved_leftover_lines + 1)[reserved_leftover_lines]
59
+ @leftovers.to_s.split("\n", reserved_leftover_lines + 1)[reserved_leftover_lines]
60
60
  end
61
61
 
62
62
  # URI of request.
@@ -64,7 +64,7 @@ module WebSocket
64
64
  # @example
65
65
  # @handshake.uri #=> "ws://example.com/path?query=true"
66
66
  def uri
67
- uri = secure ? "wss://" : "ws://"
67
+ uri = secure ? 'wss://' : 'ws://'
68
68
  uri << host
69
69
  uri << ":#{port}" if port
70
70
  uri << path
@@ -5,7 +5,7 @@ module WebSocket
5
5
  # Construct or parse a client WebSocket handshake.
6
6
  #
7
7
  # @example
8
- # @handshake = WebSocket::Handshake::Client.new(:url => 'ws://example.com')
8
+ # @handshake = WebSocket::Handshake::Client.new(url: 'ws://example.com')
9
9
  #
10
10
  # # Create request
11
11
  # @handshake.to_s # GET /demo HTTP/1.1
@@ -33,7 +33,7 @@ module WebSocket
33
33
  #
34
34
  class Client < Base
35
35
 
36
- attr_reader :origin
36
+ attr_reader :origin, :headers
37
37
 
38
38
  # Initialize new WebSocket Client
39
39
  #
@@ -48,14 +48,16 @@ module WebSocket
48
48
  # @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.
49
49
  # @option args [String] :uri Alias to :url
50
50
  # @option args [Integer] :version Version of WebSocket to use. Default: 13 (this is version from RFC)
51
+ # @option args [Hash] :headers HTTP headers to use in the handshake
51
52
  #
52
53
  # @example
53
- # Websocket::Handshake::Client.new(:url => "ws://example.com/path?query=true")
54
+ # Websocket::Handshake::Client.new(url: "ws://example.com/path?query=true")
54
55
  def initialize(args = {})
55
56
  super
56
57
 
57
58
  @version = args[:version] || DEFAULT_VERSION
58
59
  @origin = args[:origin]
60
+ @headers = args[:headers] || {}
59
61
 
60
62
  if args[:url] || args[:uri]
61
63
  uri = URI.parse(args[:url] || args[:uri])