websocker 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/websocker/client.rb +37 -24
- data/lib/websocker/version.rb +1 -1
- metadata +6 -6
data/lib/websocker/client.rb
CHANGED
@@ -10,8 +10,7 @@ require "logger"
|
|
10
10
|
# implement a websocket client that speaks the hybi-10 protocol
|
11
11
|
module Websocker
|
12
12
|
class Client
|
13
|
-
class
|
14
|
-
|
13
|
+
class SocketReadError < RuntimeError; end
|
15
14
|
class HandshakeNegotiationError < RuntimeError; end
|
16
15
|
|
17
16
|
def initialize(opts = {})
|
@@ -21,14 +20,15 @@ module Websocker
|
|
21
20
|
@path = opts[:path] || "/"
|
22
21
|
@connected = false
|
23
22
|
@logger = opts[:logger] || Logger.new(STDOUT)
|
24
|
-
@logger.debug "Connecting to #{@host}:#{@port}"
|
23
|
+
@logger.debug "Connecting to #{@host}:#{@port}#{@path}"
|
25
24
|
end
|
26
25
|
|
27
26
|
def connect
|
28
27
|
@sock = TCPSocket.open(@host, @port)
|
28
|
+
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
|
29
29
|
@connected = true
|
30
30
|
key = generateKey
|
31
|
-
|
31
|
+
write handshake(key)
|
32
32
|
headers = read_headers
|
33
33
|
received_key = headers['Sec-WebSocket-Accept']
|
34
34
|
expected_key = expected_security_key_answer(key)
|
@@ -39,12 +39,17 @@ module Websocker
|
|
39
39
|
def listen
|
40
40
|
@loop = Thread.new do
|
41
41
|
while @connected
|
42
|
-
|
43
|
-
puts "listen: #{message}"
|
44
|
-
@on_message.call(message) unless @on_message.nil?
|
42
|
+
read_once
|
45
43
|
end
|
46
44
|
end
|
47
45
|
end
|
46
|
+
|
47
|
+
def read_once
|
48
|
+
message = read
|
49
|
+
return unless @connected
|
50
|
+
@logger.debug "received: #{message}"
|
51
|
+
@on_message.call(message) unless @on_message.nil?
|
52
|
+
end
|
48
53
|
|
49
54
|
def on_message(&blk)
|
50
55
|
@on_message = blk
|
@@ -59,31 +64,35 @@ module Websocker
|
|
59
64
|
write_byte(byte1)
|
60
65
|
|
61
66
|
# write length
|
62
|
-
|
63
|
-
|
67
|
+
byte2 = 0x80 # mask flag set to true
|
68
|
+
length = data.size
|
69
|
+
if length <= 125
|
70
|
+
byte2 = byte2 | length
|
64
71
|
write_byte(byte2)
|
65
|
-
elsif
|
66
|
-
byte2 =
|
72
|
+
elsif length <= 65535
|
73
|
+
byte2 = byte2 | 126
|
67
74
|
write_byte(byte2)
|
68
75
|
# write length in next two bytes
|
69
|
-
|
76
|
+
write [length].pack('n') # 16-bit unsigned
|
70
77
|
else
|
71
|
-
byte2 =
|
78
|
+
byte2 = byte2 | 127
|
72
79
|
write_byte(byte2)
|
73
80
|
# write length in next eight bytes
|
74
|
-
|
81
|
+
write [length].pack('Q') # 64-bit unsigned
|
75
82
|
end
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
83
|
+
# masking is required from the client to the server
|
84
|
+
masking_key = Array.new(4){ rand(256) }
|
85
|
+
write(masking_key.pack("C*"))
|
86
|
+
masked_data = apply_mask(data, masking_key)
|
87
|
+
# write masked message payload
|
88
|
+
write(masked_data)
|
89
|
+
@logger.debug "wrote: #{data}"
|
81
90
|
end
|
82
91
|
|
83
92
|
def close
|
84
93
|
@logger.debug "Connection closed"
|
85
94
|
@connected = false
|
86
|
-
@on_closed unless @on_closed.nil?
|
95
|
+
@on_closed.call unless @on_closed.nil?
|
87
96
|
end
|
88
97
|
|
89
98
|
private
|
@@ -139,14 +148,14 @@ module Websocker
|
|
139
148
|
|
140
149
|
# write an unsigned byte
|
141
150
|
def write_byte(byte)
|
142
|
-
|
151
|
+
write [byte].pack("C")
|
143
152
|
end
|
144
153
|
|
145
154
|
# fin: 1 bit, indicates this is the final fragment in a message
|
146
155
|
# rsv1, rsv2, rsv3: 1 bit, reserved, usually zero unless used by websocket extensions
|
147
156
|
# opcode: 4 bits; 0 continuation, 1 text, 2 bin, 8 closed
|
148
157
|
# mask: 1 bit, indicates payload is masked
|
149
|
-
# len: 7 bits, payload length
|
158
|
+
# len: 7 bits, payload length, may also use next 2 bytes if len==126, or next 8 bytes if len==127
|
150
159
|
# payload: variable
|
151
160
|
def read_frame
|
152
161
|
byte = read_and_unpack_byte
|
@@ -179,7 +188,6 @@ module Websocker
|
|
179
188
|
end
|
180
189
|
|
181
190
|
if mask then
|
182
|
-
@logger.debugmask
|
183
191
|
masking_key = @sock.read(4).unpack("C*")
|
184
192
|
end
|
185
193
|
|
@@ -201,8 +209,13 @@ module Websocker
|
|
201
209
|
# reads a byte and returns an 8-bit unsigned integer
|
202
210
|
def read_and_unpack_byte
|
203
211
|
byte = @sock.read(1)
|
204
|
-
|
212
|
+
raise SocketReadError if byte.nil?
|
205
213
|
byte = byte.unpack('C')[0]
|
206
214
|
end
|
215
|
+
|
216
|
+
def write(data)
|
217
|
+
@sock.write(data)
|
218
|
+
@sock.flush
|
219
|
+
end
|
207
220
|
end
|
208
221
|
end
|
data/lib/websocker/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: websocker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 3
|
10
|
+
version: 0.0.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Lawrence Mcalpin
|
@@ -15,12 +15,12 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-02-
|
18
|
+
date: 2012-02-27 00:00:00 Z
|
19
19
|
dependencies: []
|
20
20
|
|
21
21
|
description: A simple implementation of a Websocket client.
|
22
22
|
email:
|
23
|
-
- lmcalpin+
|
23
|
+
- lmcalpin+websocker@gmail.com
|
24
24
|
executables: []
|
25
25
|
|
26
26
|
extensions: []
|
@@ -60,7 +60,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
60
60
|
requirements: []
|
61
61
|
|
62
62
|
rubyforge_project: websocker
|
63
|
-
rubygems_version: 1.8.
|
63
|
+
rubygems_version: 1.8.17
|
64
64
|
signing_key:
|
65
65
|
specification_version: 3
|
66
66
|
summary: Library for communicating with Websocket servers.
|