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 +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +45 -0
- data/ext/websocket-driver/websocket_mask.c +9 -8
- data/lib/websocket/driver.rb +1 -0
- data/lib/websocket/driver/client.rb +29 -23
- data/lib/websocket/driver/draft75.rb +8 -8
- data/lib/websocket/driver/draft76.rb +9 -8
- data/lib/websocket/driver/headers.rb +6 -2
- data/lib/websocket/driver/hybi.rb +11 -15
- data/lib/websocket/driver/proxy.rb +68 -0
- data/lib/websocket_mask.jar +0 -0
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 79be3fa7f1a4429fda1463f0ab0f809004b82593
|
4
|
+
data.tar.gz: eed2e51b0a21fade327b48c4dab184afb92cc7dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f718f23af5483c2c30995c63b2d40a64d2077cc23fd1d0e34e551a9f7d06618883a7a45b5d884c4d334d1d7036263b93e52c8249bcee84146295fe8c8cd0506
|
7
|
+
data.tar.gz: f0486b76187ee8a9d750621aac8498ac4dd4b4ab7f2f8d9a19e7af9b6c35532cab2ebdfd25b17ed38721eccb4d918bc8adc7c0c19f2eb70d914d34f79cd2bec4
|
data/CHANGELOG.md
CHANGED
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
|
-
|
21
|
-
|
24
|
+
n = RARRAY_LEN(payload);
|
25
|
+
unmasked = rb_ary_new2(n);
|
22
26
|
|
23
|
-
|
24
|
-
NUM2INT(rb_ary_entry(mask,
|
25
|
-
|
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));
|
data/lib/websocket/driver.rb
CHANGED
@@ -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
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
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
|
-
|
201
|
-
|
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
|
data/lib/websocket_mask.jar
CHANGED
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.
|
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-
|
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/
|
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
|