websocker 0.0.2 → 0.0.3
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.
- 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.
|