faye-websocket 0.10.9 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f7b7147b0be962c2d274d1483bc8f20804afc187033055ca76b46830b5380a01
4
- data.tar.gz: 54c1b097b2f21b5ad2ba0ccfe15c2b935de6b2e924c6ccd27635dceaae885e36
3
+ metadata.gz: 42cb4ec90fb1f2d1fd16739908d415035255136b33c1b8100f4370417d7afeee
4
+ data.tar.gz: b75409884d2ac82e84a59752027085106d1995e92d9603628400b13d7214bc4d
5
5
  SHA512:
6
- metadata.gz: 70c5f89236c7204ca9af93f9f7e3e965fe16092dcc6160ddbe1a97bfaf7f85206586383e020e3fba729a18819607df87eb3cd0010b391cd15c7599720fedb60b
7
- data.tar.gz: 7a945496751f19145a63088efe91d8b06866e2cda8ff56c7fd00f5670fbd192d9437db338a20a33b92c1b69c11f454cf2fb4a55be515b54ebec78c82c5532a6d
6
+ metadata.gz: 23293a16e2b1068bb1bd9a7c4e7516f9031185393e50d766833fe468bff7fb3b807d69015c022f35545cf41abb3e5ff9cf96282bb797ecba0eee60cea63de428
7
+ data.tar.gz: 46ac66a36431ee0adc04dbe5286499c38904b79e4da3259bb42c57c49ff97736568957da641fd640fedc237f67e258d06d5cbd0abb5d3d10eaf662f991368b67
@@ -1,3 +1,10 @@
1
+ ### 0.11.0 / 2020-07-31
2
+
3
+ - Implement TLS certificate verification and enable it by default on client
4
+ connections
5
+ - Add a `:tls` option to the client with sub-fields `:root_cert_file` and
6
+ `:verify_peer` for configuring TLS verification
7
+
1
8
  ### 0.10.9 / 2019-06-13
2
9
 
3
10
  - Use the EventMachine API rather than `IO#write` to write data; this uses the
@@ -0,0 +1,12 @@
1
+ Copyright 2010-2020 James Coglan
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use
4
+ this file except in compliance with the License. You may obtain a copy of the
5
+ License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software distributed
10
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
11
+ CONDITIONS OF ANY KIND, either express or implied. See the License for the
12
+ specific language governing permissions and limitations under the License.
data/README.md CHANGED
@@ -198,6 +198,38 @@ is an optional hash containing any of these keys:
198
198
  These are passed along to EventMachine and you can find
199
199
  [more details here](http://rubydoc.info/gems/eventmachine/EventMachine%2FConnection%3Astart_tls)
200
200
 
201
+ ### Secure sockets
202
+
203
+ Starting with version 0.11.0, `Faye::WebSocket::Client` will verify the server
204
+ certificate for `wss` connections. This is not the default behaviour for
205
+ EventMachine's TLS interface, and so our defaults for the `:tls` option are a
206
+ little different.
207
+
208
+ First, `:verify_peer` is enabled by default. Our implementation checks that the
209
+ chain of certificates sent by the server is trusted by your root certificates,
210
+ and that the final certificate's hostname matches the hostname in the request
211
+ URL.
212
+
213
+ By default, we use your system's root certificate store by invoking
214
+ `OpenSSL::X509::Store#set_default_paths`. If you want to use a different set of
215
+ root certificates, you can pass them via the `:root_cert_file` option, which
216
+ takes a path or an array of paths to the certificates you want to use.
217
+
218
+ ```ruby
219
+ ws = Faye::WebSocket::Client.new('wss://example.com/', [], :tls => {
220
+ :root_cert_file => ['path/to/certificate.pem']
221
+ })
222
+ ```
223
+
224
+ If you want to switch off certificate verification altogether, then set
225
+ `:verify_peer` to `false`.
226
+
227
+ ```ruby
228
+ ws = Faye::WebSocket::Client.new('wss://example.com/', [], :tls => {
229
+ :verify_peer => false
230
+ })
231
+ ```
232
+
201
233
  ## WebSocket API
202
234
 
203
235
  Both the server- and client-side `WebSocket` objects support the following API:
@@ -206,7 +238,8 @@ Both the server- and client-side `WebSocket` objects support the following API:
206
238
  Event has no attributes.
207
239
  - **`on(:message) { |event| }`** fires when the socket receives a message. Event
208
240
  has one attribute, **`data`**, which is either a `String` (for text frames) or
209
- an `Array` of byte-sized integers (for binary frames).
241
+ an `Array` of unsigned integers, i.e. integers in the range `0..255` (for
242
+ binary frames).
210
243
  - **`on(:error) { |event| }`** fires when there is a protocol error due to bad
211
244
  data sent by the other peer. This event is purely informational, you do not
212
245
  need to implement error recovery.
@@ -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,
@@ -17,20 +17,18 @@ 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
25
  configure_proxy(proxy)
27
26
 
28
- EventMachine.connect(endpoint.host, port, Connection) do |conn|
27
+ EventMachine.connect(@endpoint.host, port, Connection) do |conn|
29
28
  conn.parent = self
30
29
  end
31
30
  rescue => error
32
- emit_error("Network error: #{ url }: #{ error.message }")
33
- finalize_close
31
+ on_network_error(error)
34
32
  end
35
33
 
36
34
  private
@@ -46,31 +44,45 @@ module Faye
46
44
  end
47
45
 
48
46
  @proxy.on(:connect) do
49
- uri = URI.parse(@url)
50
- secure = SECURE_PROTOCOLS.include?(uri.scheme)
51
47
  @proxy = nil
52
-
53
- if secure
54
- origin_tls = { :sni_hostname => uri.host }.merge(@origin_tls)
55
- @stream.start_tls(origin_tls)
56
- end
57
-
48
+ start_tls(URI.parse(@url), @origin_tls)
58
49
  @driver.start
59
50
  end
60
51
  end
61
52
 
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
60
+
62
61
  def on_connect(stream)
63
62
  @stream = stream
64
-
65
- if @secure
66
- socket_tls = { :sni_hostname => URI.parse(@url).host }.merge(@socket_tls)
67
- @stream.start_tls(socket_tls)
68
- end
63
+ start_tls(@endpoint, @socket_tls)
69
64
 
70
65
  worker = @proxy || @driver
71
66
  worker.start
72
67
  end
73
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
+
74
86
  module Connection
75
87
  attr_accessor :parent
76
88
 
@@ -78,6 +90,14 @@ module Faye
78
90
  parent.__send__(:on_connect, self)
79
91
  end
80
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
+
81
101
  def receive_data(data)
82
102
  parent.__send__(:parse, data)
83
103
  end
@@ -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.9
4
+ version: 0.11.0
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: 2019-06-13 00:00:00.000000000 Z
11
+ date: 2020-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: eventmachine
@@ -178,7 +178,7 @@ dependencies:
178
178
  - - ">="
179
179
  - !ruby/object:Gem::Version
180
180
  version: 4.0.0
181
- description:
181
+ description:
182
182
  email: jcoglan@gmail.com
183
183
  executables: []
184
184
  extensions: []
@@ -186,17 +186,8 @@ extra_rdoc_files:
186
186
  - README.md
187
187
  files:
188
188
  - CHANGELOG.md
189
+ - LICENSE.md
189
190
  - README.md
190
- - examples/app.rb
191
- - examples/autobahn_client.rb
192
- - examples/client.rb
193
- - examples/config.ru
194
- - examples/haproxy.conf
195
- - examples/proxy_server.rb
196
- - examples/rainbows.conf
197
- - examples/server.rb
198
- - examples/sse.html
199
- - examples/ws.html
200
191
  - lib/faye/adapters/goliath.rb
201
192
  - lib/faye/adapters/rainbows.rb
202
193
  - lib/faye/adapters/rainbows_client.rb
@@ -209,11 +200,12 @@ files:
209
200
  - lib/faye/websocket/api/event.rb
210
201
  - lib/faye/websocket/api/event_target.rb
211
202
  - lib/faye/websocket/client.rb
203
+ - lib/faye/websocket/ssl_verifier.rb
212
204
  homepage: https://github.com/faye/faye-websocket-ruby
213
205
  licenses:
214
206
  - Apache-2.0
215
207
  metadata: {}
216
- post_install_message:
208
+ post_install_message:
217
209
  rdoc_options:
218
210
  - "--main"
219
211
  - README.md
@@ -232,8 +224,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
232
224
  - !ruby/object:Gem::Version
233
225
  version: '0'
234
226
  requirements: []
235
- rubygems_version: 3.0.3
236
- signing_key:
227
+ rubygems_version: 3.1.2
228
+ signing_key:
237
229
  specification_version: 4
238
230
  summary: Standards-compliant WebSocket server and client
239
231
  test_files: []
@@ -1,54 +0,0 @@
1
- require 'faye/websocket'
2
- require 'permessage_deflate'
3
- require 'rack'
4
-
5
- static = Rack::File.new(File.dirname(__FILE__))
6
- options = { :extensions => [PermessageDeflate], :ping => 5 }
7
-
8
- App = lambda do |env|
9
- if Faye::WebSocket.websocket?(env)
10
- ws = Faye::WebSocket.new(env, ['irc', 'xmpp'], options)
11
- p [:open, ws.url, ws.version, ws.protocol]
12
-
13
- ws.onmessage = lambda do |event|
14
- ws.send(event.data)
15
- end
16
-
17
- ws.onclose = lambda do |event|
18
- p [:close, event.code, event.reason]
19
- ws = nil
20
- end
21
-
22
- ws.rack_response
23
-
24
- elsif Faye::EventSource.eventsource?(env)
25
- es = Faye::EventSource.new(env)
26
- time = es.last_event_id.to_i
27
-
28
- p [:open, es.url, es.last_event_id]
29
-
30
- loop = EM.add_periodic_timer(2) do
31
- time += 1
32
- es.send("Time: #{ time }")
33
- EM.add_timer(1) do
34
- es.send('Update!!', :event => 'update', :id => time) if es
35
- end
36
- end
37
-
38
- es.send("Welcome!\n\nThis is an EventSource server.")
39
-
40
- es.onclose = lambda do |event|
41
- EM.cancel_timer(loop)
42
- p [:close, es.url]
43
- es = nil
44
- end
45
-
46
- es.rack_response
47
-
48
- else
49
- static.call(env)
50
- end
51
- end
52
-
53
- def App.log(message)
54
- end
@@ -1,49 +0,0 @@
1
- require 'bundler/setup'
2
- require 'cgi'
3
- require 'faye/websocket'
4
- require 'permessage_deflate'
5
- require 'progressbar'
6
-
7
- EM.run {
8
- ruby = RUBY_PLATFORM =~ /java/ ? 'jruby' : 'mri-ruby'
9
- version = defined?(RUBY_ENGINE_VERSION) ? RUBY_ENGINE_VERSION : RUBY_VERSION
10
- version += " (#{ RUBY_VERSION })" if ruby == 'jruby'
11
-
12
- host = 'ws://0.0.0.0:9001'
13
- agent = CGI.escape("#{ ruby }-#{ version }")
14
- cases = 0
15
- options = { :extensions => [PermessageDeflate] }
16
-
17
- socket = Faye::WebSocket::Client.new("#{ host }/getCaseCount")
18
- progress = nil
19
-
20
- socket.onmessage = lambda do |event|
21
- puts "Total cases to run: #{ event.data }"
22
- cases = event.data.to_i
23
- progress = ProgressBar.create(:title => 'Autobahn', :total => cases)
24
- end
25
-
26
- run_case = lambda do |n|
27
- if n > cases
28
- socket = Faye::WebSocket::Client.new("#{ host }/updateReports?agent=#{ agent }")
29
- socket.onclose = lambda { |e| EM.stop }
30
- next
31
- end
32
-
33
- url = "#{ host }/runCase?case=#{ n }&agent=#{ agent }"
34
- socket = Faye::WebSocket::Client.new(url, [], options)
35
-
36
- socket.onmessage = lambda do |event|
37
- socket.send(event.data)
38
- end
39
-
40
- socket.on :close do |event|
41
- progress.increment
42
- run_case[n + 1]
43
- end
44
- end
45
-
46
- socket.onclose = lambda do |event|
47
- run_case[1]
48
- end
49
- }
@@ -1,33 +0,0 @@
1
- require 'bundler/setup'
2
- require 'faye/websocket'
3
- require 'eventmachine'
4
- require 'permessage_deflate'
5
-
6
- EM.run {
7
- url = ARGV[0]
8
- proxy = ARGV[1]
9
-
10
- ws = Faye::WebSocket::Client.new(url, [],
11
- :proxy => { :origin => proxy, :headers => { 'User-Agent' => 'Echo' } },
12
- :headers => { 'Origin' => 'http://faye.jcoglan.com' },
13
- :extensions => [PermessageDeflate]
14
- )
15
-
16
- ws.onopen = lambda do |event|
17
- p [:open, ws.headers]
18
- ws.send('mic check')
19
- end
20
-
21
- ws.onclose = lambda do |close|
22
- p [:close, close.code, close.reason]
23
- EM.stop
24
- end
25
-
26
- ws.onerror = lambda do |error|
27
- p [:error, error.message]
28
- end
29
-
30
- ws.onmessage = lambda do |message|
31
- p [:message, message.data]
32
- end
33
- }
@@ -1,12 +0,0 @@
1
- # Run using your favourite server:
2
- #
3
- # thin start -R examples/config.ru -p 7000
4
- # rainbows -c examples/rainbows.conf -E production examples/config.ru -p 7000
5
-
6
- require 'bundler/setup'
7
- require File.expand_path('../app', __FILE__)
8
-
9
- Faye::WebSocket.load_adapter('thin')
10
- Faye::WebSocket.load_adapter('rainbows')
11
-
12
- run App
@@ -1,20 +0,0 @@
1
- defaults
2
- mode http
3
- timeout client 5s
4
- timeout connect 5s
5
- timeout server 5s
6
-
7
- frontend all 0.0.0.0:3000
8
- mode http
9
- timeout client 120s
10
-
11
- option forwardfor
12
- option http-server-close
13
- option http-pretend-keepalive
14
-
15
- default_backend sockets
16
-
17
- backend sockets
18
- balance uri depth 2
19
- timeout server 120s
20
- server socket1 127.0.0.1:7000
@@ -1,13 +0,0 @@
1
- require 'bundler/setup'
2
- require 'eventmachine'
3
- require 'websocket/driver'
4
-
5
- require File.expand_path('../../spec/proxy_server', __FILE__)
6
-
7
- port = ARGV[0]
8
- secure = ARGV[1] == 'tls'
9
-
10
- EM.run {
11
- proxy = ProxyServer.new(:debug => true)
12
- proxy.listen(port, secure)
13
- }
@@ -1,3 +0,0 @@
1
- Rainbows! do
2
- use :EventMachine
3
- end
@@ -1,51 +0,0 @@
1
- require 'bundler/setup'
2
- require 'rack/content_length'
3
- require 'rack/chunked'
4
-
5
- port = ARGV[0] || 7000
6
- secure = ARGV[1] == 'tls'
7
- engine = ARGV[2] || 'thin'
8
- spec = File.expand_path('../../spec', __FILE__)
9
-
10
- require File.expand_path('../app', __FILE__)
11
- Faye::WebSocket.load_adapter(engine)
12
-
13
- case engine
14
-
15
- when 'goliath'
16
- class WebSocketServer < Goliath::API
17
- def response(env)
18
- App.call(env)
19
- end
20
- end
21
-
22
- when 'puma'
23
- require 'puma/binder'
24
- require 'puma/events'
25
- events = Puma::Events.new($stdout, $stderr)
26
- binder = Puma::Binder.new(events)
27
- binder.parse(["tcp://0.0.0.0:#{ port }"], App)
28
- server = Puma::Server.new(App, events)
29
- server.binder = binder
30
- server.run.join
31
-
32
- when 'rainbows'
33
- rackup = Unicorn::Configurator::RACKUP
34
- rackup[:port] = port
35
- rackup[:set_listener] = true
36
- options = rackup[:options]
37
- options[:config_file] = File.expand_path('../rainbows.conf', __FILE__)
38
- Rainbows::HttpServer.new(App, options).start.join
39
-
40
- when 'thin'
41
- thin = Rack::Handler.get('thin')
42
- thin.run(App, :Host => '0.0.0.0', :Port => port) do |server|
43
- if secure
44
- server.ssl_options = {
45
- :private_key_file => spec + '/server.key',
46
- :cert_chain_file => spec + '/server.crt'
47
- }
48
- server.ssl = true
49
- end
50
- end
51
- end
@@ -1,38 +0,0 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta http-equiv="Content-type" content="text/html; charset=utf-8">
5
- <title>EventSource test</title>
6
- </head>
7
- <body>
8
-
9
- <h1>EventSource test</h1>
10
- <ul></ul>
11
-
12
- <script type="text/javascript">
13
- var logger = document.getElementsByTagName('ul')[0],
14
- socket = new EventSource('/');
15
-
16
- var log = function(text) {
17
- logger.innerHTML += '<li>' + text + '</li>';
18
- };
19
-
20
- socket.onopen = function() {
21
- log('OPEN');
22
- };
23
-
24
- socket.onmessage = function(event) {
25
- log('MESSAGE: ' + event.data);
26
- };
27
-
28
- socket.addEventListener('update', function(event) {
29
- log('UPDATE(' + event.lastEventId + '): ' + event.data);
30
- });
31
-
32
- socket.onerror = function(event) {
33
- log('ERROR: ' + event.message);
34
- };
35
- </script>
36
-
37
- </body>
38
- </html>
@@ -1,43 +0,0 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta http-equiv="Content-type" content="text/html; charset=utf-8">
5
- <title>WebSocket test</title>
6
- </head>
7
- <body>
8
-
9
- <h1>WebSocket test</h1>
10
- <ul></ul>
11
-
12
- <script type="text/javascript">
13
- var logger = document.getElementsByTagName('ul')[0],
14
- Socket = window.MozWebSocket || window.WebSocket,
15
- protos = ['foo', 'bar', 'xmpp'],
16
- socket = new Socket('ws://' + location.hostname + ':' + location.port + '/', protos),
17
- index = 0;
18
-
19
- var log = function(text) {
20
- logger.innerHTML += '<li>' + text + '</li>';
21
- };
22
-
23
- socket.addEventListener('open', function() {
24
- log('OPEN: ' + socket.protocol);
25
- socket.send('Hello, world');
26
- });
27
-
28
- socket.onerror = function(event) {
29
- log('ERROR: ' + event.message);
30
- };
31
-
32
- socket.onmessage = function(event) {
33
- log('MESSAGE: ' + event.data);
34
- setTimeout(function() { socket.send(++index + ' ' + event.data) }, 2000);
35
- };
36
-
37
- socket.onclose = function(event) {
38
- log('CLOSE: ' + event.code + ', ' + event.reason);
39
- };
40
- </script>
41
-
42
- </body>
43
- </html>