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.
@@ -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.