websocket-eventmachine-server 1.0.0 → 1.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.
@@ -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: