em-websocket-server 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/README.markdown ADDED
File without changes
@@ -0,0 +1,23 @@
1
+ spec = Gem::Specification.new do |s|
2
+ s.name = 'em-websocket-server'
3
+ s.version = '0.1'
4
+ s.date = '2009-12-14'
5
+ s.summary = 'An evented ruby websocket server built on top of EventMachine'
6
+ s.email = "dan.simpson@gmail.com"
7
+ s.homepage = "http://github.com/dansimpson/em-websocket-server"
8
+ s.description = "An evented ruby websocket server built on top of EventMachine"
9
+ s.has_rdoc = false
10
+
11
+
12
+ s.authors = ["Dan Simpson"]
13
+ s.add_dependency('eventmachine', '>= 0.12.4')
14
+
15
+
16
+ s.files = [
17
+ "README.markdown",
18
+ "em-websocket-server.gemspec",
19
+ "examples/chat.rb",
20
+ "examples/chat.html",
21
+ "lib/em-websocket-server.rb"
22
+ ]
23
+ end
@@ -0,0 +1,38 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <title>node.websocket.js test</title>
5
+
6
+ <style type="text/css" media="screen">
7
+ #time { background: #ccc; }
8
+ </style>
9
+ </head>
10
+ <body>
11
+
12
+ <h1>websocketed demo</h1>
13
+
14
+ <h2>The time is <span id="time">opening socket</span></h2>
15
+ <p>The information for the time is provided by the server. No Date() or intervals here!</p>
16
+
17
+ <script>
18
+ var webSocket = new WebSocket('ws://192.168.0.2:8000/time');
19
+
20
+ webSocket.onopen = function(event){
21
+ document.getElementById('time').innerHTML = 'waiting for socket';
22
+ webSocket.send('start');
23
+ };
24
+
25
+ webSocket.onmessage = function(event){
26
+ console.log(event);
27
+ var object = JSON.parse(event.data);
28
+ document.getElementById('time').innerHTML = object.time;
29
+ };
30
+
31
+ webSocket.onclose = function(event){
32
+ document.getElementById('time').innerHTML = 'socket closed';
33
+ };
34
+
35
+ </script>
36
+
37
+ </body>
38
+ </html>
data/examples/chat.rb ADDED
@@ -0,0 +1,32 @@
1
+ require 'rubygems'
2
+ require 'json'
3
+
4
+ class TimeServer < WebSocketServer
5
+
6
+ def on_connect
7
+ @timer = EM.add_periodic_timer(5) do
8
+ sync_time
9
+ end
10
+ end
11
+
12
+ def on_disconnect
13
+ @timer
14
+ end
15
+
16
+ def on_receive msg
17
+ puts "msg rcv"
18
+ end
19
+
20
+ def sync_time
21
+ send_message({ :time => Time.now }.to_json)
22
+ end
23
+
24
+ end
25
+
26
+
27
+ EM.epoll
28
+ EM.set_descriptor_table_size(10240)
29
+
30
+ EM.run do
31
+ EM.start_server "0.0.0.0", 8000, TimeServer
32
+ end
@@ -0,0 +1,164 @@
1
+ require 'rubygems'
2
+ require 'eventmachine'
3
+
4
+ class WebSocketServer < EM::Connection
5
+
6
+ @@logger = nil
7
+ @@num_connections = 0
8
+ @@path_regex = /^GET (\/[^\s]*) HTTP\/1\.1$/
9
+ @@header_regex = /^([^:]+):\s*([^$]+)/
10
+ @@callbacks = {}
11
+ @@accepted_origins = []
12
+
13
+ attr_accessor :connected,
14
+ :headers,
15
+ :path
16
+
17
+ def initialize *args
18
+ super
19
+ @connected = false
20
+ end
21
+
22
+ def valid_origin?
23
+ true
24
+ #@@accepted_origins.include?(@headers["Origin"])
25
+ end
26
+
27
+ def valid_path?
28
+ true
29
+ #@@callbacks.key?(@path)
30
+ end
31
+
32
+ def valid_upgrade?
33
+ true
34
+ #@headers["Upgrade"] =~ /websocket/i
35
+ end
36
+
37
+ def origin
38
+ @headers["Origin"]
39
+ end
40
+
41
+ def host
42
+ @headers["Host"]
43
+ end
44
+
45
+ def self.path name, &block
46
+ @@callbacks[name] = block
47
+ end
48
+
49
+ #tcp connection established
50
+ def post_init
51
+ @@num_connections += 1
52
+ end
53
+
54
+ #must be public for em
55
+ def unbind
56
+ @@num_connections -= 1
57
+ on_disconnect
58
+ end
59
+
60
+ # Frames need to start with 0x00-0x7f byte and end with
61
+ # an 0xFF byte. Per spec, we can also set the first
62
+ # byte to a value betweent 0x80 and 0xFF, followed by
63
+ # a leading length indicator. No support yet
64
+ def send_message msg
65
+ send_data "\x00#{msg}\xff"
66
+ end
67
+
68
+ protected
69
+
70
+ #override this method
71
+ def on_receive msg
72
+ log msg
73
+ end
74
+
75
+ #override this method
76
+ def on_connect
77
+ log "connected"
78
+ end
79
+
80
+ #override this method
81
+ def on_disconnect
82
+ log "disconnected"
83
+ end
84
+
85
+ def log msg
86
+ if @@logger
87
+ @@logger.info msg
88
+ else
89
+ puts msg
90
+ end
91
+ end
92
+
93
+ private
94
+
95
+ # when the connection receives data from the client
96
+ # we either handshake, accept the start command
97
+ # or handle the message at the app layer
98
+ def receive_data data
99
+ unless @connected
100
+ handshake data
101
+ else
102
+ msg = data.gsub(/^(\x00)|(\xff)$/, "")
103
+
104
+ if msg == "start"
105
+ on_connect
106
+ else
107
+ on_receive msg
108
+ end
109
+ end
110
+ end
111
+
112
+ # parse the headers, validate the origin and path
113
+ # and respond with appropiate headers for a
114
+ # healthy relationship with the client
115
+ def handshake data
116
+
117
+ #convert the headers to a hash
118
+ parse_headers data
119
+
120
+ # close the connection if the connection
121
+ # originates from an invalid source
122
+ close_connection unless valid_origin?
123
+
124
+ # close the connection if a callback
125
+ # is not registered for the path
126
+ close_connection unless valid_path?
127
+
128
+ # don't respond to non-websocket HTTP requests
129
+ close_connection unless valid_upgrade?
130
+
131
+ #complete the handshake
132
+ send_headers
133
+
134
+ @connected = true
135
+ end
136
+
137
+ # send the handshake response headers to
138
+ # complete the initial com
139
+ def send_headers
140
+
141
+ response = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
142
+ response << "Upgrade: WebSocket\r\n"
143
+ response << "Connection: Upgrade\r\n"
144
+ response << "WebSocket-Origin: #{origin}\r\n"
145
+ response << "WebSocket-Location: ws://#{host}#{path}\r\n\r\n"
146
+
147
+ send_data response
148
+ end
149
+
150
+ # turn http style headers into a ruby hash
151
+ # TODO: this is probably not done "well"
152
+ def parse_headers data
153
+ lines = data.split("\r\n")
154
+
155
+ @path = @@path_regex.match(lines.shift)[1]
156
+ @headers = {}
157
+
158
+ lines.each do |line|
159
+ kvp = @@header_regex.match(line)
160
+ @headers[kvp[1].strip] = kvp[2].strip
161
+ end
162
+ end
163
+
164
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-websocket-server
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - Dan Simpson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-14 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: eventmachine
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.12.4
24
+ version:
25
+ description: An evented ruby websocket server built on top of EventMachine
26
+ email: dan.simpson@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - README.markdown
35
+ - em-websocket-server.gemspec
36
+ - examples/chat.rb
37
+ - examples/chat.html
38
+ - lib/em-websocket-server.rb
39
+ has_rdoc: true
40
+ homepage: http://github.com/dansimpson/em-websocket-server
41
+ licenses: []
42
+
43
+ post_install_message:
44
+ rdoc_options: []
45
+
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.3.5
64
+ signing_key:
65
+ specification_version: 3
66
+ summary: An evented ruby websocket server built on top of EventMachine
67
+ test_files: []
68
+