faye-websocket 0.10.9 → 0.11.0

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.
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>