faye-websocket 0.10.6 → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +83 -51
- data/LICENSE.md +12 -0
- data/README.md +76 -72
- data/lib/faye/eventsource.rb +4 -4
- data/lib/faye/rack_stream.rb +1 -0
- data/lib/faye/websocket.rb +5 -4
- data/lib/faye/websocket/api.rb +13 -6
- data/lib/faye/websocket/api/event.rb +1 -1
- data/lib/faye/websocket/api/event_target.rb +2 -2
- data/lib/faye/websocket/client.rb +57 -34
- data/lib/faye/websocket/ssl_verifier.rb +89 -0
- metadata +28 -45
- data/examples/app.rb +0 -54
- data/examples/autobahn_client.rb +0 -47
- data/examples/client.rb +0 -34
- data/examples/config.ru +0 -13
- data/examples/haproxy.conf +0 -20
- data/examples/proxy_server.rb +0 -14
- data/examples/rainbows.conf +0 -3
- data/examples/server.rb +0 -50
- data/examples/sse.html +0 -38
- data/examples/ws.html +0 -43
data/lib/faye/eventsource.rb
CHANGED
@@ -63,7 +63,7 @@ module Faye
|
|
63
63
|
end
|
64
64
|
|
65
65
|
private
|
66
|
-
|
66
|
+
|
67
67
|
def open
|
68
68
|
return unless @ready_state == WebSocket::API::CONNECTING
|
69
69
|
|
@@ -83,9 +83,9 @@ module Faye
|
|
83
83
|
gsub(/(\r\n|\r|\n)/, '\1data: ')
|
84
84
|
|
85
85
|
frame = ""
|
86
|
-
frame << "event: #{options[:event]}\r\n" if options[:event]
|
87
|
-
frame << "id: #{options[:id]}\r\n" if options[:id]
|
88
|
-
frame << "data: #{message}\r\n\r\n"
|
86
|
+
frame << "event: #{ options[:event] }\r\n" if options[:event]
|
87
|
+
frame << "id: #{ options[:id] }\r\n" if options[:id]
|
88
|
+
frame << "data: #{ message }\r\n\r\n"
|
89
89
|
|
90
90
|
@stream.write(frame)
|
91
91
|
true
|
data/lib/faye/rack_stream.rb
CHANGED
data/lib/faye/websocket.rb
CHANGED
@@ -17,9 +17,10 @@ module Faye
|
|
17
17
|
class WebSocket
|
18
18
|
root = File.expand_path('../websocket', __FILE__)
|
19
19
|
|
20
|
-
autoload :Adapter,
|
21
|
-
autoload :API,
|
22
|
-
autoload :Client,
|
20
|
+
autoload :Adapter, root + '/adapter'
|
21
|
+
autoload :API, root + '/api'
|
22
|
+
autoload :Client, root + '/client'
|
23
|
+
autoload :SslVerifier, root + '/ssl_verifier'
|
23
24
|
|
24
25
|
ADAPTERS = {
|
25
26
|
'goliath' => :Goliath,
|
@@ -44,7 +45,7 @@ module Faye
|
|
44
45
|
def self.load_adapter(backend)
|
45
46
|
const = Kernel.const_get(ADAPTERS[backend]) rescue nil
|
46
47
|
require(backend) unless const
|
47
|
-
path = File.expand_path("../adapters/#{backend}.rb", __FILE__)
|
48
|
+
path = File.expand_path("../adapters/#{ backend }.rb", __FILE__)
|
48
49
|
require(path) if File.file?(path)
|
49
50
|
end
|
50
51
|
|
data/lib/faye/websocket/api.rb
CHANGED
@@ -43,7 +43,7 @@ module Faye
|
|
43
43
|
|
44
44
|
@driver.on(:open) { |e| open }
|
45
45
|
@driver.on(:message) { |e| receive_message(e.data) }
|
46
|
-
@driver.on(:close) { |e| begin_close(e.reason, e.code) }
|
46
|
+
@driver.on(:close) { |e| begin_close(e.reason, e.code, :wait_for_write => true) }
|
47
47
|
|
48
48
|
@driver.on(:error) do |error|
|
49
49
|
emit_error(error.message)
|
@@ -83,13 +83,16 @@ module Faye
|
|
83
83
|
unless code == 1000 or (code >= 3000 and code <= 4999)
|
84
84
|
raise ArgumentError, "Failed to execute 'close' on WebSocket: " +
|
85
85
|
"The code must be either 1000, or between 3000 and 4999. " +
|
86
|
-
"#{code} is neither."
|
86
|
+
"#{ code } is neither."
|
87
|
+
end
|
88
|
+
|
89
|
+
if @ready_state < CLOSING
|
90
|
+
@close_timer = EventMachine.add_timer(CLOSE_TIMEOUT) { begin_close('', 1006) }
|
87
91
|
end
|
88
92
|
|
89
93
|
@ready_state = CLOSING unless @ready_state == CLOSED
|
90
|
-
@driver.close(reason, code)
|
91
94
|
|
92
|
-
@
|
95
|
+
@driver.close(reason, code)
|
93
96
|
end
|
94
97
|
|
95
98
|
def protocol
|
@@ -121,13 +124,17 @@ module Faye
|
|
121
124
|
dispatch_event(event)
|
122
125
|
end
|
123
126
|
|
124
|
-
def begin_close(reason, code)
|
127
|
+
def begin_close(reason, code, options = {})
|
125
128
|
return if @ready_state == CLOSED
|
126
129
|
@ready_state = CLOSING
|
127
130
|
@close_params = [reason, code]
|
128
131
|
|
129
132
|
if @stream
|
130
|
-
|
133
|
+
if options[:wait_for_write]
|
134
|
+
@stream.close_connection_after_writing
|
135
|
+
else
|
136
|
+
@stream.close_connection
|
137
|
+
end
|
131
138
|
else
|
132
139
|
finalize_close
|
133
140
|
end
|
@@ -10,7 +10,7 @@ module Faye::WebSocket::API
|
|
10
10
|
|
11
11
|
def initialize(event_type, options)
|
12
12
|
@type = event_type
|
13
|
-
options.each { |key, value| instance_variable_set("@#{key}", value) }
|
13
|
+
options.each { |key, value| instance_variable_set("@#{ key }", value) }
|
14
14
|
end
|
15
15
|
|
16
16
|
def init_event(event_type, can_bubble, cancelable)
|
@@ -5,10 +5,10 @@ module Faye::WebSocket::API
|
|
5
5
|
events = %w[open message error close]
|
6
6
|
|
7
7
|
events.each do |event_type|
|
8
|
-
define_method "on#{event_type}=" do |handler|
|
8
|
+
define_method "on#{ event_type }=" do |handler|
|
9
9
|
EventMachine.next_tick do
|
10
10
|
flush(event_type, handler)
|
11
|
-
instance_variable_set("@on#{event_type}", handler)
|
11
|
+
instance_variable_set("@on#{ event_type }", handler)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
@@ -7,7 +7,7 @@ module Faye
|
|
7
7
|
extend Forwardable
|
8
8
|
include API
|
9
9
|
|
10
|
-
DEFAULT_PORTS = {'http' => 80, 'https' => 443, 'ws' => 80, 'wss' => 443}
|
10
|
+
DEFAULT_PORTS = { 'http' => 80, 'https' => 443, 'ws' => 80, 'wss' => 443 }
|
11
11
|
SECURE_PROTOCOLS = ['https', 'wss']
|
12
12
|
|
13
13
|
def_delegators :@driver, :headers, :status
|
@@ -17,58 +17,72 @@ module Faye
|
|
17
17
|
super(options) { ::WebSocket::Driver.client(self, :max_length => options[:max_length], :protocols => protocols) }
|
18
18
|
|
19
19
|
proxy = options.fetch(:proxy, {})
|
20
|
-
endpoint
|
21
|
-
port = endpoint.port || DEFAULT_PORTS[endpoint.scheme]
|
22
|
-
@secure = SECURE_PROTOCOLS.include?(endpoint.scheme)
|
20
|
+
@endpoint = URI.parse(proxy[:origin] || @url)
|
21
|
+
port = @endpoint.port || DEFAULT_PORTS[@endpoint.scheme]
|
23
22
|
@origin_tls = options.fetch(:tls, {})
|
24
23
|
@socket_tls = proxy[:origin] ? proxy.fetch(:tls, {}) : @origin_tls
|
25
24
|
|
26
|
-
|
27
|
-
@proxy = @driver.proxy(proxy[:origin])
|
28
|
-
if headers = proxy[:headers]
|
29
|
-
headers.each { |name, value| @proxy.set_header(name, value) }
|
30
|
-
end
|
25
|
+
configure_proxy(proxy)
|
31
26
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
27
|
+
EventMachine.connect(@endpoint.host, port, Connection) do |conn|
|
28
|
+
conn.parent = self
|
29
|
+
end
|
30
|
+
rescue => error
|
31
|
+
on_network_error(error)
|
32
|
+
end
|
36
33
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
private
|
35
|
+
|
36
|
+
def configure_proxy(proxy)
|
37
|
+
return unless proxy[:origin]
|
41
38
|
|
42
|
-
|
43
|
-
|
39
|
+
@proxy = @driver.proxy(proxy[:origin])
|
40
|
+
@proxy.on(:error) { |error| @driver.emit(:error, error) }
|
44
41
|
|
45
|
-
|
46
|
-
|
47
|
-
end
|
42
|
+
if headers = proxy[:headers]
|
43
|
+
headers.each { |name, value| @proxy.set_header(name, value) }
|
48
44
|
end
|
49
45
|
|
50
|
-
|
51
|
-
|
46
|
+
@proxy.on(:connect) do
|
47
|
+
@proxy = nil
|
48
|
+
start_tls(URI.parse(@url), @origin_tls)
|
49
|
+
@driver.start
|
52
50
|
end
|
53
|
-
rescue => error
|
54
|
-
emit_error("Network error: #{url}: #{error.message}")
|
55
|
-
finalize_close
|
56
51
|
end
|
57
52
|
|
58
|
-
|
53
|
+
def start_tls(uri, options)
|
54
|
+
return unless SECURE_PROTOCOLS.include?(uri.scheme)
|
55
|
+
|
56
|
+
tls_options = { :sni_hostname => uri.host, :verify_peer => true }.merge(options)
|
57
|
+
@ssl_verifier = SslVerifier.new(uri.host, tls_options)
|
58
|
+
@stream.start_tls(tls_options)
|
59
|
+
end
|
59
60
|
|
60
61
|
def on_connect(stream)
|
61
62
|
@stream = stream
|
62
|
-
|
63
|
-
if @secure
|
64
|
-
socket_tls = {:sni_hostname => URI.parse(@url).host}.merge(@socket_tls)
|
65
|
-
@stream.start_tls(socket_tls)
|
66
|
-
end
|
63
|
+
start_tls(@endpoint, @socket_tls)
|
67
64
|
|
68
65
|
worker = @proxy || @driver
|
69
66
|
worker.start
|
70
67
|
end
|
71
68
|
|
69
|
+
def on_network_error(error)
|
70
|
+
emit_error("Network error: #{ @url }: #{ error.message }")
|
71
|
+
finalize_close
|
72
|
+
end
|
73
|
+
|
74
|
+
def ssl_verify_peer(cert)
|
75
|
+
@ssl_verifier.ssl_verify_peer(cert)
|
76
|
+
rescue => error
|
77
|
+
on_network_error(error)
|
78
|
+
end
|
79
|
+
|
80
|
+
def ssl_handshake_completed
|
81
|
+
@ssl_verifier.ssl_handshake_completed
|
82
|
+
rescue => error
|
83
|
+
on_network_error(error)
|
84
|
+
end
|
85
|
+
|
72
86
|
module Connection
|
73
87
|
attr_accessor :parent
|
74
88
|
|
@@ -76,11 +90,20 @@ module Faye
|
|
76
90
|
parent.__send__(:on_connect, self)
|
77
91
|
end
|
78
92
|
|
93
|
+
def ssl_verify_peer(cert)
|
94
|
+
parent.__send__(:ssl_verify_peer, cert)
|
95
|
+
end
|
96
|
+
|
97
|
+
def ssl_handshake_completed
|
98
|
+
parent.__send__(:ssl_handshake_completed)
|
99
|
+
end
|
100
|
+
|
79
101
|
def receive_data(data)
|
80
102
|
parent.__send__(:parse, data)
|
81
103
|
end
|
82
104
|
|
83
|
-
def unbind
|
105
|
+
def unbind(error = nil)
|
106
|
+
parent.__send__(:emit_error, error) if error
|
84
107
|
parent.__send__(:finalize_close)
|
85
108
|
end
|
86
109
|
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# This code is based on the implementation in Faraday:
|
2
|
+
#
|
3
|
+
# https://github.com/lostisland/faraday/blob/v1.0.1/lib/faraday/adapter/em_http_ssl_patch.rb
|
4
|
+
#
|
5
|
+
# Faraday is published under the MIT license as detailed here:
|
6
|
+
#
|
7
|
+
# https://github.com/lostisland/faraday/blob/v1.0.1/LICENSE.md
|
8
|
+
#
|
9
|
+
# Copyright (c) 2009-2019 Rick Olson, Zack Hobson
|
10
|
+
#
|
11
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
12
|
+
# of this software and associated documentation files (the "Software"), to deal
|
13
|
+
# in the Software without restriction, including without limitation the rights
|
14
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
15
|
+
# copies of the Software, and to permit persons to whom the Software is
|
16
|
+
# furnished to do so, subject to the following conditions:
|
17
|
+
#
|
18
|
+
# The above copyright notice and this permission notice shall be included in
|
19
|
+
# all copies or substantial portions of the Software.
|
20
|
+
|
21
|
+
require 'openssl'
|
22
|
+
|
23
|
+
module Faye
|
24
|
+
class WebSocket
|
25
|
+
|
26
|
+
SSLError = Class.new(OpenSSL::SSL::SSLError)
|
27
|
+
|
28
|
+
class SslVerifier
|
29
|
+
def initialize(hostname, ssl_opts)
|
30
|
+
@hostname = hostname
|
31
|
+
@ssl_opts = ssl_opts
|
32
|
+
@cert_store = OpenSSL::X509::Store.new
|
33
|
+
|
34
|
+
if root = @ssl_opts[:root_cert_file]
|
35
|
+
[root].flatten.each { |ca_path| @cert_store.add_file(ca_path) }
|
36
|
+
else
|
37
|
+
@cert_store.set_default_paths
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def ssl_verify_peer(cert_text)
|
42
|
+
return true unless should_verify?
|
43
|
+
|
44
|
+
certificate = parse_cert(cert_text)
|
45
|
+
return false unless certificate
|
46
|
+
|
47
|
+
unless @cert_store.verify(certificate)
|
48
|
+
raise SSLError, "Unable to verify the server certificate for '#{ @hostname }'"
|
49
|
+
end
|
50
|
+
|
51
|
+
store_cert(certificate)
|
52
|
+
@last_cert = certificate
|
53
|
+
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
def ssl_handshake_completed
|
58
|
+
return unless should_verify?
|
59
|
+
|
60
|
+
unless identity_verified?
|
61
|
+
raise SSLError, "Host '#{ @hostname }' does not match the server certificate"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def should_verify?
|
68
|
+
@ssl_opts[:verify_peer] != false
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse_cert(cert_text)
|
72
|
+
OpenSSL::X509::Certificate.new(cert_text)
|
73
|
+
rescue OpenSSL::X509::CertificateError
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
|
77
|
+
def store_cert(certificate)
|
78
|
+
@cert_store.add_cert(certificate)
|
79
|
+
rescue OpenSSL::X509::StoreError => error
|
80
|
+
raise error unless error.message == 'cert already in hash table'
|
81
|
+
end
|
82
|
+
|
83
|
+
def identity_verified?
|
84
|
+
@last_cert and OpenSSL::SSL.verify_certificate_identity(@last_cert, @hostname)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: faye-websocket
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Coglan
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-05-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: eventmachine
|
@@ -66,20 +66,6 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: public_suffix
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "<"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: 1.5.0
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "<"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: 1.5.0
|
83
69
|
- !ruby/object:Gem::Dependency
|
84
70
|
name: puma
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -137,45 +123,51 @@ dependencies:
|
|
137
123
|
- !ruby/object:Gem::Version
|
138
124
|
version: 0.2.0
|
139
125
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
126
|
+
name: thin
|
141
127
|
requirement: !ruby/object:Gem::Requirement
|
142
128
|
requirements:
|
143
|
-
- - "
|
129
|
+
- - ">="
|
144
130
|
- !ruby/object:Gem::Version
|
145
|
-
version:
|
131
|
+
version: 1.2.0
|
132
|
+
- - ">"
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
146
135
|
type: :development
|
147
136
|
prerelease: false
|
148
137
|
version_requirements: !ruby/object:Gem::Requirement
|
149
138
|
requirements:
|
150
|
-
- - "
|
139
|
+
- - ">="
|
151
140
|
- !ruby/object:Gem::Version
|
152
|
-
version:
|
141
|
+
version: 1.2.0
|
142
|
+
- - ">"
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
153
145
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
146
|
+
name: rainbows
|
155
147
|
requirement: !ruby/object:Gem::Requirement
|
156
148
|
requirements:
|
157
|
-
- - "
|
149
|
+
- - "~>"
|
158
150
|
- !ruby/object:Gem::Version
|
159
|
-
version:
|
151
|
+
version: 4.4.0
|
160
152
|
type: :development
|
161
153
|
prerelease: false
|
162
154
|
version_requirements: !ruby/object:Gem::Requirement
|
163
155
|
requirements:
|
164
|
-
- - "
|
156
|
+
- - "~>"
|
165
157
|
- !ruby/object:Gem::Version
|
166
|
-
version:
|
158
|
+
version: 4.4.0
|
167
159
|
- !ruby/object:Gem::Dependency
|
168
160
|
name: goliath
|
169
161
|
requirement: !ruby/object:Gem::Requirement
|
170
162
|
requirements:
|
171
|
-
- - "
|
163
|
+
- - ">"
|
172
164
|
- !ruby/object:Gem::Version
|
173
165
|
version: '0'
|
174
166
|
type: :development
|
175
167
|
prerelease: false
|
176
168
|
version_requirements: !ruby/object:Gem::Requirement
|
177
169
|
requirements:
|
178
|
-
- - "
|
170
|
+
- - ">"
|
179
171
|
- !ruby/object:Gem::Version
|
180
172
|
version: '0'
|
181
173
|
- !ruby/object:Gem::Dependency
|
@@ -192,7 +184,7 @@ dependencies:
|
|
192
184
|
- - ">="
|
193
185
|
- !ruby/object:Gem::Version
|
194
186
|
version: 4.0.0
|
195
|
-
description:
|
187
|
+
description:
|
196
188
|
email: jcoglan@gmail.com
|
197
189
|
executables: []
|
198
190
|
extensions: []
|
@@ -200,17 +192,8 @@ extra_rdoc_files:
|
|
200
192
|
- README.md
|
201
193
|
files:
|
202
194
|
- CHANGELOG.md
|
195
|
+
- LICENSE.md
|
203
196
|
- README.md
|
204
|
-
- examples/app.rb
|
205
|
-
- examples/autobahn_client.rb
|
206
|
-
- examples/client.rb
|
207
|
-
- examples/config.ru
|
208
|
-
- examples/haproxy.conf
|
209
|
-
- examples/proxy_server.rb
|
210
|
-
- examples/rainbows.conf
|
211
|
-
- examples/server.rb
|
212
|
-
- examples/sse.html
|
213
|
-
- examples/ws.html
|
214
197
|
- lib/faye/adapters/goliath.rb
|
215
198
|
- lib/faye/adapters/rainbows.rb
|
216
199
|
- lib/faye/adapters/rainbows_client.rb
|
@@ -223,11 +206,12 @@ files:
|
|
223
206
|
- lib/faye/websocket/api/event.rb
|
224
207
|
- lib/faye/websocket/api/event_target.rb
|
225
208
|
- lib/faye/websocket/client.rb
|
209
|
+
- lib/faye/websocket/ssl_verifier.rb
|
226
210
|
homepage: https://github.com/faye/faye-websocket-ruby
|
227
211
|
licenses:
|
228
|
-
-
|
212
|
+
- Apache-2.0
|
229
213
|
metadata: {}
|
230
|
-
post_install_message:
|
214
|
+
post_install_message:
|
231
215
|
rdoc_options:
|
232
216
|
- "--main"
|
233
217
|
- README.md
|
@@ -246,9 +230,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
246
230
|
- !ruby/object:Gem::Version
|
247
231
|
version: '0'
|
248
232
|
requirements: []
|
249
|
-
|
250
|
-
|
251
|
-
signing_key:
|
233
|
+
rubygems_version: 3.1.6
|
234
|
+
signing_key:
|
252
235
|
specification_version: 4
|
253
236
|
summary: Standards-compliant WebSocket server and client
|
254
237
|
test_files: []
|