socketio-client 0.0.1

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/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in socket.io-client.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # Simple Socket IO client
2
+
3
+ Quick and kinda dirty socket.io client using web sockets
4
+
5
+ ## Features
6
+
7
+ This client currently supports:
8
+
9
+ * Listeners for all 9 possible message
10
+ * Send messages of the type:
11
+ * message
12
+ * json
13
+ * event
14
+
15
+ ## How to use:
16
+
17
+ ```ruby
18
+ require 'socketIO'
19
+
20
+ client = SocketIO.connect("localhost") do
21
+ before_start do
22
+ on_message {|message| puts "incoming message: #{message}"}
23
+ on_event('news') { |data| puts data.first} # data is an array fo things.
24
+ end
25
+
26
+ end
27
+ ```
28
+
29
+ ## Sync vs Async
30
+
31
+ You can start the socket io syncronously and then continue with your work
32
+ this crates threads so be careful.
33
+
34
+ ```ruby
35
+ require 'socketIO'
36
+
37
+ client = SocketIO.connect("localhost", sync: true) do
38
+ before_start do
39
+ on_message {|message| puts message}
40
+ on_disconnect {puts "I GOT A DISCONNECT"}
41
+ end
42
+
43
+ after_start do
44
+ emit("loadLogs", "/var/www/rails_app/log/production.log")
45
+ end
46
+ end
47
+
48
+ puts "socket still running"
49
+ loop do
50
+ sleep 10
51
+ puts 'zzz'
52
+ end
53
+ ```
54
+
55
+ ## Examples
56
+
57
+ examples can be found in the examples/ folder.
58
+ A corrosponding server can be found in the examples/servers
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ desc "Run all specs"
5
+ RSpec::Core::RakeTask.new('spec') do |t|
6
+ t.rspec_opts = ['--colour --format documentation']
7
+ end
@@ -0,0 +1 @@
1
+ # not yet implemented
@@ -0,0 +1,14 @@
1
+ require 'socketIO'
2
+
3
+ client = SocketIO.connect("localhost") do
4
+ before_start do
5
+ on_message {|message| puts "incoming message: #{message}"}
6
+ on_disconnect {puts "I GOT A TDISCONNECT"}
7
+ end
8
+
9
+ after_start do
10
+ emit("loadLogs", "/Users/lyon/test/rails_app/log/development.log")
11
+ end
12
+ end
13
+
14
+ puts "thread exited and I have the power back"
@@ -0,0 +1 @@
1
+ // not yet implemented
@@ -0,0 +1,18 @@
1
+ var io = require('socket.io').listen(80)
2
+ ,spawn = require('child_process').spawn
3
+
4
+ console.log("Up and running. waiting on connections")
5
+ io.sockets.on('connection', function(socket){
6
+ console.log("new connection.")
7
+
8
+ socket.on("loadLogs", function(path){
9
+ var tail = spawn("tail", ["-F", path])
10
+
11
+ console.log("Begining tail on: " + path)
12
+ tail.stdout.on("data", function(data){
13
+ console.log(data.toString("utf8"))
14
+ socket.send(data.toString("utf8"))
15
+ })
16
+ })
17
+
18
+ })
@@ -0,0 +1,10 @@
1
+ var io = require('socket.io').listen(80);
2
+
3
+ io.sockets.on('connection', function (socket) {
4
+
5
+ setInterval(function(){
6
+ socket.send('got something for ya');
7
+ socket.emit('news', { hello: 'world'});
8
+ }, 10000)
9
+
10
+ });
data/example/simple.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'socketIO'
2
+
3
+ client = SocketIO.connect("localhost") do
4
+ before_start do
5
+ on_message {|message| puts "incoming message: #{message}"}
6
+ on_event('news') { |data| puts data.first} # data is an array fo things.
7
+ end
8
+
9
+ end
10
+
data/lib/SocketIO.rb ADDED
@@ -0,0 +1,169 @@
1
+ require 'web_socket'
2
+ require 'rest_client'
3
+ require 'json'
4
+ require 'parser'
5
+
6
+ module SocketIO
7
+
8
+ def self.connect(host, options = {}, &block)
9
+ response = RestClient.get "http://#{host}/socket.io/1/"
10
+ # resonse should be in the form of sessionid:heartbeattimeout:closetimeout:supported stuff
11
+ response_array = response.split(':')
12
+ response_array = [host] + response_array << options
13
+ cli = Client.new(*response_array)
14
+ cli.instance_eval(&block) if block
15
+ cli.start
16
+ end
17
+
18
+ class Client
19
+ VERSION = "0.0.1"
20
+
21
+ [:INT, :TERM].each do |sig|
22
+ Signal.trap(sig) do
23
+ puts
24
+ puts "bye"
25
+ exit
26
+ end
27
+ end
28
+
29
+ # The state of the Socket.IO socket can be disconnected, disconnecting, connected and connecting.
30
+ # The transport connection can be closed, closing, open, and opening.
31
+
32
+ def initialize(host, session_id, heartbeat_timeout, connection_timeout, supported_transports, options = {})
33
+ @host = host
34
+ @session_id = session_id
35
+ @hb_timeout = heartbeat_timeout
36
+ @connect_timeout = connection_timeout
37
+ @supported_transports = supported_transports
38
+ @options = options
39
+ @reconnect = options[:reconnect]
40
+ @on_event = {}
41
+ end
42
+
43
+ def start
44
+ self.instance_eval(&@before_start) if @before_start
45
+ connect_transport
46
+ start_recieve_loop
47
+ self.instance_eval(&@after_start) if @after_start
48
+ @thread.join unless @options[:sync]
49
+ self
50
+ end
51
+
52
+ def connect_transport
53
+ if @supported_transports.include? "websocket"
54
+ @transport = WebSocket.new("ws://#{@host}/socket.io/1/websocket/#{@session_id}")
55
+ else
56
+ raise "We only support WebSockets.. and this server doesnt like web sockets.. O NO!!"
57
+ end
58
+ end
59
+
60
+ def start_recieve_loop
61
+ @thread = Thread.new() do
62
+ while data = @transport.receive()
63
+ decoded = Parser.decode(data)
64
+ case decoded[:type]
65
+ when '0'
66
+ @on_disconnect.call if @on_disconnect
67
+ when '1'
68
+ @on_connect.call if @on_connect
69
+ when '2'
70
+ send_heartbeat
71
+ @on_heartbeat.call if @on_heartbeat
72
+ when '3'
73
+ @on_message.call decoded[:data] if @on_message
74
+ when '4'
75
+ @on_json_message.call decoded[:data] if @on_json_message
76
+ when '5'
77
+ message = JSON.parse(decoded[:data])
78
+ @on_event[message['name']].call message['args'] if @on_event[message['name']]
79
+ when '6'
80
+ @on_error.call decoded[:data] if @on_error
81
+ when '7'
82
+ @on_ack.call if @on_ack
83
+ when '8'
84
+ @on_noop.call if @on_noop
85
+ end
86
+ end
87
+ end
88
+ @thread
89
+ end
90
+
91
+ def disconnect
92
+ @transport.send("0::")
93
+ end
94
+
95
+ def disconnected
96
+ if @reconnect
97
+ connect_transport
98
+ start_recieve_loop
99
+ end
100
+ end
101
+
102
+ def join
103
+ @thread.join
104
+ end
105
+
106
+ def send_heartbeat
107
+ @transport.send("2::") #rescue false
108
+ end
109
+
110
+ def send_message(string)
111
+ @transport.send("3:::#{string}") #rescue false
112
+ end
113
+ alias :send :send_message
114
+
115
+ def send_json_message(hash)
116
+ @transport.send("4:::#{hash.to_json}") # rescue false
117
+ end
118
+
119
+ def send_event(name, hash)
120
+ @transport.send("5:::#{{name: name, args: [hash]}.to_json}") # rescue false
121
+ end
122
+ alias :emit :send_event
123
+
124
+ def before_start(&block)
125
+ @before_start = block
126
+ end
127
+
128
+ def after_start(&block)
129
+ @after_start = block
130
+ end
131
+
132
+ def on_disconnect(&block)
133
+ @on_disconnect = block
134
+ end
135
+
136
+ def on_connect(&block)
137
+ @on_connect = block
138
+ end
139
+
140
+ def on_heartbeat(&block)
141
+ @on_heartbeat = block
142
+ end
143
+
144
+ def on_message(&block)
145
+ @on_message = block
146
+ end
147
+
148
+ def on_json_message(&block)
149
+ @on_json_message = block
150
+ end
151
+
152
+ def on_event(name, &block)
153
+ @on_event[name] = block
154
+ end
155
+
156
+ def on_ack(&block)
157
+ @on_ack = block
158
+ end
159
+
160
+ def on_error(&block)
161
+ @on_error = block
162
+ end
163
+
164
+ def on_noop(&block)
165
+ @on_noop = block
166
+ end
167
+ end
168
+
169
+ end
data/lib/parser.rb ADDED
@@ -0,0 +1,13 @@
1
+ module Parser
2
+ @regexp = /([^:]+):([0-9]+)?(\+)?:([^:]+)?:?([\s\S]*)?/
3
+
4
+ # returns hash as {type: '1', id: '1', end_point: '4', data: [{key: value}]}
5
+ def self.decode(string)
6
+ if pieces = string.match(@regexp)
7
+ {type: pieces[1], id: pieces[2], end_point: pieces[4], data: pieces[5]}
8
+ else
9
+ {type: '0'}
10
+ end
11
+ end
12
+
13
+ end
data/lib/web_socket.rb ADDED
@@ -0,0 +1,429 @@
1
+ # Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
2
+ # Lincense: New BSD Lincense
3
+ # Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75
4
+ # Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
5
+ # Reference: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-07
6
+ # Reference: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10
7
+
8
+ require "base64"
9
+ require "socket"
10
+ require "uri"
11
+ require "digest/md5"
12
+ require "digest/sha1"
13
+ require "openssl"
14
+ require "stringio"
15
+
16
+
17
+ class WebSocket
18
+
19
+ class << self
20
+
21
+ attr_accessor(:debug)
22
+
23
+ end
24
+
25
+ class Error < RuntimeError
26
+
27
+ end
28
+
29
+ WEB_SOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
30
+ OPCODE_CONTINUATION = 0x00
31
+ OPCODE_TEXT = 0x01
32
+ OPCODE_BINARY = 0x02
33
+ OPCODE_CLOSE = 0x08
34
+ OPCODE_PING = 0x09
35
+ OPCODE_PONG = 0x0a
36
+
37
+ def initialize(arg, params = {})
38
+ if params[:server] # server
39
+
40
+ @server = params[:server]
41
+ @socket = arg
42
+ line = gets().chomp()
43
+ if !(line =~ /\AGET (\S+) HTTP\/1.1\z/n)
44
+ raise(WebSocket::Error, "invalid request: #{line}")
45
+ end
46
+ @path = $1
47
+ read_header()
48
+ if @header["sec-websocket-version"]
49
+ @web_socket_version = @header["sec-websocket-version"]
50
+ @key3 = nil
51
+ elsif @header["sec-websocket-key1"] && @header["sec-websocket-key2"]
52
+ @web_socket_version = "hixie-76"
53
+ @key3 = read(8)
54
+ else
55
+ @web_socket_version = "hixie-75"
56
+ @key3 = nil
57
+ end
58
+ if !@server.accepted_origin?(self.origin)
59
+ raise(WebSocket::Error,
60
+ ("Unaccepted origin: %s (server.accepted_domains = %p)\n\n" +
61
+ "To accept this origin, write e.g. \n" +
62
+ " WebSocketServer.new(..., :accepted_domains => [%p]), or\n" +
63
+ " WebSocketServer.new(..., :accepted_domains => [\"*\"])\n") %
64
+ [self.origin, @server.accepted_domains, @server.origin_to_domain(self.origin)])
65
+ end
66
+ @handshaked = false
67
+
68
+ else # client
69
+
70
+ @web_socket_version = "hixie-76"
71
+ uri = arg.is_a?(String) ? URI.parse(arg) : arg
72
+
73
+ if uri.scheme == "ws"
74
+ default_port = 80
75
+ elsif uri.scheme = "wss"
76
+ default_port = 443
77
+ else
78
+ raise(WebSocket::Error, "unsupported scheme: #{uri.scheme}")
79
+ end
80
+
81
+ @path = (uri.path.empty? ? "/" : uri.path) + (uri.query ? "?" + uri.query : "")
82
+ host = uri.host + (uri.port == default_port ? "" : ":#{uri.port}")
83
+ origin = params[:origin] || "http://#{uri.host}"
84
+ key1 = generate_key()
85
+ key2 = generate_key()
86
+ key3 = generate_key3()
87
+
88
+ socket = TCPSocket.new(uri.host, uri.port || default_port)
89
+
90
+ if uri.scheme == "ws"
91
+ @socket = socket
92
+ else
93
+ @socket = ssl_handshake(socket)
94
+ end
95
+
96
+ write(
97
+ "GET #{@path} HTTP/1.1\r\n" +
98
+ "Upgrade: WebSocket\r\n" +
99
+ "Connection: Upgrade\r\n" +
100
+ "Host: #{host}\r\n" +
101
+ "Origin: #{origin}\r\n" +
102
+ "Sec-WebSocket-Key1: #{key1}\r\n" +
103
+ "Sec-WebSocket-Key2: #{key2}\r\n" +
104
+ "\r\n" +
105
+ "#{key3}")
106
+ flush()
107
+
108
+ line = gets().chomp()
109
+ raise(WebSocket::Error, "bad response: #{line}") if !(line =~ /\AHTTP\/1.1 101 /n)
110
+ read_header()
111
+ if (@header["sec-websocket-origin"] || "").downcase() != origin.downcase()
112
+ raise(WebSocket::Error,
113
+ "origin doesn't match: '#{@header["sec-websocket-origin"]}' != '#{origin}'")
114
+ end
115
+ reply_digest = read(16)
116
+ expected_digest = hixie_76_security_digest(key1, key2, key3)
117
+ if reply_digest != expected_digest
118
+ raise(WebSocket::Error,
119
+ "security digest doesn't match: %p != %p" % [reply_digest, expected_digest])
120
+ end
121
+ @handshaked = true
122
+
123
+ end
124
+ @received = []
125
+ @buffer = ""
126
+ @closing_started = false
127
+ end
128
+
129
+ attr_reader(:server, :header, :path)
130
+
131
+ def handshake(status = nil, header = {})
132
+ if @handshaked
133
+ raise(WebSocket::Error, "handshake has already been done")
134
+ end
135
+ status ||= "101 Switching Protocols"
136
+ def_header = {}
137
+ case @web_socket_version
138
+ when "hixie-75"
139
+ def_header["WebSocket-Origin"] = self.origin
140
+ def_header["WebSocket-Location"] = self.location
141
+ extra_bytes = ""
142
+ when "hixie-76"
143
+ def_header["Sec-WebSocket-Origin"] = self.origin
144
+ def_header["Sec-WebSocket-Location"] = self.location
145
+ extra_bytes = hixie_76_security_digest(
146
+ @header["Sec-WebSocket-Key1"], @header["Sec-WebSocket-Key2"], @key3)
147
+ else
148
+ def_header["Sec-WebSocket-Accept"] = security_digest(@header["sec-websocket-key"])
149
+ extra_bytes = ""
150
+ end
151
+ header = def_header.merge(header)
152
+ header_str = header.map(){ |k, v| "#{k}: #{v}\r\n" }.join("")
153
+ # Note that Upgrade and Connection must appear in this order.
154
+ write(
155
+ "HTTP/1.1 #{status}\r\n" +
156
+ "Upgrade: websocket\r\n" +
157
+ "Connection: Upgrade\r\n" +
158
+ "#{header_str}\r\n#{extra_bytes}")
159
+ flush()
160
+ @handshaked = true
161
+ end
162
+
163
+ def send(data)
164
+ if !@handshaked
165
+ raise(WebSocket::Error, "call WebSocket\#handshake first")
166
+ end
167
+ case @web_socket_version
168
+ when "hixie-75", "hixie-76"
169
+ data = force_encoding(data.dup(), "ASCII-8BIT")
170
+ write("\x00#{data}\xff")
171
+ flush()
172
+ else
173
+ send_frame(OPCODE_TEXT, data, !@server)
174
+ end
175
+ end
176
+
177
+ def receive()
178
+ if !@handshaked
179
+ raise(WebSocket::Error, "call WebSocket\#handshake first")
180
+ end
181
+ case @web_socket_version
182
+
183
+ when "hixie-75", "hixie-76"
184
+ packet = gets("\xff")
185
+ return nil if !packet
186
+ if packet =~ /\A\x00(.*)\xff\z/nm
187
+ return force_encoding($1, "UTF-8")
188
+ elsif packet == "\xff" && read(1) == "\x00" # closing
189
+ close(1005, "", :peer)
190
+ return nil
191
+ else
192
+ raise(WebSocket::Error, "input must be either '\\x00...\\xff' or '\\xff\\x00'")
193
+ end
194
+
195
+ else
196
+ begin
197
+ bytes = read(2).unpack("C*")
198
+ fin = (bytes[0] & 0x80) != 0
199
+ opcode = bytes[0] & 0x0f
200
+ mask = (bytes[1] & 0x80) != 0
201
+ plength = bytes[1] & 0x7f
202
+ if plength == 126
203
+ bytes = read(2)
204
+ plength = bytes.unpack("n")[0]
205
+ elsif plength == 127
206
+ bytes = read(8)
207
+ (high, low) = bytes.unpack("NN")
208
+ plength = high * (2 ** 32) + low
209
+ end
210
+ if @server && !mask
211
+ # Masking is required.
212
+ @socket.close()
213
+ raise(WebSocket::Error, "received unmasked data")
214
+ end
215
+ mask_key = mask ? read(4).unpack("C*") : nil
216
+ payload = read(plength)
217
+ payload = apply_mask(payload, mask_key) if mask
218
+ case opcode
219
+ when OPCODE_TEXT
220
+ return force_encoding(payload, "UTF-8")
221
+ when OPCODE_BINARY
222
+ raise(WebSocket::Error, "received binary data, which is not supported")
223
+ when OPCODE_CLOSE
224
+ close(1005, "", :peer)
225
+ return nil
226
+ when OPCODE_PING
227
+ raise(WebSocket::Error, "received ping, which is not supported")
228
+ when OPCODE_PONG
229
+ else
230
+ raise(WebSocket::Error, "received unknown opcode: %d" % opcode)
231
+ end
232
+ rescue EOFError
233
+ return nil
234
+ end
235
+
236
+ end
237
+ end
238
+
239
+ def tcp_socket
240
+ return @socket
241
+ end
242
+
243
+ def host
244
+ return @header["host"]
245
+ end
246
+
247
+ def origin
248
+ case @web_socket_version
249
+ when "7", "8"
250
+ name = "sec-websocket-origin"
251
+ else
252
+ name = "origin"
253
+ end
254
+ if @header[name]
255
+ return @header[name]
256
+ else
257
+ raise(WebSocket::Error, "%s header is missing" % name)
258
+ end
259
+ end
260
+
261
+ def location
262
+ return "ws://#{self.host}#{@path}"
263
+ end
264
+
265
+ # Does closing handshake.
266
+ def close(code = 1005, reason = "", origin = :self)
267
+ if !@closing_started
268
+ case @web_socket_version
269
+ when "hixie-75", "hixie-76"
270
+ write("\xff\x00")
271
+ else
272
+ if code == 1005
273
+ payload = ""
274
+ else
275
+ payload = [code].pack("n") + force_encoding(reason.dup(), "ASCII-8BIT")
276
+ end
277
+ send_frame(OPCODE_CLOSE, payload, false)
278
+ end
279
+ end
280
+ @socket.close() if origin == :peer
281
+ @closing_started = true
282
+ end
283
+
284
+ def close_socket()
285
+ @socket.close()
286
+ end
287
+
288
+ private
289
+
290
+ NOISE_CHARS = ("\x21".."\x2f").to_a() + ("\x3a".."\x7e").to_a()
291
+
292
+ def read_header()
293
+ @header = {}
294
+ while line = gets()
295
+ line = line.chomp()
296
+ break if line.empty?
297
+ if !(line =~ /\A(\S+): (.*)\z/n)
298
+ raise(WebSocket::Error, "invalid request: #{line}")
299
+ end
300
+ @header[$1] = $2
301
+ @header[$1.downcase()] = $2
302
+ end
303
+ if !(@header["upgrade"] =~ /\AWebSocket\z/i)
304
+ raise(WebSocket::Error, "invalid Upgrade: " + @header["upgrade"])
305
+ end
306
+ if !(@header["connection"] =~ /\AUpgrade\z/i)
307
+ raise(WebSocket::Error, "invalid Connection: " + @header["connection"])
308
+ end
309
+ end
310
+
311
+ def send_frame(opcode, payload, mask)
312
+ payload = force_encoding(payload.dup(), "ASCII-8BIT")
313
+ # Setting StringIO's encoding to ASCII-8BIT.
314
+ buffer = StringIO.new(force_encoding("", "ASCII-8BIT"))
315
+ write_byte(buffer, 0x80 | opcode)
316
+ masked_byte = mask ? 0x80 : 0x00
317
+ if payload.bytesize <= 125
318
+ write_byte(buffer, masked_byte | payload.bytesize)
319
+ elsif payload.bytesize < 2 ** 16
320
+ write_byte(buffer, masked_byte | 126)
321
+ buffer.write([payload.bytesize].pack("n"))
322
+ else
323
+ write_byte(buffer, masked_byte | 127)
324
+ buffer.write([payload.bytesize / (2 ** 32), payload.bytesize % (2 ** 32)].pack("NN"))
325
+ end
326
+ if mask
327
+ mask_key = Array.new(4){ rand(256) }
328
+ buffer.write(mask_key.pack("C*"))
329
+ payload = apply_mask(payload, mask_key)
330
+ end
331
+ buffer.write(payload)
332
+ write(buffer.string)
333
+ end
334
+
335
+ def gets(rs = $/)
336
+ line = @socket.gets(rs)
337
+ $stderr.printf("recv> %p\n", line) if WebSocket.debug
338
+ return line
339
+ end
340
+
341
+ def read(num_bytes)
342
+ str = @socket.read(num_bytes)
343
+ $stderr.printf("recv> %p\n", str) if WebSocket.debug
344
+ if str && str.bytesize == num_bytes
345
+ return str
346
+ else
347
+ raise(EOFError)
348
+ end
349
+ end
350
+
351
+ def write(data)
352
+ if WebSocket.debug
353
+ data.scan(/\G(.*?(\n|\z))/n) do
354
+ $stderr.printf("send> %p\n", $&) if !$&.empty?
355
+ end
356
+ end
357
+ @socket.write(data)
358
+ end
359
+
360
+ def flush()
361
+ @socket.flush()
362
+ end
363
+
364
+ def write_byte(buffer, byte)
365
+ buffer.write([byte].pack("C"))
366
+ end
367
+
368
+ def security_digest(key)
369
+ return Base64.encode64(Digest::SHA1.digest(key + WEB_SOCKET_GUID)).gsub(/\n/, "")
370
+ end
371
+
372
+ def hixie_76_security_digest(key1, key2, key3)
373
+ bytes1 = websocket_key_to_bytes(key1)
374
+ bytes2 = websocket_key_to_bytes(key2)
375
+ return Digest::MD5.digest(bytes1 + bytes2 + key3)
376
+ end
377
+
378
+ def apply_mask(payload, mask_key)
379
+ orig_bytes = payload.unpack("C*")
380
+ new_bytes = []
381
+ orig_bytes.each_with_index() do |b, i|
382
+ new_bytes.push(b ^ mask_key[i % 4])
383
+ end
384
+ return new_bytes.pack("C*")
385
+ end
386
+
387
+ def generate_key()
388
+ spaces = 1 + rand(12)
389
+ max = 0xffffffff / spaces
390
+ number = rand(max + 1)
391
+ key = (number * spaces).to_s()
392
+ (1 + rand(12)).times() do
393
+ char = NOISE_CHARS[rand(NOISE_CHARS.size)]
394
+ pos = rand(key.size + 1)
395
+ key[pos...pos] = char
396
+ end
397
+ spaces.times() do
398
+ pos = 1 + rand(key.size - 1)
399
+ key[pos...pos] = " "
400
+ end
401
+ return key
402
+ end
403
+
404
+ def generate_key3()
405
+ return [rand(0x100000000)].pack("N") + [rand(0x100000000)].pack("N")
406
+ end
407
+
408
+ def websocket_key_to_bytes(key)
409
+ num = key.gsub(/[^\d]/n, "").to_i() / key.scan(/ /).size
410
+ return [num].pack("N")
411
+ end
412
+
413
+ def force_encoding(str, encoding)
414
+ if str.respond_to?(:force_encoding)
415
+ return str.force_encoding(encoding)
416
+ else
417
+ return str
418
+ end
419
+ end
420
+
421
+ def ssl_handshake(socket)
422
+ ssl_context = OpenSSL::SSL::SSLContext.new()
423
+ ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
424
+ ssl_socket.sync_close = true
425
+ ssl_socket.connect()
426
+ return ssl_socket
427
+ end
428
+
429
+ end
Binary file
Binary file
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require 'socketIO'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "socketio-client"
7
+ s.version = SocketIO::Client::VERSION
8
+ s.authors = ["Lyon"]
9
+ s.email = ["lyon@delorum.com"]
10
+ s.homepage = "http://github.com/lyondhill/socket.io-ruby-client"
11
+ s.summary = %q{A basic Socket.io client implememtation written for ruby}
12
+ s.description = %q{uses a very simple web socket}
13
+
14
+ s.rubyforge_project = "socketio-client"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ s.add_runtime_dependency "rest-client"
24
+ end
@@ -0,0 +1,22 @@
1
+ require 'socketIO'
2
+
3
+ describe Parser do
4
+
5
+ it 'should be able to decode all valid messages' do
6
+ Parser.decode("0").should == {type: "0"}
7
+ Parser.decode("1::").should == {type: "1", id: nil, end_point: nil, data: ""}
8
+ Parser.decode("2::").should == {type: "2", id: nil, end_point: nil, data: ""}
9
+ Parser.decode("3:::hay you").should == {type: "3", id: nil, end_point: nil, data: "hay you"}
10
+ Parser.decode("4:::{\"can\":\"youcall\"}").should == {type: "4", id: nil, end_point: nil, data: "{\"can\":\"youcall\"}"}
11
+ Parser.decode("5:::hay you").should == {type: "5", id: nil, end_point: nil, data: "hay you"}
12
+ Parser.decode("6:::").should == {type: "6", id: nil, end_point: nil, data: ""}
13
+ Parser.decode("7:::there is an error").should == {type: "7", id: nil, end_point: nil, data: "there is an error"}
14
+ Parser.decode("8:::").should == {type: "8", id: nil, end_point: nil, data: ""}
15
+ end
16
+
17
+ it "should give a disconnect if bad input" do
18
+ Parser.decode("hay dude").should == {type: "0"}
19
+ Parser.decode("9").should == {type: "0"}
20
+ end
21
+
22
+ end
@@ -0,0 +1,17 @@
1
+ var io = require('socket.io').listen(80);
2
+
3
+ io.sockets.on('connection', function(socket) {
4
+
5
+ socket.on('message', function(msg) {
6
+ socket.send(msg)
7
+ })
8
+
9
+ socket.on('event', function(data) {
10
+ socket.emit('event', data);
11
+ });
12
+
13
+ socket.on('dc', function() {
14
+ socket.close()
15
+ })
16
+
17
+ });
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ echo_server = fork do
4
+ puts `pwd`
5
+ # exec ""
6
+ end
7
+ puts "pid ??? #{echo_server}"
8
+ Process.detach(echo_server)
File without changes
@@ -0,0 +1,46 @@
1
+ describe SocketIO do
2
+
3
+ before :all do
4
+ @client = SocketIO.connect("localhost", :sync => true)
5
+ end
6
+
7
+ it "can send a heartbeat" do
8
+ @client.send_heartbeat
9
+ # should not bonk. haha
10
+ end
11
+
12
+ it "gets a message back when it sends one" do
13
+ count = 0
14
+ @client.on_message do |msg|
15
+ count += 1 if msg == "hay dude"
16
+ end
17
+ @client.send("hay dude")
18
+ sleep 0.5
19
+ count.should == 1
20
+ end
21
+
22
+ it "gets an emit back when it hits the echo" do
23
+ count = 0
24
+ @client.on_event("event") do |data|
25
+ count += 1 if data[0] == {"first"=>"element", "second"=>"guy"}
26
+ end
27
+ @client.emit("event", {first: "element", second: "guy"})
28
+ sleep 0.5
29
+ count.should == 1
30
+ end
31
+
32
+ it "can have a block for every thing" do
33
+ @client.on_disconnect { }
34
+ @client.on_connect { }
35
+ @client.on_heartbeat { }
36
+ @client.on_message { |msg| }
37
+ @client.on_json_message { |json| }
38
+ @client.on_event('en') { |event_hash| }
39
+ @client.on_ack { }
40
+ @client.on_error { |data| }
41
+ @client.on_noop { }
42
+ end
43
+
44
+
45
+
46
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: socketio-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Lyon
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-10 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rest-client
16
+ requirement: &2154912940 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2154912940
25
+ description: uses a very simple web socket
26
+ email:
27
+ - lyon@delorum.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - Gemfile
33
+ - README.md
34
+ - Rakefile
35
+ - example/complex.rb
36
+ - example/remote_log.rb
37
+ - example/servers/complex.js
38
+ - example/servers/remote_log.js
39
+ - example/servers/simple.js
40
+ - example/simple.rb
41
+ - lib/SocketIO.rb
42
+ - lib/parser.rb
43
+ - lib/web_socket.rb
44
+ - pkg/socket.io-client-0.0.1.gem
45
+ - pkg/socketio-client-0.0.1.gem
46
+ - socketio-client.gemspec
47
+ - spec/parser_spec.rb
48
+ - spec/setup/echo.js
49
+ - spec/setup/start.rb
50
+ - spec/setup/stop.rb
51
+ - spec/socketio_spec.rb
52
+ homepage: http://github.com/lyondhill/socket.io-ruby-client
53
+ licenses: []
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubyforge_project: socketio-client
72
+ rubygems_version: 1.8.11
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: A basic Socket.io client implememtation written for ruby
76
+ test_files:
77
+ - spec/parser_spec.rb
78
+ - spec/setup/echo.js
79
+ - spec/setup/start.rb
80
+ - spec/setup/stop.rb
81
+ - spec/socketio_spec.rb