em-websocket 0.5.1 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Gemfile +1 -1
- data/LICENCE +7 -0
- data/README.md +1 -5
- data/lib/em-websocket/connection.rb +40 -4
- data/lib/em-websocket/handshake04.rb +10 -1
- data/lib/em-websocket/handshake75.rb +11 -1
- data/lib/em-websocket/message_processor_06.rb +16 -2
- data/lib/em-websocket/version.rb +1 -1
- data/spec/integration/common_spec.rb +27 -0
- data/spec/integration/draft06_spec.rb +1 -0
- data/spec/integration/draft13_spec.rb +1 -0
- data/spec/integration/shared_examples.rb +13 -0
- metadata +9 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b5ae1d6db2d88e23c98fce247af5c18d9af7343d196cc85224d2d62e6f39cf6b
|
4
|
+
data.tar.gz: eff929c27eb3949705cf0e5438af33c7b05612c5e5deedaf2ea39a81f0308559
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 301e06c551ea5414cd463a606fc709676932094bdf92743555cf5ecccb20fb0c0d7abe14c19a61942337ea8c60053bd59d7b31841178df9f05dbae3c6e66232f
|
7
|
+
data.tar.gz: 5020a0dd90126780266b55c197acadcbabc40044e5513b4461f4f4d9500d9ace38646c931aeb19fd638b852a68346b5cb44c48b9cc5e5b5f2e96e3a80ff2cbf4
|
data/Gemfile
CHANGED
data/LICENCE
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright (c) 2009-2014 Ilya Grigorik, Martyn Loughran
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -73,7 +73,7 @@ If unsure use a code in the 4xxx range. em-websocket may also close a connection
|
|
73
73
|
|
74
74
|
## Secure server
|
75
75
|
|
76
|
-
It is possible to accept secure `wss://` connections by passing `:secure => true` when opening the connection. Pass a `:tls_options` hash containing keys as described in http://
|
76
|
+
It is possible to accept secure `wss://` connections by passing `:secure => true` when opening the connection. Pass a `:tls_options` hash containing keys as described in http://www.rubydoc.info/github/eventmachine/eventmachine/EventMachine/Connection:start_tls
|
77
77
|
|
78
78
|
**Warning**: Safari 5 does not currently support prompting on untrusted SSL certificates therefore using a self signed certificate may leave you scratching your head.
|
79
79
|
|
@@ -140,7 +140,3 @@ Using flash emulation does require some minimal support from em-websocket which
|
|
140
140
|
* [Twitter AMQP WebSocket Example](http://github.com/rubenfonseca/twitter-amqp-websocket-example)
|
141
141
|
* examples/multicast.rb - broadcast all ruby tweets to all subscribers
|
142
142
|
* examples/echo.rb - server <> client exchange via a websocket
|
143
|
-
|
144
|
-
# License
|
145
|
-
|
146
|
-
The MIT License - Copyright (c) 2009-2013 Ilya Grigorik, Martyn Loughran
|
@@ -45,6 +45,7 @@ module EventMachine
|
|
45
45
|
@secure_proxy = options[:secure_proxy] || false
|
46
46
|
@tls_options = options[:tls_options] || {}
|
47
47
|
@close_timeout = options[:close_timeout]
|
48
|
+
@outbound_limit = options[:outbound_limit] || 0
|
48
49
|
|
49
50
|
@handler = nil
|
50
51
|
|
@@ -88,6 +89,16 @@ module EventMachine
|
|
88
89
|
trigger_on_error(e) || raise(e)
|
89
90
|
end
|
90
91
|
|
92
|
+
def send_data(data)
|
93
|
+
if @outbound_limit > 0 &&
|
94
|
+
get_outbound_data_size + data.bytesize > @outbound_limit
|
95
|
+
abort(:outbound_limit_reached)
|
96
|
+
return 0
|
97
|
+
end
|
98
|
+
|
99
|
+
super(data)
|
100
|
+
end
|
101
|
+
|
91
102
|
def unbind
|
92
103
|
debug [:unbind, :connection]
|
93
104
|
|
@@ -99,7 +110,9 @@ module EventMachine
|
|
99
110
|
end
|
100
111
|
|
101
112
|
def dispatch(data)
|
102
|
-
if data.match(
|
113
|
+
if data.match(%r|^GET /healthcheck|)
|
114
|
+
send_healthcheck_response
|
115
|
+
elsif data.match(/\A<policy-file-request\s*\/>/)
|
103
116
|
send_flash_cross_domain_file
|
104
117
|
else
|
105
118
|
@handshake ||= begin
|
@@ -118,7 +131,7 @@ module EventMachine
|
|
118
131
|
debug [:error, e]
|
119
132
|
trigger_on_error(e)
|
120
133
|
# Handshake errors require the connection to be aborted
|
121
|
-
abort
|
134
|
+
abort(:handshake_error)
|
122
135
|
}
|
123
136
|
|
124
137
|
handshake
|
@@ -128,6 +141,23 @@ module EventMachine
|
|
128
141
|
end
|
129
142
|
end
|
130
143
|
|
144
|
+
def send_healthcheck_response
|
145
|
+
debug [:healthcheck, 'OK']
|
146
|
+
|
147
|
+
healthcheck_res = ["HTTP/1.1 200 OK"]
|
148
|
+
healthcheck_res << "Content-Type: text/plain"
|
149
|
+
healthcheck_res << "Content-Length: 2"
|
150
|
+
|
151
|
+
healthcheck_res = healthcheck_res.join("\r\n") + "\r\n\r\nOK"
|
152
|
+
|
153
|
+
send_data healthcheck_res
|
154
|
+
|
155
|
+
# handle the healthcheck request transparently
|
156
|
+
# no need to notify the user about this connection
|
157
|
+
@onclose = nil
|
158
|
+
close_connection_after_writing
|
159
|
+
end
|
160
|
+
|
131
161
|
def send_flash_cross_domain_file
|
132
162
|
file = '<?xml version="1.0"?><cross-domain-policy><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>'
|
133
163
|
debug [:cross_domain, file]
|
@@ -236,6 +266,11 @@ module EventMachine
|
|
236
266
|
@handler ? @handler.state : :handshake
|
237
267
|
end
|
238
268
|
|
269
|
+
# Returns the IP address for the remote peer
|
270
|
+
def remote_ip
|
271
|
+
get_peername[2,6].unpack('nC4')[1..4].join('.')
|
272
|
+
end
|
273
|
+
|
239
274
|
# Returns the maximum frame size which this connection is configured to
|
240
275
|
# accept. This can be set globally or on a per connection basis, and
|
241
276
|
# defaults to a value of 10MB if not set.
|
@@ -256,7 +291,8 @@ module EventMachine
|
|
256
291
|
|
257
292
|
# As definited in draft 06 7.2.2, some failures require that the server
|
258
293
|
# abort the websocket connection rather than close cleanly
|
259
|
-
def abort
|
294
|
+
def abort(reason)
|
295
|
+
debug [:abort, reason]
|
260
296
|
close_connection
|
261
297
|
end
|
262
298
|
|
@@ -266,7 +302,7 @@ module EventMachine
|
|
266
302
|
@handler.close_websocket(code, body)
|
267
303
|
else
|
268
304
|
# The handshake hasn't completed - should be safe to terminate
|
269
|
-
abort
|
305
|
+
abort(:handshake_incomplete)
|
270
306
|
end
|
271
307
|
end
|
272
308
|
|
@@ -17,12 +17,21 @@ module EventMachine
|
|
17
17
|
upgrade << "Upgrade: websocket"
|
18
18
|
upgrade << "Connection: Upgrade"
|
19
19
|
upgrade << "Sec-WebSocket-Accept: #{signature}"
|
20
|
+
if protocol = headers['sec-websocket-protocol']
|
21
|
+
validate_protocol!(protocol)
|
22
|
+
upgrade << "Sec-WebSocket-Protocol: #{protocol}"
|
23
|
+
end
|
20
24
|
|
21
|
-
# TODO: Support sec-websocket-protocol
|
25
|
+
# TODO: Support sec-websocket-protocol selection
|
22
26
|
# TODO: sec-websocket-extensions
|
23
27
|
|
24
28
|
return upgrade.join("\r\n") + "\r\n\r\n"
|
25
29
|
end
|
30
|
+
|
31
|
+
def self.validate_protocol!(protocol)
|
32
|
+
raise HandshakeError, "Invalid WebSocket-Protocol: empty" if protocol.empty?
|
33
|
+
# TODO: Validate characters
|
34
|
+
end
|
26
35
|
end
|
27
36
|
end
|
28
37
|
end
|
@@ -9,10 +9,20 @@ module EventMachine
|
|
9
9
|
upgrade << "Upgrade: WebSocket\r\n"
|
10
10
|
upgrade << "Connection: Upgrade\r\n"
|
11
11
|
upgrade << "WebSocket-Origin: #{headers['origin']}\r\n"
|
12
|
-
upgrade << "WebSocket-Location: #{location}\r\n
|
12
|
+
upgrade << "WebSocket-Location: #{location}\r\n"
|
13
|
+
if protocol = headers['sec-websocket-protocol']
|
14
|
+
validate_protocol!(protocol)
|
15
|
+
upgrade << "Sec-WebSocket-Protocol: #{protocol}\r\n"
|
16
|
+
end
|
17
|
+
upgrade << "\r\n"
|
13
18
|
|
14
19
|
return upgrade
|
15
20
|
end
|
21
|
+
|
22
|
+
def self.validate_protocol!(protocol)
|
23
|
+
raise HandshakeError, "Invalid WebSocket-Protocol: empty" if protocol.empty?
|
24
|
+
# TODO: Validate characters
|
25
|
+
end
|
16
26
|
end
|
17
27
|
end
|
18
28
|
end
|
@@ -37,8 +37,22 @@ module EventMachine
|
|
37
37
|
@connection.close_connection_after_writing
|
38
38
|
end
|
39
39
|
when :ping
|
40
|
-
#
|
41
|
-
|
40
|
+
# There are a couple of protections here against malicious/broken WebSocket abusing ping frames.
|
41
|
+
#
|
42
|
+
# 1. Delay 200ms before replying. This reduces the number of pings from WebSocket clients behaving as
|
43
|
+
# `for (;;) { send_ping(conn); rcv_pong(conn); }`. The spec says we "SHOULD respond with Pong frame as soon
|
44
|
+
# as is practical".
|
45
|
+
# 2. Reply at most every 200ms. This reduces the number of pong frames sent to WebSocket clients behaving as
|
46
|
+
# `for (;;) { send_ping(conn); }`. The spec says "If an endpoint receives a Ping frame and has not yet sent
|
47
|
+
# Pong frame(s) in response to previous Ping frame(s), the endpoint MAY elect to send a Pong frame for only
|
48
|
+
# the most recently processed Ping frame."
|
49
|
+
@most_recent_pong_application_data = application_data
|
50
|
+
if @pong_timer == nil then
|
51
|
+
@pong_timer = EventMachine.add_timer(0.2) do
|
52
|
+
@pong_timer = nil
|
53
|
+
send_frame(:pong, @most_recent_pong_application_data)
|
54
|
+
end
|
55
|
+
end
|
42
56
|
@connection.trigger_on_ping(application_data)
|
43
57
|
when :pong
|
44
58
|
@connection.trigger_on_pong(application_data)
|
data/lib/em-websocket/version.rb
CHANGED
@@ -108,4 +108,31 @@ describe "WebSocket server" do
|
|
108
108
|
end
|
109
109
|
}
|
110
110
|
end
|
111
|
+
|
112
|
+
context "outbound limit set" do
|
113
|
+
it "should close the connection if the limit is reached" do
|
114
|
+
em {
|
115
|
+
start_server(:outbound_limit => 150) do |ws|
|
116
|
+
# Increase the message size by one on each loop
|
117
|
+
ws.onmessage{|msg| ws.send(msg + "x") }
|
118
|
+
ws.onclose{|status|
|
119
|
+
status[:code].should == 1006 # Unclean
|
120
|
+
status[:was_clean].should be false
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
EM.add_timer(0.1) do
|
125
|
+
ws = EventMachine::WebSocketClient.connect('ws://127.0.0.1:12345/')
|
126
|
+
ws.callback { ws.send_msg "hello" }
|
127
|
+
ws.disconnect { done } # Server closed the connection
|
128
|
+
ws.stream { |msg|
|
129
|
+
# minus frame size ? (getting 146 max here)
|
130
|
+
msg.data.size.should <= 150
|
131
|
+
# Return back the message
|
132
|
+
ws.send_msg(msg.data)
|
133
|
+
}
|
134
|
+
end
|
135
|
+
}
|
136
|
+
end
|
137
|
+
end
|
111
138
|
end
|
@@ -29,6 +29,19 @@ shared_examples_for "a websocket server" do
|
|
29
29
|
}
|
30
30
|
end
|
31
31
|
|
32
|
+
it "should expose the remote IP address" do
|
33
|
+
em {
|
34
|
+
start_server { |ws|
|
35
|
+
ws.onopen {
|
36
|
+
ws.remote_ip.should == "127.0.0.1"
|
37
|
+
done
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
start_client
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
32
45
|
it "should send messages successfully" do
|
33
46
|
em {
|
34
47
|
start_server { |ws|
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: em-websocket
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ilya Grigorik
|
8
8
|
- Martyn Loughran
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2020-09-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine
|
@@ -50,6 +50,7 @@ files:
|
|
50
50
|
- ".gitignore"
|
51
51
|
- CHANGELOG.rdoc
|
52
52
|
- Gemfile
|
53
|
+
- LICENCE
|
53
54
|
- README.md
|
54
55
|
- Rakefile
|
55
56
|
- em-websocket.gemspec
|
@@ -101,9 +102,10 @@ files:
|
|
101
102
|
- spec/unit/handshake_spec.rb
|
102
103
|
- spec/unit/masking_spec.rb
|
103
104
|
homepage: http://github.com/igrigorik/em-websocket
|
104
|
-
licenses:
|
105
|
+
licenses:
|
106
|
+
- MIT
|
105
107
|
metadata: {}
|
106
|
-
post_install_message:
|
108
|
+
post_install_message:
|
107
109
|
rdoc_options: []
|
108
110
|
require_paths:
|
109
111
|
- lib
|
@@ -118,9 +120,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
118
120
|
- !ruby/object:Gem::Version
|
119
121
|
version: '0'
|
120
122
|
requirements: []
|
121
|
-
|
122
|
-
|
123
|
-
signing_key:
|
123
|
+
rubygems_version: 3.0.3
|
124
|
+
signing_key:
|
124
125
|
specification_version: 4
|
125
126
|
summary: EventMachine based WebSocket server
|
126
127
|
test_files:
|