faye-websocket 0.10.6 → 0.11.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.
@@ -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
@@ -79,6 +79,7 @@ module Faye
79
79
  end
80
80
 
81
81
  def write(data)
82
+ return @rack_hijack_io_reader.send_data(data) if @rack_hijack_io_reader
82
83
  return @rack_hijack_io.write(data) if @rack_hijack_io
83
84
  return @stream_send.call(data) if @stream_send
84
85
  rescue => e
@@ -17,9 +17,10 @@ module Faye
17
17
  class WebSocket
18
18
  root = File.expand_path('../websocket', __FILE__)
19
19
 
20
- autoload :Adapter, root + '/adapter'
21
- autoload :API, root + '/api'
22
- autoload :Client, root + '/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
 
@@ -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
- @close_timer = EventMachine.add_timer(CLOSE_TIMEOUT) { begin_close('', 1006) }
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
- @stream.close_connection_after_writing
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 = URI.parse(proxy[:origin] || @url)
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
- if proxy[:origin]
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
- @proxy.on(:connect) do
33
- uri = URI.parse(@url)
34
- secure = SECURE_PROTOCOLS.include?(uri.scheme)
35
- @proxy = nil
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
- if secure
38
- origin_tls = {:sni_hostname => uri.host}.merge(@origin_tls)
39
- @stream.start_tls(origin_tls)
40
- end
34
+ private
35
+
36
+ def configure_proxy(proxy)
37
+ return unless proxy[:origin]
41
38
 
42
- @driver.start
43
- end
39
+ @proxy = @driver.proxy(proxy[:origin])
40
+ @proxy.on(:error) { |error| @driver.emit(:error, error) }
44
41
 
45
- @proxy.on(:error) do |error|
46
- @driver.emit(:error, error)
47
- end
42
+ if headers = proxy[:headers]
43
+ headers.each { |name, value| @proxy.set_header(name, value) }
48
44
  end
49
45
 
50
- EventMachine.connect(endpoint.host, port, Connection) do |conn|
51
- conn.parent = self
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
- private
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.10.6
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: 2017-01-22 00:00:00.000000000 Z
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: rainbows
126
+ name: thin
141
127
  requirement: !ruby/object:Gem::Requirement
142
128
  requirements:
143
- - - "~>"
129
+ - - ">="
144
130
  - !ruby/object:Gem::Version
145
- version: 4.4.0
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: 4.4.0
141
+ version: 1.2.0
142
+ - - ">"
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
153
145
  - !ruby/object:Gem::Dependency
154
- name: thin
146
+ name: rainbows
155
147
  requirement: !ruby/object:Gem::Requirement
156
148
  requirements:
157
- - - ">="
149
+ - - "~>"
158
150
  - !ruby/object:Gem::Version
159
- version: 1.2.0
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: 1.2.0
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
- - MIT
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
- rubyforge_project:
250
- rubygems_version: 2.6.8
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: []