websocket-client 0.1.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/examples/simple.rb +26 -0
- data/examples/with_block.rb +27 -0
- data/lib/websocket_client/client.rb +111 -0
- data/lib/websocket_client/ietf_00.rb +90 -0
- data/lib/websocket_client.rb +2 -0
- metadata +69 -0
data/examples/simple.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$: << File.dirname(__FILE__) + '/../lib'
|
4
|
+
|
5
|
+
require 'websocket_client'
|
6
|
+
|
7
|
+
## Create a disconnected client
|
8
|
+
client = WebSocketClient.create( 'ws://localhost:8081/websockets/' )
|
9
|
+
|
10
|
+
## Set up the message handler before connecting
|
11
|
+
client.on_message do |msg|
|
12
|
+
puts "received #{msg}"
|
13
|
+
end
|
14
|
+
|
15
|
+
## Connect
|
16
|
+
client.connect()
|
17
|
+
|
18
|
+
## Use the connected client
|
19
|
+
puts "sending"
|
20
|
+
client.send( "HOWDY-1" )
|
21
|
+
client.send( "HOWDY-2" )
|
22
|
+
client.send( "HOWDY-3" )
|
23
|
+
sleep(1)
|
24
|
+
|
25
|
+
## Explicit disconncet
|
26
|
+
client.disconnect
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$: << File.dirname(__FILE__) + '/../lib'
|
4
|
+
|
5
|
+
require 'websocket_client'
|
6
|
+
|
7
|
+
## Create a disconnected client
|
8
|
+
WebSocketClient.create( 'ws://localhost:8081/websockets/' ) do |client|
|
9
|
+
|
10
|
+
## Set up the message handler before connecting
|
11
|
+
client.on_message do |msg|
|
12
|
+
puts "received #{msg}"
|
13
|
+
end
|
14
|
+
|
15
|
+
## Connect
|
16
|
+
client.connect() do |client|
|
17
|
+
|
18
|
+
## Use the connected client
|
19
|
+
puts "sending"
|
20
|
+
client.send( "HOWDY-1" )
|
21
|
+
client.send( "HOWDY-2" )
|
22
|
+
client.send( "HOWDY-3" )
|
23
|
+
sleep(1)
|
24
|
+
end
|
25
|
+
|
26
|
+
## Disconnected
|
27
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'uri'
|
3
|
+
require 'websocket_client/ietf_00'
|
4
|
+
|
5
|
+
|
6
|
+
module WebSocketClient
|
7
|
+
|
8
|
+
DEFAULT_HANDSHAKE = Ietf00
|
9
|
+
|
10
|
+
class DebugSocket
|
11
|
+
def initialize(socket)
|
12
|
+
@socket = socket
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(method, *args)
|
16
|
+
puts "> #{method}(#{args.join( ', ')})"
|
17
|
+
@socket.__send__( method, *args )
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.create(uri,handshake_class=WebSocketClient::DEFAULT_HANDSHAKE,&block)
|
22
|
+
Client.new(uri, handshake_class, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
class Client
|
26
|
+
attr_reader :uri
|
27
|
+
|
28
|
+
def initialize(uri,handshake_class=WebSocketClient::DEFAULT_HANDSHAKE, &block)
|
29
|
+
@handshake_class = handshake_class
|
30
|
+
@socket = nil
|
31
|
+
@on_message_handler = nil
|
32
|
+
@handler_thread = nil
|
33
|
+
@close_state = nil
|
34
|
+
case ( uri )
|
35
|
+
when String
|
36
|
+
@uri = URI.parse( uri )
|
37
|
+
else
|
38
|
+
@uri = uri
|
39
|
+
end
|
40
|
+
block.call( self ) if block
|
41
|
+
end
|
42
|
+
|
43
|
+
def connect(&block)
|
44
|
+
@socket = TCPSocket.open(uri.host, uri.port)
|
45
|
+
@handshake = @handshake_class.new( uri, @socket )
|
46
|
+
|
47
|
+
start_handler
|
48
|
+
|
49
|
+
if ( block )
|
50
|
+
begin
|
51
|
+
block.call( self )
|
52
|
+
ensure
|
53
|
+
disconnect()
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def disconnect
|
59
|
+
@socket.puts 0xFF
|
60
|
+
@socket.putc 0x00
|
61
|
+
@close_state = :requested
|
62
|
+
end
|
63
|
+
|
64
|
+
def send(msg)
|
65
|
+
@socket.print @handshake.encode_text_message( msg )
|
66
|
+
@socket.flush
|
67
|
+
end
|
68
|
+
|
69
|
+
def wait_forever()
|
70
|
+
@handler_thread.join
|
71
|
+
end
|
72
|
+
|
73
|
+
def on_message(msg=nil,&block)
|
74
|
+
if ( block )
|
75
|
+
@on_message_handler = block
|
76
|
+
else
|
77
|
+
@on_message_handler.call( msg ) if @on_message_handler
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def start_handler
|
82
|
+
@handler_thread = Thread.new(@socket) do |socket|
|
83
|
+
msg = ''
|
84
|
+
while ( ! socket.eof? )
|
85
|
+
c = socket.getc
|
86
|
+
if ( c == 0x00 )
|
87
|
+
if ( @close_state == :half_closed )
|
88
|
+
socket.close
|
89
|
+
break;
|
90
|
+
else
|
91
|
+
msg = ''
|
92
|
+
end
|
93
|
+
elsif ( c == 0xFF )
|
94
|
+
if ( @close_state == :requested )
|
95
|
+
@close_state = :half_closed
|
96
|
+
else
|
97
|
+
on_message( msg )
|
98
|
+
end
|
99
|
+
else
|
100
|
+
if ( @close_state != nil )
|
101
|
+
@close_state = nil
|
102
|
+
end
|
103
|
+
msg << c
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'uri'
|
3
|
+
require 'digest/md5'
|
4
|
+
|
5
|
+
module WebSocketClient
|
6
|
+
|
7
|
+
class Ietf00
|
8
|
+
|
9
|
+
def initialize(uri, socket)
|
10
|
+
@socket = socket
|
11
|
+
key1, key2, key3, solution = generate_keys()
|
12
|
+
|
13
|
+
@socket.puts "GET #{uri.path} HTTP/1.1"
|
14
|
+
@socket.puts "Host: #{uri.host}"
|
15
|
+
@socket.puts "Connection: upgrade"
|
16
|
+
@socket.puts "Upgrade: websocket"
|
17
|
+
@socket.puts "Origin: http://#{uri.host}/"
|
18
|
+
@socket.puts "Sec-WebSocket-Version: 8"
|
19
|
+
@socket.puts "Sec-WebSocket-Key1: #{key1}"
|
20
|
+
@socket.puts "Sec-WebSocket-Key2: #{key2}"
|
21
|
+
@socket.puts ""
|
22
|
+
@socket.print key3
|
23
|
+
@socket.puts ""
|
24
|
+
@socket.flush
|
25
|
+
|
26
|
+
while ( ! @socket.eof? )
|
27
|
+
line = @socket.gets
|
28
|
+
break if ( line.strip == '' )
|
29
|
+
end
|
30
|
+
challenge = @socket.read( 16 )
|
31
|
+
if ( challenge == solution )
|
32
|
+
puts "success!"
|
33
|
+
end
|
34
|
+
#dump 'solution', solution
|
35
|
+
#dump 'challenge', challenge
|
36
|
+
end
|
37
|
+
|
38
|
+
def encode_text_message(msg)
|
39
|
+
@socket.putc 0x00
|
40
|
+
@socket.print msg
|
41
|
+
@socket.putc 0xFF
|
42
|
+
@socket.flush
|
43
|
+
end
|
44
|
+
|
45
|
+
def generate_keys()
|
46
|
+
key1 = generate_header_key
|
47
|
+
key2 = generate_header_key
|
48
|
+
key3 = generate_content_key
|
49
|
+
[ key1, key2, key3, solve( key1, key2, key3 ) ]
|
50
|
+
end
|
51
|
+
|
52
|
+
def solve(key1, key2, key3)
|
53
|
+
int1 = solve_header_key( key1 )
|
54
|
+
int2 = solve_header_key( key2 )
|
55
|
+
input = int1.to_s + int2.to_s + key3
|
56
|
+
Digest::MD5.digest( input )
|
57
|
+
end
|
58
|
+
|
59
|
+
def solve_header_key(key)
|
60
|
+
key_digits = key.strip.gsub( /[^0-9]/, '').to_i
|
61
|
+
key_spaces = key.strip.gsub( /[^ ]/, '').size
|
62
|
+
solution = key_digits / key_spaces
|
63
|
+
solution
|
64
|
+
end
|
65
|
+
|
66
|
+
def generate_header_key
|
67
|
+
key = ''
|
68
|
+
1.upto(32) do
|
69
|
+
key << rand(90) + 32
|
70
|
+
end
|
71
|
+
1.upto( rand(10) + 2 ) do
|
72
|
+
key[rand(key.size-1)+1,1] = ' '
|
73
|
+
end
|
74
|
+
1.upto( rand(10) + 2 ) do
|
75
|
+
key[rand(key.size-1)+1,1] = rand(9).to_s
|
76
|
+
end
|
77
|
+
key
|
78
|
+
end
|
79
|
+
|
80
|
+
def generate_content_key
|
81
|
+
key = []
|
82
|
+
'tacobob1'.each_byte do |byte|
|
83
|
+
key << byte
|
84
|
+
end
|
85
|
+
key.pack('cccccccc')
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: websocket-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- The TorqueBox Team
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-06-10 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description:
|
22
|
+
email: team@torquebox.org
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- lib/websocket_client/client.rb
|
31
|
+
- lib/websocket_client/ietf_00.rb
|
32
|
+
- lib/websocket_client.rb
|
33
|
+
- examples/simple.rb
|
34
|
+
- examples/with_block.rb
|
35
|
+
homepage:
|
36
|
+
licenses: []
|
37
|
+
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
hash: 3
|
49
|
+
segments:
|
50
|
+
- 0
|
51
|
+
version: "0"
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
requirements: []
|
62
|
+
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.7.1
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: Pure ruby WebSocket client
|
68
|
+
test_files: []
|
69
|
+
|