websocket-eventmachine-server 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.1
4
+
5
+ - Extract most functionality to websocket-eventmachine-base gem to reuse with client
6
+
3
7
  ## 1.0.0
4
8
 
5
9
  - initial release
data/README.md CHANGED
@@ -2,11 +2,12 @@
2
2
 
3
3
  WebSocket-EventMachine-Server is Ruby WebSocket server based on EventMachine.
4
4
 
5
- ## Why another WebSocket server?
5
+ - [Autobahn tests](http://imanel.github.com/websocket-ruby/autobahn/server)
6
+ - [Docs](http://rdoc.info/github/imanel/websocket-eventmachine-server/master/frames)
6
7
 
7
- There are multiple Ruby WebSocket servers, each with different quirks and errors. Most commonly used em-websocket is unfortunately slow and have multiple bugs(see Autobahn tests below). This library was created to fix most of them.
8
+ ## Why another WebSocket server?
8
9
 
9
- [Autobahn tests](http://imanel.github.com/websocket-ruby/autobahn/server)
10
+ There are multiple Ruby WebSocket servers, each with different quirks and errors. Most commonly used em-websocket is unfortunately slow and have multiple bugs(see Autobahn tests above). This library was created to fix most of them.
10
11
 
11
12
  ## Installation
12
13
 
@@ -23,63 +24,191 @@ gem 'websocket-eventmachine-server'
23
24
  ## Simple server example
24
25
 
25
26
  ```ruby
26
- EventMachine.run {
27
+ EM.run do
27
28
 
28
- WebSocket::EventMachine::Server.start(:host => "0.0.0.0", :port => 8080) do |ws|
29
- ws.onopen {
30
- puts "WebSocket connection open"
29
+ WebSocket::EventMachine::Server.start(:host => "0.0.0.0", :port => 8080) do |ws|
30
+ ws.onopen do
31
+ puts "Client connected"
32
+ end
31
33
 
32
- # publish message to the client
33
- ws.send "Hello Client"
34
- }
34
+ ws.onmessage do |msg, type|
35
+ puts "Received message: #{msg}"
36
+ ws.send msg, :type => type
37
+ end
35
38
 
36
- ws.onclose { puts "Connection closed" }
37
- ws.onmessage { |msg|
38
- puts "Recieved message: #{msg}"
39
- ws.send "Pong: #{msg}"
40
- }
39
+ ws.onclose do
40
+ puts "Client disconnected"
41
41
  end
42
- }
42
+ end
43
+
44
+ end
45
+ ```
46
+
47
+ ## Options
48
+
49
+ Following options can be passed to WebSocket::EventMachine::Server initializer:
50
+
51
+ - `[String] :host` - IP on which server should accept connections. '0.0.0.0' means all.
52
+ - `[Integer] :port` - Port on which server should accept connections.
53
+ - `[Boolean] :secure` - Enable secure WSS protocol. This will enable both SSL encryption and using WSS url and require `tls_options` key.
54
+ - `[Boolean] :secure_proxy` - Enable secure WSS protocol over proxy. This will enable only using WSS url and assume that SSL encryption is handled by some kind proxy(like [Stunnel](http://www.stunnel.org/))
55
+ - `[Hash] :tls_options` - Options for SSL(according to [EventMachine start_tls method](http://eventmachine.rubyforge.org/EventMachine/Connection.html#start_tls-instance_method))
56
+ - `[String] :private_key_file` - URL to private key file
57
+ - `[String] :cert_chain_file` - URL to cert chain file
58
+
59
+ ## Methods
60
+
61
+ Following methods are available for WebSocket::EventMachine::Server object:
62
+
63
+ ### onopen
64
+
65
+ Called after client is connected.
66
+
67
+ Example:
68
+
69
+ ```ruby
70
+ ws.onopen do
71
+ puts "Client connected"
72
+ end
73
+ ```
74
+
75
+ ### onclose
76
+
77
+ Called after client closed connection.
78
+
79
+ Example:
80
+
81
+ ```ruby
82
+ ws.onclose do
83
+ puts "Client disconnected"
84
+ end
85
+ ```
86
+
87
+ ### onmessage
88
+
89
+ Called when server receive message.
90
+
91
+ Parameters:
92
+
93
+ - `[String] message` - content of message
94
+ - `[Symbol] type` - type is type of message(:text or :binary)
95
+
96
+ Example:
97
+
98
+ ```ruby
99
+ ws.onmessage do |msg, type|
100
+ puts "Received message: #{msg} or type: #{type}"
101
+ end
102
+ ```
103
+
104
+ ### onerror
105
+
106
+ Called when server discovers error.
107
+
108
+ Parameters:
109
+
110
+ - `[String] error` - error reason.
111
+
112
+ Example:
113
+
114
+ ```ruby
115
+ ws.onerror do |error|
116
+ puts "Error occured: #{error}"
117
+ end
43
118
  ```
44
119
 
45
- ## Secure server
120
+ ### onping
121
+
122
+ Called when server receive ping request. Pong request is sent automatically.
46
123
 
47
- It is possible to accept secure wss:// connections by passing :secure => true when opening the connection. Safari 5 does not currently support prompting on untrusted SSL certificates therefore using signed certificates is highly recommended. Pass a :tls_options hash containing keys as described in http://eventmachine.rubyforge.org/EventMachine/Connection.html#M000296
124
+ Parameters:
48
125
 
49
- For example,
126
+ - `[String] message` - message for ping request.
127
+
128
+ Example:
50
129
 
51
130
  ```ruby
52
- WebSocket::EventMachine::Server.start({
53
- :host => "0.0.0.0",
54
- :port => 443,
55
- :secure => true,
56
- :tls_options => {
57
- :private_key_file => "/private/key",
58
- :cert_chain_file => "/ssl/certificate"
59
- }
60
- }) do |ws|
61
- ...
131
+ ws.onping do |message|
132
+ puts "Ping received: #{message}"
62
133
  end
63
134
  ```
64
135
 
65
- ## Running behind an SSL Proxy/Terminator, like Stunnel
136
+ ### onpong
137
+
138
+ Called when server receive pong response.
66
139
 
67
- The :secure_proxy => true option makes it possible to run correctly when behind a secure SSL proxy/terminator like [Stunnel](http://www.stunnel.org/). When setting :secure_proxy => true, any reponse from the em-websocket which contains the websocket url will use the wss:// url scheme. None of the traffic is encrypted.
140
+ Parameters:
68
141
 
69
- This option is necessary when using websockets with an SSL proxy/terminator on Safari 5.1.x or earlier, and also on Safari in iOS 5.x and earlier. Most versions of Chrome, Safari 5.2, and Safari in iOS 6 do not appear to have this problem.
142
+ - `[String] message` - message for pong response.
70
143
 
71
- For example,
144
+ Example:
72
145
 
73
146
  ```ruby
74
- WebSocket::EventMachine::Server.start({
75
- :host => "0.0.0.0",
76
- :port => 8080,
77
- :secure_proxy => true
78
- }) do |ws|
79
- ...
147
+ ws.onpong do |message|
148
+ puts "Pong received: #{message}"
80
149
  end
81
150
  ```
82
151
 
152
+ ### send
153
+
154
+ Sends message to client.
155
+
156
+ Parameters:
157
+
158
+ - `[String] message` - message that should be sent to client
159
+ - `[Hash] params` - params for message(optional)
160
+ - `[Symbol] :type` - type of message. Valid values are :text, :binary(default is :text)
161
+
162
+ Example:
163
+
164
+ ```ruby
165
+ ws.send "Hello Client!"
166
+ ws.send "binary data", :type => :binary
167
+ ```
168
+
169
+ ### close
170
+
171
+ Closes connection and optionally send close frame to client.
172
+
173
+ Parameters:
174
+
175
+ - `[Integer] code` - code of closing, according to WebSocket specification(optional)
176
+ - `[String] data` - data to send in closing frame(optional)
177
+
178
+ Example:
179
+
180
+ ```ruby
181
+ ws.close
182
+ ```
183
+
184
+ ### ping
185
+
186
+ Sends ping request.
187
+
188
+ Parameters:
189
+
190
+ - `[String] data` - data to send in ping request(optional)
191
+
192
+ Example:
193
+
194
+ ```ruby
195
+ ws.ping 'Hi'
196
+ ```
197
+
198
+ ### pong
199
+
200
+ Sends pong request. Usually there should be no need to send this request, as pong responses are sent automatically by server.
201
+
202
+ Parameters:
203
+
204
+ - `[String] data` - data to send in pong request(optional)
205
+
206
+ Example:
207
+
208
+ ``` ruby
209
+ ws.pong 'Hello'
210
+ ```
211
+
83
212
  ## Migrating from EM-WebSocket
84
213
 
85
214
  This library is compatible with EM-WebSocket, so only thing you need to change is running server - you need to change from EM-WebSocket to WebSocket::EventMachine::Server in your application and everything will be working.
@@ -1,5 +1,4 @@
1
- require 'websocket'
2
- require 'eventmachine'
1
+ require 'websocket-eventmachine-base'
3
2
 
4
3
  module WebSocket
5
4
  module EventMachine
@@ -12,7 +11,7 @@ module WebSocket
12
11
  # ws.onclose { puts "WebSocket closed" }
13
12
  # ws.onerror { |e| puts "Error: #{e}" }
14
13
  # end
15
- class Server < ::EventMachine::Connection
14
+ class Server < Base
16
15
 
17
16
  ###########
18
17
  ### API ###
@@ -41,188 +40,86 @@ module WebSocket
41
40
  @tls_options = args[:tls_options] || {}
42
41
  end
43
42
 
43
+ ############################
44
+ ### EventMachine methods ###
45
+ ############################
46
+
47
+ # Eventmachine internal
48
+ # @private
49
+ def post_init
50
+ @state = :connecting
51
+ @handshake = WebSocket::Handshake::Server.new(:secure => @secure_proxy)
52
+ start_tls(@tls_options) if @secure
53
+ end
54
+
55
+ #######################
56
+ ### Private methods ###
57
+ #######################
58
+
59
+ private
60
+
61
+ def incoming_frame
62
+ WebSocket::Frame::Incoming::Server
63
+ end
64
+
65
+ def outgoing_frame
66
+ WebSocket::Frame::Outgoing::Server
67
+ end
68
+
69
+ public
70
+
71
+ #########################
72
+ ### Inherited methods ###
73
+ #########################
74
+
44
75
  # Called when connection is opened.
45
76
  # No parameters are passed to block
46
- def onopen(&blk); @onopen = blk; end
77
+ def onopen(&blk); super; end
47
78
 
48
79
  # Called when connection is closed.
49
80
  # No parameters are passed to block
50
- def onclose(&blk); @onclose = blk; end
81
+ def onclose(&blk); super; end
51
82
 
52
83
  # Called when error occurs.
53
84
  # One parameter passed to block:
54
85
  # error - string with error message
55
- def onerror(&blk); @onerror = blk; end
86
+ def onerror(&blk); super; end
56
87
 
57
- # Called when message is received from server.
88
+ # Called when message is received.
58
89
  # Two parameters passed to block:
59
- # message - string with message sent to server
90
+ # message - string with received message
60
91
  # type - type of message. Valid values are :text and :binary
61
- def onmessage(&blk); @onmessage = blk; end
92
+ def onmessage(&blk); super; end
62
93
 
63
- # Called when ping message is received from server.
94
+ # Called when ping message is received
64
95
  # One parameter passed to block:
65
96
  # message - string with ping message
66
- def onping(&blk); @onping = blk; end
97
+ def onping(&blk); super; end
67
98
 
68
- # Called when pond message is received from server.
99
+ # Called when pond message is received
69
100
  # One parameter passed to block:
70
101
  # message - string with pong message
71
- def onpong(&blk); @onpong = blk; end
102
+ def onpong(&blk); super; end
72
103
 
73
- # Send data to client
104
+ # Send data
74
105
  # @param data [String] Data to send
75
106
  # @param args [Hash] Arguments for send
76
107
  # @option args [String] :type Type of frame to send - available types are "text", "binary", "ping", "pong" and "close"
77
108
  # @option args [Integer] :code Code for close frame
78
109
  # @return [Boolean] true if data was send, otherwise call on_error if needed
79
- def send(data, args = {})
80
- type = args[:type] || :text
81
- unless type == :plain
82
- frame = WebSocket::Frame::Outgoing::Server.new args.merge(:version => @handshake.version, :data => data)
83
- if !frame.supported?
84
- trigger_onerror("Frame type '#{type}' is not supported in protocol version #{@handshake.version}")
85
- return false
86
- elsif !frame.require_sending?
87
- return false
88
- end
89
- data = frame.to_s
90
- end
91
- debug "Sending raw: ", data
92
- send_data(data)
93
- true
94
- end
110
+ def send(data, args = {}); super; end
95
111
 
96
112
  # Close connection
97
- # @return [Boolean] true if connection is closed immediately, false if waiting for server to close connection
98
- def close(code = 1000, data = nil)
99
- if @state == :open
100
- @state = :closing
101
- return false if send(data, :type => :close, :code => code)
102
- else
103
- send(data, :type => :close) if @state == :closing
104
- @state = :closed
105
- end
106
- close_connection_after_writing
107
- true
108
- end
113
+ # @return [Boolean] true if connection is closed immediately, false if waiting for other side to close connection
114
+ def close(code = 1000, data = nil); super; end
109
115
 
110
- # Send ping message to client
116
+ # Send ping message
111
117
  # @return [Boolean] false if protocol version is not supporting ping requests
112
- def ping(data = '')
113
- send(data, :type => :ping)
114
- end
118
+ def ping(data = ''); super; end
115
119
 
116
- # Send pong message to client
120
+ # Send pong message
117
121
  # @return [Boolean] false if protocol version is not supporting pong requests
118
- def pong(data = '')
119
- send(data, :type => :pong)
120
- end
121
-
122
- ############################
123
- ### EventMachine methods ###
124
- ############################
125
-
126
- # Eventmachine internal
127
- # @private
128
- def post_init
129
- @state = :connecting
130
- @handshake = WebSocket::Handshake::Server.new(:secure => @secure_proxy)
131
- start_tls(@tls_options) if @secure
132
- end
133
-
134
- # Eventmachine internal
135
- # @private
136
- def receive_data(data)
137
- debug "Received raw: ", data
138
- case @state
139
- when :connecting then handle_connecting(data)
140
- when :open then handle_open(data)
141
- when :closing then handle_closing(data)
142
- end
143
- end
144
-
145
- # Eventmachine internal
146
- # @private
147
- def unbind
148
- unless @state == :closed
149
- @state = :closed
150
- close
151
- trigger_onclose('')
152
- end
153
- end
154
-
155
- #######################
156
- ### Private methods ###
157
- #######################
158
-
159
- private
160
-
161
- ['onopen'].each do |m|
162
- define_method "trigger_#{m}" do
163
- callback = instance_variable_get("@#{m}")
164
- callback.call if callback
165
- end
166
- end
167
-
168
- ['onerror', 'onping', 'onpong', 'onclose'].each do |m|
169
- define_method "trigger_#{m}" do |data|
170
- callback = instance_variable_get("@#{m}")
171
- callback.call(data) if callback
172
- end
173
- end
174
-
175
- def trigger_onmessage(data, type)
176
- @onmessage.call(data, type) if @onmessage
177
- end
178
-
179
- def handle_connecting(data)
180
- @handshake << data
181
- return unless @handshake.finished?
182
- if @handshake.valid?
183
- send(@handshake.to_s, :type => :plain) if @handshake.should_respond?
184
- @frame = WebSocket::Frame::Incoming::Server.new(:version => @handshake.version)
185
- @state = :open
186
- trigger_onopen
187
- handle_open(@handshake.leftovers) if @handshake.leftovers
188
- else
189
- trigger_onerror(@handshake.error)
190
- close
191
- end
192
- end
193
-
194
- def handle_open(data)
195
- @frame << data
196
- while frame = @frame.next
197
- case frame.type
198
- when :close
199
- @state = :closing
200
- close
201
- trigger_onclose(frame.to_s)
202
- when :ping
203
- pong(frame.to_s)
204
- trigger_onping(frame.to_s)
205
- when :pong
206
- trigger_onpong(frame.to_s)
207
- when :text
208
- trigger_onmessage(frame.to_s, :text)
209
- when :binary
210
- trigger_onmessage(frame.to_s, :binary)
211
- end
212
- end
213
- unbind if @frame.error?
214
- end
215
-
216
- def handle_closing(data)
217
- @state = :closed
218
- close
219
- trigger_onclose
220
- end
221
-
222
- def debug(description, data)
223
- return unless @debug
224
- puts(description + data.bytes.to_a.collect{|b| '\x' + b.to_s(16).rjust(2, '0')}.join) unless @state == :connecting
225
- end
122
+ def pong(data = ''); super; end
226
123
 
227
124
  end
228
125
  end
@@ -1,7 +1,7 @@
1
1
  module WebSocket
2
2
  module EventMachine
3
3
  class Server
4
- VERSION = '1.0.0'
4
+ VERSION = '1.0.1'
5
5
  end
6
6
  end
7
7
  end
@@ -12,9 +12,7 @@ Gem::Specification.new do |s|
12
12
  s.summary = %q{WebSocket server for Ruby}
13
13
  s.description = %q{WebSocket server for Ruby}
14
14
 
15
- s.add_dependency 'websocket', '~> 1.0'
16
- s.add_dependency 'websocket-native', '~> 1.0'
17
- s.add_dependency 'eventmachine', '~> 1.0'
15
+ s.add_dependency 'websocket-eventmachine-base', '~> 1.0'
18
16
 
19
17
  s.files = `git ls-files`.split("\n")
20
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: websocket-eventmachine-server
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,42 +9,10 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-19 00:00:00.000000000 Z
12
+ date: 2012-12-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: websocket
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ~>
20
- - !ruby/object:Gem::Version
21
- version: '1.0'
22
- type: :runtime
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ~>
28
- - !ruby/object:Gem::Version
29
- version: '1.0'
30
- - !ruby/object:Gem::Dependency
31
- name: websocket-native
32
- requirement: !ruby/object:Gem::Requirement
33
- none: false
34
- requirements:
35
- - - ~>
36
- - !ruby/object:Gem::Version
37
- version: '1.0'
38
- type: :runtime
39
- prerelease: false
40
- version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
- requirements:
43
- - - ~>
44
- - !ruby/object:Gem::Version
45
- version: '1.0'
46
- - !ruby/object:Gem::Dependency
47
- name: eventmachine
15
+ name: websocket-eventmachine-base
48
16
  requirement: !ruby/object:Gem::Requirement
49
17
  none: false
50
18
  requirements: