websocket-driver 0.3.5-java → 0.4.0-java

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 24db628b86446bcd2f11741520ffb9a9938a6995
4
- data.tar.gz: ebe19156acc768b1696c571cdab8e02fe07428c7
3
+ metadata.gz: 79be3fa7f1a4429fda1463f0ab0f809004b82593
4
+ data.tar.gz: eed2e51b0a21fade327b48c4dab184afb92cc7dd
5
5
  SHA512:
6
- metadata.gz: 0ad1e18fe373697fdee0d438d5522cb11ee6524de7a136c4c8a72c77b7ca1be002f1bb1141850f493a26913a8f3a1dbfabf9d466f434b857e1d0653c5168e568
7
- data.tar.gz: a4637d8e37031dbb633489dc9534764e670416898ba3d83b00ca90701d43581125f01f042a243cb3033afe093dd72107921ffcc4e569c665e1c087c59cdb8218
6
+ metadata.gz: 1f718f23af5483c2c30995c63b2d40a64d2077cc23fd1d0e34e551a9f7d06618883a7a45b5d884c4d334d1d7036263b93e52c8249bcee84146295fe8c8cd0506
7
+ data.tar.gz: f0486b76187ee8a9d750621aac8498ac4dd4b4ab7f2f8d9a19e7af9b6c35532cab2ebdfd25b17ed38721eccb4d918bc8adc7c0c19f2eb70d914d34f79cd2bec4
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ### 0.4.0 / 2014-11-08
2
+
3
+ * Support connection via HTTP proxies using `CONNECT`
4
+
1
5
  ### 0.3.5 / 2014-10-04
2
6
 
3
7
  * Fix bug where the `Server` driver doesn't pass `ping` callbacks to its delegate
data/README.md CHANGED
@@ -193,6 +193,51 @@ sent back by the server:
193
193
  * `driver.headers` - a hash-like object containing the response headers
194
194
 
195
195
 
196
+ ### HTTP Proxies
197
+
198
+ The client driver supports connections via HTTP proxies using the `CONNECT`
199
+ method. Instead of sending the WebSocket handshake immediately, it will send a
200
+ `CONNECT` request, wait for a `200` response, and then proceed as normal.
201
+
202
+ To use this feature, call `proxy = driver.proxy(url)` where `url` is the origin
203
+ of the proxy, including a username and password if required. This produces an
204
+ object that manages the process of connecting via the proxy. You should call
205
+ `proxy.start` to begin the connection process, and pass data you receive via the
206
+ socket to `proxy.parse(data)`. When the proxy emits `:connect`, you should then
207
+ start sending incoming data to `driver.parse(data)` as normal, and call
208
+ `driver.start`.
209
+
210
+ ```rb
211
+ proxy = driver.proxy('http://username:password@proxy.example.com')
212
+
213
+ proxy.on :connect do
214
+ driver.start
215
+ end
216
+ ```
217
+
218
+ The proxy's `:connect` event is also where you should perform a TLS handshake on
219
+ your TCP stream, if you are connecting to a `wss:` endpoint.
220
+
221
+ In the event that proxy connection fails, `proxy` will emit an `:error`. You can
222
+ inspect the proxy's response via `proxy.status` and `proxy.headers`.
223
+
224
+ ```rb
225
+ proxy.on :error do |error|
226
+ puts error.message
227
+ puts proxy.status
228
+ puts proxy.headers.inspect
229
+ end
230
+ ```
231
+
232
+ Before calling `proxy.start` you can set custom headers using
233
+ `proxy.set_header`:
234
+
235
+ ```rb
236
+ proxy.set_header('User-Agent', 'ruby')
237
+ proxy.start
238
+ ```
239
+
240
+
196
241
  ### Driver API
197
242
 
198
243
  Drivers are created using one of the following methods:
@@ -13,19 +13,20 @@ void Init_websocket_mask() {
13
13
  }
14
14
 
15
15
  VALUE method_websocket_mask(VALUE self, VALUE payload, VALUE mask) {
16
+ int n, i, p, m;
17
+ int mask_array[4];
18
+ VALUE unmasked;
19
+
16
20
  if (mask == Qnil || RARRAY_LEN(mask) == 0) {
17
21
  return payload;
18
22
  }
19
23
 
20
- int n = RARRAY_LEN(payload), i, p, m;
21
- VALUE unmasked = rb_ary_new2(n);
24
+ n = RARRAY_LEN(payload);
25
+ unmasked = rb_ary_new2(n);
22
26
 
23
- int mask_array[] = {
24
- NUM2INT(rb_ary_entry(mask, 0)),
25
- NUM2INT(rb_ary_entry(mask, 1)),
26
- NUM2INT(rb_ary_entry(mask, 2)),
27
- NUM2INT(rb_ary_entry(mask, 3))
28
- };
27
+ for (i = 0; i < 4; i++) {
28
+ mask_array[i] = NUM2INT(rb_ary_entry(mask, i));
29
+ }
29
30
 
30
31
  for (i = 0; i < n; i++) {
31
32
  p = NUM2INT(rb_ary_entry(payload, i));
@@ -51,6 +51,7 @@ module WebSocket
51
51
  autoload :EventEmitter, root + '/event_emitter'
52
52
  autoload :Headers, root + '/headers'
53
53
  autoload :Hybi, root + '/hybi'
54
+ autoload :Proxy, root + '/proxy'
54
55
  autoload :Server, root + '/server'
55
56
 
56
57
  include EventEmitter
@@ -15,12 +15,36 @@ module WebSocket
15
15
  @key = Client.generate_key
16
16
  @accept = Hybi.generate_accept(@key)
17
17
  @http = HTTP::Response.new
18
+
19
+ uri = URI.parse(@socket.url)
20
+ host = uri.host + (uri.port ? ":#{uri.port}" : '')
21
+ path = (uri.path == '') ? '/' : uri.path
22
+ @pathname = path + (uri.query ? '?' + uri.query : '')
23
+
24
+ @headers['Host'] = host
25
+ @headers['Upgrade'] = 'websocket'
26
+ @headers['Connection'] = 'Upgrade'
27
+ @headers['Sec-WebSocket-Key'] = @key
28
+ @headers['Sec-WebSocket-Version'] = '13'
29
+
30
+ if @protocols.size > 0
31
+ @headers['Sec-WebSocket-Protocol'] = @protocols * ', '
32
+ end
33
+
34
+ if uri.user
35
+ auth = Base64.encode64([uri.user, uri.password] * ':').gsub(/\n/, '')
36
+ @headers['Authorization'] = 'Basic ' + auth
37
+ end
18
38
  end
19
39
 
20
40
  def version
21
41
  'hybi-13'
22
42
  end
23
43
 
44
+ def proxy(origin, options = {})
45
+ Proxy.new(self, origin, options)
46
+ end
47
+
24
48
  def start
25
49
  return false unless @ready_state == -1
26
50
  @socket.write(Driver.encode(handshake_request, :binary))
@@ -30,8 +54,10 @@ module WebSocket
30
54
 
31
55
  def parse(buffer)
32
56
  return super if @ready_state > 0
57
+
33
58
  @http.parse(buffer)
34
59
  return fail_handshake('Invalid HTTP response') if @http.error?
60
+
35
61
  validate_handshake if @http.complete?
36
62
  parse(@http.body) if @ready_state == 1
37
63
  end
@@ -39,29 +65,9 @@ module WebSocket
39
65
  private
40
66
 
41
67
  def handshake_request
42
- uri = URI.parse(@socket.url)
43
- host = uri.host + (uri.port ? ":#{uri.port}" : '')
44
- path = (uri.path == '') ? '/' : uri.path
45
- query = uri.query ? "?#{uri.query}" : ''
46
-
47
- headers = [ "GET #{path}#{query} HTTP/1.1",
48
- "Host: #{host}",
49
- "Upgrade: websocket",
50
- "Connection: Upgrade",
51
- "Sec-WebSocket-Key: #{@key}",
52
- "Sec-WebSocket-Version: 13"
53
- ]
54
-
55
- if @protocols.size > 0
56
- headers << "Sec-WebSocket-Protocol: #{@protocols * ', '}"
57
- end
58
-
59
- if uri.user
60
- auth = Base64.encode64([uri.user, uri.password] * ':').gsub(/\n/, '')
61
- headers << "Authorization: Basic #{auth}"
62
- end
63
-
64
- (headers + [@headers.to_s, '']).join("\r\n")
68
+ start = "GET #{@pathname} HTTP/1.1"
69
+ headers = [start, @headers.to_s, '']
70
+ headers.join("\r\n")
65
71
  end
66
72
 
67
73
  def fail_handshake(message)
@@ -5,6 +5,11 @@ module WebSocket
5
5
  def initialize(socket, options = {})
6
6
  super
7
7
  @stage = 0
8
+
9
+ @headers['Upgrade'] = 'WebSocket'
10
+ @headers['Connection'] = 'Upgrade'
11
+ @headers['WebSocket-Origin'] = @socket.env['HTTP_ORIGIN']
12
+ @headers['WebSocket-Location'] = @socket.url
8
13
  end
9
14
 
10
15
  def version
@@ -73,14 +78,9 @@ module WebSocket
73
78
  private
74
79
 
75
80
  def handshake_response
76
- upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
77
- upgrade << "Upgrade: WebSocket\r\n"
78
- upgrade << "Connection: Upgrade\r\n"
79
- upgrade << "WebSocket-Origin: #{@socket.env['HTTP_ORIGIN']}\r\n"
80
- upgrade << "WebSocket-Location: #{@socket.url}\r\n"
81
- upgrade << @headers.to_s
82
- upgrade << "\r\n"
83
- upgrade
81
+ start = 'HTTP/1.1 101 Web Socket Protocol Handshake'
82
+ headers = [start, @headers.to_s, '']
83
+ headers.join("\r\n")
84
84
  end
85
85
 
86
86
  def parse_leading_byte(data)
@@ -9,6 +9,12 @@ module WebSocket
9
9
  input = @socket.env['rack.input']
10
10
  @stage = -1
11
11
  @body = input ? input.read.bytes.to_a : []
12
+
13
+ @headers.clear
14
+ @headers['Upgrade'] = 'WebSocket'
15
+ @headers['Connection'] = 'Upgrade'
16
+ @headers['Sec-WebSocket-Origin'] = @socket.env['HTTP_ORIGIN']
17
+ @headers['Sec-WebSocket-Location'] = @socket.url
12
18
  end
13
19
 
14
20
  def version
@@ -32,14 +38,9 @@ module WebSocket
32
38
  private
33
39
 
34
40
  def handshake_response
35
- upgrade = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
36
- upgrade << "Upgrade: WebSocket\r\n"
37
- upgrade << "Connection: Upgrade\r\n"
38
- upgrade << "Sec-WebSocket-Origin: #{@socket.env['HTTP_ORIGIN']}\r\n"
39
- upgrade << "Sec-WebSocket-Location: #{@socket.url}\r\n"
40
- upgrade << @headers.to_s
41
- upgrade << "\r\n"
42
- upgrade
41
+ start = 'HTTP/1.1 101 WebSocket Protocol Handshake'
42
+ headers = [start, @headers.to_s, '']
43
+ headers.join("\r\n")
43
44
  end
44
45
 
45
46
  def handshake_signature
@@ -6,13 +6,17 @@ module WebSocket
6
6
 
7
7
  def initialize(received = {})
8
8
  @raw = received
9
- @sent = Set.new
10
- @lines = []
9
+ clear
11
10
 
12
11
  @received = {}
13
12
  @raw.each { |k,v| @received[HTTP.normalize_header(k)] = v }
14
13
  end
15
14
 
15
+ def clear
16
+ @sent = Set.new
17
+ @lines = []
18
+ end
19
+
16
20
  def [](name)
17
21
  @received[HTTP.normalize_header(name)]
18
22
  end
@@ -62,9 +62,17 @@ module WebSocket
62
62
 
63
63
  return unless @socket.respond_to?(:env)
64
64
 
65
+ sec_key = @socket.env['HTTP_SEC_WEBSOCKET_KEY']
66
+ protos = @socket.env['HTTP_SEC_WEBSOCKET_PROTOCOL']
67
+
68
+ @headers['Upgrade'] = 'websocket'
69
+ @headers['Connection'] = 'Upgrade'
70
+ @headers['Sec-WebSocket-Accept'] = Hybi.generate_accept(sec_key)
71
+
65
72
  if protos = @socket.env['HTTP_SEC_WEBSOCKET_PROTOCOL']
66
73
  protos = protos.split(/\s*,\s*/) if String === protos
67
74
  @protocol = protos.find { |p| @protocols.include?(p) }
75
+ @headers['Sec-WebSocket-Protocol'] = @protocol if @protocol
68
76
  end
69
77
  end
70
78
 
@@ -197,21 +205,9 @@ module WebSocket
197
205
  private
198
206
 
199
207
  def handshake_response
200
- sec_key = @socket.env['HTTP_SEC_WEBSOCKET_KEY']
201
- return '' unless String === sec_key
202
-
203
- headers = [
204
- "HTTP/1.1 101 Switching Protocols",
205
- "Upgrade: websocket",
206
- "Connection: Upgrade",
207
- "Sec-WebSocket-Accept: #{Hybi.generate_accept(sec_key)}"
208
- ]
209
-
210
- if @protocol
211
- headers << "Sec-WebSocket-Protocol: #{@protocol}"
212
- end
213
-
214
- (headers + [@headers.to_s, '']).join("\r\n")
208
+ start = 'HTTP/1.1 101 Switching Protocols'
209
+ headers = [start, @headers.to_s, '']
210
+ headers.join("\r\n")
215
211
  end
216
212
 
217
213
  def shutdown(code, reason)
@@ -0,0 +1,68 @@
1
+ module WebSocket
2
+ class Driver
3
+
4
+ class Proxy
5
+ include EventEmitter
6
+
7
+ PORTS = {'ws' => 80, 'wss' => 443}
8
+
9
+ attr_reader :status, :headers
10
+
11
+ def initialize(client, origin, options)
12
+ super()
13
+
14
+ @client = client
15
+ @http = HTTP::Response.new
16
+ @socket = client.instance_variable_get(:@socket)
17
+ @origin = URI.parse(@socket.url)
18
+ @url = URI.parse(origin)
19
+ @options = options
20
+ @state = 0
21
+
22
+ @headers = Headers.new
23
+ @headers['Host'] = @origin.host + (@origin.port ? ":#{@origin.port}" : '')
24
+ @headers['Connection'] = 'keep-alive'
25
+ @headers['Proxy-Connection'] = 'keep-alive'
26
+
27
+ if @url.user
28
+ auth = Base64.encode64([@url.user, @url.password] * ':').gsub(/\n/, '')
29
+ @headers['Proxy-Authorization'] = 'Basic ' + auth
30
+ end
31
+ end
32
+
33
+ def set_header(name, value)
34
+ return false unless @state == 0
35
+ @headers[name] = value
36
+ true
37
+ end
38
+
39
+ def start
40
+ return false unless @state == 0
41
+ @state = 1
42
+
43
+ port = @origin.port || PORTS[@origin.scheme]
44
+ start = "CONNECT #{@origin.host}:#{port} HTTP/1.1"
45
+ headers = [start, @headers.to_s, '']
46
+
47
+ @socket.write(headers.join("\r\n"))
48
+ true
49
+ end
50
+
51
+ def parse(buffer)
52
+ @http.parse(buffer)
53
+ return unless @http.complete?
54
+
55
+ @status = @http.code
56
+ @headers = Headers.new(@http.headers)
57
+
58
+ if @status == 200
59
+ emit(:connect)
60
+ else
61
+ message = "Can't establish a connection to the server at #{@socket.url}"
62
+ emit(:error, ProtocolError.new(message))
63
+ end
64
+ end
65
+ end
66
+
67
+ end
68
+ end
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: websocket-driver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.4.0
5
5
  platform: java
6
6
  authors:
7
7
  - James Coglan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-04 00:00:00.000000000 Z
11
+ date: 2014-11-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: eventmachine
@@ -69,16 +69,17 @@ files:
69
69
  - lib/websocket/driver.rb
70
70
  - lib/websocket/http.rb
71
71
  - lib/websocket/http/request.rb
72
- - lib/websocket/http/headers.rb
73
72
  - lib/websocket/http/response.rb
74
- - lib/websocket/driver/utf8_match.rb
75
- - lib/websocket/driver/headers.rb
73
+ - lib/websocket/http/headers.rb
76
74
  - lib/websocket/driver/hybi.rb
77
- - lib/websocket/driver/server.rb
78
75
  - lib/websocket/driver/client.rb
76
+ - lib/websocket/driver/draft76.rb
77
+ - lib/websocket/driver/proxy.rb
78
+ - lib/websocket/driver/server.rb
79
79
  - lib/websocket/driver/draft75.rb
80
+ - lib/websocket/driver/headers.rb
81
+ - lib/websocket/driver/utf8_match.rb
80
82
  - lib/websocket/driver/event_emitter.rb
81
- - lib/websocket/driver/draft76.rb
82
83
  - lib/websocket/driver/hybi/stream_reader.rb
83
84
  - lib/websocket_mask.jar
84
85
  homepage: http://github.com/faye/websocket-driver-ruby