em-websocket-server 0.13 → 0.15

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,29 +0,0 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <title>em-websocket-server test</title>
5
- </head>
6
- <body>
7
-
8
- <h1>em-websocket-server timesync demo</h1>
9
- <h2 id="time">opening socket</h2>
10
-
11
- <script>
12
- var webSocket = new WebSocket('ws://localhost:8000/time');
13
-
14
- webSocket.onopen = function(event){
15
- document.getElementById('time').innerHTML = 'waiting for socket';
16
- };
17
-
18
- webSocket.onmessage = function(event){
19
- var object = JSON.parse(event.data);
20
- document.getElementById('time').innerHTML = object.time;
21
- };
22
-
23
- webSocket.onclose = function(event){
24
- document.getElementById('time').innerHTML = 'socket closed';
25
- };
26
- </script>
27
-
28
- </body>
29
- </html>
@@ -1,37 +0,0 @@
1
- $:.unshift File.dirname(__FILE__) + '/../lib'
2
-
3
- require 'rubygems'
4
- require 'web_socket'
5
- require 'json'
6
-
7
- class TimeServer < WebSocket::Server
8
-
9
- def on_connect
10
- puts "Connection Accepted"
11
- @timer = EM.add_periodic_timer(5, EM.Callback(self, :sync_time))
12
- end
13
-
14
- def on_disconnect
15
- puts "Connection released"
16
- EM.cancel_timer @timer
17
- end
18
-
19
- def on_receive msg
20
- end
21
-
22
- def sync_time
23
- puts "Hi"
24
- send_message({ :time => Time.now }.to_json)
25
- end
26
-
27
- end
28
-
29
-
30
- EM.epoll = true if EM.epoll?
31
- EM.kqueue = true if EM.kqueue?
32
-
33
- EM.set_descriptor_table_size(10240)
34
-
35
- EM.run do
36
- EM.start_server "0.0.0.0", 8000, TimeServer
37
- end
@@ -1,13 +0,0 @@
1
- require 'rubygems'
2
- require 'eventmachine'
3
- require 'digest'
4
- require 'pp'
5
-
6
- module WebSocket
7
- VERSION = 0.13
8
- end
9
-
10
- require 'web_socket/util.rb'
11
- require 'web_socket/frame.rb'
12
- require 'web_socket/server.rb'
13
- require 'web_socket/client.rb'
@@ -1,71 +0,0 @@
1
-
2
- module WebSocket
3
-
4
- class Client < EM::Connection
5
-
6
- def path
7
- "/chat"
8
- end
9
-
10
- def host
11
- "localhost:8000"
12
- end
13
-
14
- def origin
15
- "localhost"
16
- end
17
-
18
- # em override
19
- def post_init
20
- @connected = false
21
- end
22
-
23
- def connection_completed
24
- send_headers
25
- end
26
-
27
- # em override
28
- def unbind
29
- on_disconnect
30
- end
31
-
32
- def on_disconnect
33
- end
34
-
35
- def send_message msg
36
- send_data Frame.encode(msg)
37
- end
38
-
39
- private
40
-
41
- def receive_data data
42
- unless @connected
43
- handshake data
44
- else
45
- while msg = data.slice!(/\000([^\377]*)\377/)
46
- on_receive Frame.decode(msg)
47
- end
48
- end
49
- end
50
-
51
- def handshake data
52
-
53
- #convert the headers to a hash
54
- @headers = Util.parse_headers(data)
55
- @connected = true
56
-
57
- on_connect
58
- end
59
-
60
- def send_headers
61
- result = "GET #{path} HTTP/1.1\r\n"
62
- result << "Upgrade: WebSocket\r\n"
63
- result << "Connection: Upgrade\r\n"
64
- result << "Host: #{host}\r\n"
65
- result << "Origin: #{origin}\r\n\r\n"
66
-
67
- send_data result
68
- end
69
- end
70
-
71
- end
@@ -1,20 +0,0 @@
1
- module WebSocket
2
-
3
- class Frame
4
-
5
- # Frames need to start with 0x00-0x7f byte and end with
6
- # an 0xFF byte. Per spec, we can also set the first
7
- # byte to a value betweent 0x80 and 0xFF, followed by
8
- # a leading length indicator. No support yet.
9
- def self.encode data
10
- "\x00#{data}\xff"
11
- end
12
-
13
- # Strip leading and trailing bytes
14
- def self.decode data
15
- data.gsub(/^(\x00)|(\xff)$/, "")
16
- end
17
-
18
- end
19
-
20
- end
@@ -1,202 +0,0 @@
1
- module WebSocket
2
-
3
- class Server < EM::Connection
4
-
5
- @@logger = nil
6
- @@num_connections = 0
7
- @@callbacks = {}
8
- @@accepted_origins = []
9
-
10
- attr_accessor :connected,
11
- :headers
12
-
13
- def initialize *args
14
- super
15
- @connected = false
16
- @protocol_version = 75
17
- end
18
-
19
- def valid_origin?
20
- @@accepted_origins.empty? || @@accepted_origins.include?(origin)
21
- end
22
-
23
- #not doing anything with this yet
24
- def valid_path?
25
- true
26
- end
27
-
28
- def valid_upgrade?
29
- @headers[:upgrade] =~ /websocket/i
30
- end
31
-
32
- def origin
33
- @headers[:origin]
34
- end
35
-
36
- def host
37
- @headers[:host]
38
- end
39
-
40
- def path
41
- @headers[:path]
42
- end
43
-
44
- def cookies
45
- @headers[:cookie]
46
- end
47
-
48
- def protocol
49
- @headers[:protocol]
50
- end
51
-
52
- def self.path name, &block
53
- @@callbacks[name] = block
54
- end
55
-
56
- #tcp connection established
57
- def post_init
58
- @@num_connections += 1
59
- end
60
-
61
- #must be public for em
62
- def unbind
63
- @@num_connections -= 1
64
- on_disconnect
65
- end
66
-
67
- def send_message msg
68
- send_data Frame.encode(msg)
69
- end
70
-
71
- protected
72
-
73
- #override this method
74
- def on_receive msg
75
- log msg
76
- end
77
-
78
- #override this method
79
- def on_connect
80
- log "connected"
81
- end
82
-
83
- #override this method
84
- def on_disconnect
85
- log "disconnected"
86
- end
87
-
88
- def log msg
89
- if @@logger
90
- @@logger.info msg
91
- else
92
- puts msg
93
- end
94
- end
95
-
96
- private
97
-
98
- # when the connection receives data from the client
99
- # we either handshake or handle the message at
100
- # the app layer
101
- def receive_data data
102
- unless @connected
103
- handshake data
104
- else
105
- while msg = data.slice!(/\000([^\377]*)\377/)
106
- on_receive Frame.decode(msg)
107
- end
108
- end
109
- end
110
-
111
- # parse the headers, validate the origin and path
112
- # and respond with appropiate headers for a
113
- # healthy relationship with the client
114
- def handshake data
115
- #convert the headers to a hash
116
- @headers, challenge = Util.parse_headers(data)
117
-
118
- if challenge
119
- @protocol_version = 76
120
-
121
- key1 = @headers[:'sec-websocket-key1']
122
- key2 = @headers[:'sec-websocket-key2']
123
-
124
- part1 = number_from_key(key1)
125
- part2 = number_from_key(key2)
126
-
127
- unless part1 && part2
128
- close_connection
129
- return
130
- end
131
-
132
- buffer = []
133
- buffer += big_endian_bytes(part1)
134
- buffer += big_endian_bytes(part2)
135
- buffer += challenge.bytes.to_a
136
-
137
- md5 = Digest::MD5.new
138
- hash = md5.digest(buffer.pack('c*'))
139
- end
140
-
141
- # close the connection if the connection
142
- # originates from an invalid source
143
- close_connection unless valid_origin?
144
-
145
- # close the connection if a callback
146
- # is not registered for the path
147
- close_connection unless valid_path?
148
-
149
- # don't respond to non-websocket HTTP requests
150
- close_connection unless valid_upgrade?
151
-
152
- #complete the handshake
153
- send_headers(hash)
154
-
155
- @connected = true
156
-
157
- on_connect
158
- end
159
-
160
- # send the handshake response headers to
161
- # complete the initial com
162
- def send_headers(hash)
163
- response = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
164
- response << "Upgrade: WebSocket\r\n"
165
- response << "Connection: Upgrade\r\n"
166
- if @protocol_version > 75
167
- response << "Sec-WebSocket-Origin: #{origin}\r\n"
168
- response << "Sec-WebSocket-Location: ws://#{host}#{path}\r\n"
169
- response << "Sec-WebSocket-protocol: ws://#{host}#{path}\r\n"
170
- else
171
- response << "WebSocket-Origin: #{origin}\r\n"
172
- response << "WebSocket-Location: ws://#{host}#{path}\r\n"
173
- end
174
- response << "\r\n"
175
- response << hash if @protocol_version > 75
176
-
177
-
178
- send_data response
179
- end
180
-
181
- def number_from_key(key)
182
- digits = key.scan(/\d+/).join.to_i
183
- spaces = key.scan(/\s+/).join.length
184
-
185
- if spaces > 0
186
- return digits / spaces
187
- else
188
- return nil
189
- end
190
- end
191
-
192
- def big_endian_bytes(num)
193
- bytes = []
194
- 4.times do
195
- bytes.unshift(num & 0xff)
196
- num >>= 8
197
- end
198
- bytes
199
- end
200
- end
201
-
202
- end
@@ -1,55 +0,0 @@
1
- module WebSocket
2
-
3
- class Util
4
-
5
- @@path_regex = /^GET (\/[^\s]*) HTTP\/1\.1$/
6
- @@hs_regex = /^HTTP\/1\.1 101 Web Socket Protocol Handshake$/
7
- @@header_regex = /^([^:]+):\s*([^$]+)/
8
-
9
- # Parse http style headers into a ruby hash
10
- def self.parse_headers data
11
- lines = data.split("\r\n")
12
- line = lines.shift
13
-
14
- headers = {}
15
-
16
- case line
17
- when @@path_regex
18
- headers[:path] = @@path_regex.match(line)[1]
19
- when @@hs_regex
20
- #do nothing
21
- else
22
- throw "Unrecognized Header!"
23
- end
24
-
25
- while line = lines.shift
26
- if line.chomp.empty?
27
- break
28
- end
29
-
30
- kvp = @@header_regex.match(line)
31
- headers[kvp[1].strip.downcase.to_sym] = kvp[2].strip
32
- end
33
-
34
- challenge = lines.pop if lines
35
-
36
- [headers, challenge]
37
- end
38
-
39
- # encode ruby hash into HTTP style headers
40
- def self.encode_headers data
41
- result = ""
42
-
43
- data.each_pair do |k,v|
44
- result << k.to_s
45
- result << ": "
46
- result << v.to_s
47
- result << "\r\n"
48
- end
49
-
50
- result << "\r\n"
51
- result
52
- end
53
-
54
- end
55
- end