websocker 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 NotConnectedError < RuntimeError; end
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
- @sock.write handshake(key)
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
- message = read
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
- if data.size <= 125
63
- byte2 = data.size
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 data.size <= 65535
66
- byte2 = 0b10000000 | 126
72
+ elsif length <= 65535
73
+ byte2 = byte2 | 126
67
74
  write_byte(byte2)
68
75
  # write length in next two bytes
69
- @sock.write [length].pack('n') # 16-bit unsigned
76
+ write [length].pack('n') # 16-bit unsigned
70
77
  else
71
- byte2 = 0b10000000 | 127
78
+ byte2 = byte2 | 127
72
79
  write_byte(byte2)
73
80
  # write length in next eight bytes
74
- @sock.write [length].pack('Q') # 64-bit unsigned
81
+ write [length].pack('Q') # 64-bit unsigned
75
82
  end
76
-
77
- @sock.write(data)
78
- @sock.flush
79
-
80
- puts "Writing #{byte1}, #{byte2}, #{data}"
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
- @sock.write [byte].pack("C")
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
- @logger.debug "read_and_unpack_byte: #{byte}"
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
@@ -1,3 +1,3 @@
1
1
  module Websocker
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
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: 27
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 2
10
- version: 0.0.2
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-24 00:00:00 Z
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+turntable_api@gmail.com
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.16
63
+ rubygems_version: 1.8.17
64
64
  signing_key:
65
65
  specification_version: 3
66
66
  summary: Library for communicating with Websocket servers.