kontena-websocket-client 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a021faa81b4ccbdf828328d594afa2c4e8f67106
4
+ data.tar.gz: e2492654a39c665aaa2ec360256ac82b31d50830
5
+ SHA512:
6
+ metadata.gz: fb510138008dfa603069858a9c1dbb715edce8761d326de6983dfb2851b7559819264ddbbefa09354ff8b335d197d2e93a6d98f875112f97d5d54187a25334d0
7
+ data.tar.gz: dd61aaba0fbf17c2cbc7d0942e58fbf1749a95716c68c80f87c9fe11666e44c82344c2e84899b6be172bc0729a9224194f29ac73901538b376c4e3b533ba2fd0
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /vendor/
11
+
12
+ # rspec failure tracking
13
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --require spec_helper
2
+ --format documentation
3
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,21 @@
1
+ sudo: false
2
+ dist: trusty
3
+ language: ruby
4
+ rvm:
5
+ - 2.1.10
6
+ - 2.2.6
7
+ - 2.3.3
8
+ - 2.4.1
9
+ before_install: gem install bundler -v 1.15.1
10
+ install:
11
+ - bundle install --path vendor/bundle
12
+ script:
13
+ - bundle exec rspec
14
+ deploy:
15
+ provider: rubygems
16
+ api_key:
17
+ secure: Aw06Tbfzg5J2XcdnbI6t5o2Wc04pqybm/7skgLtyMw1H8cmMY+7xglO7GHgz0C+Mp3oXs4NSLbUWoM2vmdL936NtQkv0WV6Zp1Ardkk3wl40m9dPDnhXy2pJoEaC00PFEuMNxAG0PphPwskSXyjzpGqTeohuZSkVER9VaKlWrbdroRlJK2u9XSkTyne9Nk1QJwrO/guCVwHGn+/VgzACE0xNsCoDcyUWxuwz/XZFupWzIrrCGGVnuzOxpY93nBIJmyFM5Va43b2QzvRWex5EmetoMaHAA9NiCa2bzjFauY9z6M93bXHvCipqd2YFb47AsSNVb6U8hVFaWwR+dYs3xN/O3Isil90BagPUHl/JGgq1MCR5qjpsFNY95F1DepRuHolFTxtHVZigJyPRxnb7YtUZUCHNvBYAsGI9l0weRlj3Ucp1sbGlVvxSOvkrIMgM7ii5SG/t6hgYTyahdIlQcNVwG/JE6Ll2IPUIb8ZLFPN4OnFzzWpmnOja4liOqC1N1X9kKKTa9yFmQbdSID+vd3KPn1IBnS2s2amB1ApmAJ64WDTeGw947kXRy+t2Wk/Ux9qXj1RD/JM6dgPZAvrTvGA17T2Z75yHWYtCG99/FJyhn4kCCYLuui1c+DkvieBeMrZrG4S0hzK30gPsQ3K36YH9VJPvyqlCrURvqpiNfkE=
18
+ gem: kontena-websocket-client
19
+ on:
20
+ tags: true
21
+ repo: kontena/kontena-websocket-client
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in kontena-websocket-client.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,190 @@
1
+ Copyright 2017 Kontena, Inc.
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the 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
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
14
+
15
+ Apache License
16
+ Version 2.0, January 2004
17
+ http://www.apache.org/licenses/
18
+
19
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
20
+
21
+ 1. Definitions.
22
+
23
+ "License" shall mean the terms and conditions for use, reproduction,
24
+ and distribution as defined by Sections 1 through 9 of this document.
25
+
26
+ "Licensor" shall mean the copyright owner or entity authorized by
27
+ the copyright owner that is granting the License.
28
+
29
+ "Legal Entity" shall mean the union of the acting entity and all
30
+ other entities that control, are controlled by, or are under common
31
+ control with that entity. For the purposes of this definition,
32
+ "control" means (i) the power, direct or indirect, to cause the
33
+ direction or management of such entity, whether by contract or
34
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
35
+ outstanding shares, or (iii) beneficial ownership of such entity.
36
+
37
+ "You" (or "Your") shall mean an individual or Legal Entity
38
+ exercising permissions granted by this License.
39
+
40
+ "Source" form shall mean the preferred form for making modifications,
41
+ including but not limited to software source code, documentation
42
+ source, and configuration files.
43
+
44
+ "Object" form shall mean any form resulting from mechanical
45
+ transformation or translation of a Source form, including but
46
+ not limited to compiled object code, generated documentation,
47
+ and conversions to other media types.
48
+
49
+ "Work" shall mean the work of authorship, whether in Source or
50
+ Object form, made available under the License, as indicated by a
51
+ copyright notice that is included in or attached to the work
52
+ (an example is provided in the Appendix below).
53
+
54
+ "Derivative Works" shall mean any work, whether in Source or Object
55
+ form, that is based on (or derived from) the Work and for which the
56
+ editorial revisions, annotations, elaborations, or other modifications
57
+ represent, as a whole, an original work of authorship. For the purposes
58
+ of this License, Derivative Works shall not include works that remain
59
+ separable from, or merely link (or bind by name) to the interfaces of,
60
+ the Work and Derivative Works thereof.
61
+
62
+ "Contribution" shall mean any work of authorship, including
63
+ the original version of the Work and any modifications or additions
64
+ to that Work or Derivative Works thereof, that is intentionally
65
+ submitted to Licensor for inclusion in the Work by the copyright owner
66
+ or by an individual or Legal Entity authorized to submit on behalf of
67
+ the copyright owner. For the purposes of this definition, "submitted"
68
+ means any form of electronic, verbal, or written communication sent
69
+ to the Licensor or its representatives, including but not limited to
70
+ communication on electronic mailing lists, source code control systems,
71
+ and issue tracking systems that are managed by, or on behalf of, the
72
+ Licensor for the purpose of discussing and improving the Work, but
73
+ excluding communication that is conspicuously marked or otherwise
74
+ designated in writing by the copyright owner as "Not a Contribution."
75
+
76
+ "Contributor" shall mean Licensor and any individual or Legal Entity
77
+ on behalf of whom a Contribution has been received by Licensor and
78
+ subsequently incorporated within the Work.
79
+
80
+ 2. Grant of Copyright License. Subject to the terms and conditions of
81
+ this License, each Contributor hereby grants to You a perpetual,
82
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
83
+ copyright license to reproduce, prepare Derivative Works of,
84
+ publicly display, publicly perform, sublicense, and distribute the
85
+ Work and such Derivative Works in Source or Object form.
86
+
87
+ 3. Grant of Patent License. Subject to the terms and conditions of
88
+ this License, each Contributor hereby grants to You a perpetual,
89
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
90
+ (except as stated in this section) patent license to make, have made,
91
+ use, offer to sell, sell, import, and otherwise transfer the Work,
92
+ where such license applies only to those patent claims licensable
93
+ by such Contributor that are necessarily infringed by their
94
+ Contribution(s) alone or by combination of their Contribution(s)
95
+ with the Work to which such Contribution(s) was submitted. If You
96
+ institute patent litigation against any entity (including a
97
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
98
+ or a Contribution incorporated within the Work constitutes direct
99
+ or contributory patent infringement, then any patent licenses
100
+ granted to You under this License for that Work shall terminate
101
+ as of the date such litigation is filed.
102
+
103
+ 4. Redistribution. You may reproduce and distribute copies of the
104
+ Work or Derivative Works thereof in any medium, with or without
105
+ modifications, and in Source or Object form, provided that You
106
+ meet the following conditions:
107
+
108
+ (a) You must give any other recipients of the Work or
109
+ Derivative Works a copy of this License; and
110
+
111
+ (b) You must cause any modified files to carry prominent notices
112
+ stating that You changed the files; and
113
+
114
+ (c) You must retain, in the Source form of any Derivative Works
115
+ that You distribute, all copyright, patent, trademark, and
116
+ attribution notices from the Source form of the Work,
117
+ excluding those notices that do not pertain to any part of
118
+ the Derivative Works; and
119
+
120
+ (d) If the Work includes a "NOTICE" text file as part of its
121
+ distribution, then any Derivative Works that You distribute must
122
+ include a readable copy of the attribution notices contained
123
+ within such NOTICE file, excluding those notices that do not
124
+ pertain to any part of the Derivative Works, in at least one
125
+ of the following places: within a NOTICE text file distributed
126
+ as part of the Derivative Works; within the Source form or
127
+ documentation, if provided along with the Derivative Works; or,
128
+ within a display generated by the Derivative Works, if and
129
+ wherever such third-party notices normally appear. The contents
130
+ of the NOTICE file are for informational purposes only and
131
+ do not modify the License. You may add Your own attribution
132
+ notices within Derivative Works that You distribute, alongside
133
+ or as an addendum to the NOTICE text from the Work, provided
134
+ that such additional attribution notices cannot be construed
135
+ as modifying the License.
136
+
137
+ You may add Your own copyright statement to Your modifications and
138
+ may provide additional or different license terms and conditions
139
+ for use, reproduction, or distribution of Your modifications, or
140
+ for any such Derivative Works as a whole, provided Your use,
141
+ reproduction, and distribution of the Work otherwise complies with
142
+ the conditions stated in this License.
143
+
144
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
145
+ any Contribution intentionally submitted for inclusion in the Work
146
+ by You to the Licensor shall be under the terms and conditions of
147
+ this License, without any additional terms or conditions.
148
+ Notwithstanding the above, nothing herein shall supersede or modify
149
+ the terms of any separate license agreement you may have executed
150
+ with Licensor regarding such Contributions.
151
+
152
+ 6. Trademarks. This License does not grant permission to use the trade
153
+ names, trademarks, service marks, or product names of the Licensor,
154
+ except as required for reasonable and customary use in describing the
155
+ origin of the Work and reproducing the content of the NOTICE file.
156
+
157
+ 7. Disclaimer of Warranty. Unless required by applicable law or
158
+ agreed to in writing, Licensor provides the Work (and each
159
+ Contributor provides its Contributions) on an "AS IS" BASIS,
160
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
161
+ implied, including, without limitation, any warranties or conditions
162
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
163
+ PARTICULAR PURPOSE. You are solely responsible for determining the
164
+ appropriateness of using or redistributing the Work and assume any
165
+ risks associated with Your exercise of permissions under this License.
166
+
167
+ 8. Limitation of Liability. In no event and under no legal theory,
168
+ whether in tort (including negligence), contract, or otherwise,
169
+ unless required by applicable law (such as deliberate and grossly
170
+ negligent acts) or agreed to in writing, shall any Contributor be
171
+ liable to You for damages, including any direct, indirect, special,
172
+ incidental, or consequential damages of any character arising as a
173
+ result of this License or out of the use or inability to use the
174
+ Work (including but not limited to damages for loss of goodwill,
175
+ work stoppage, computer failure or malfunction, or any and all
176
+ other commercial damages or losses), even if such Contributor
177
+ has been advised of the possibility of such damages.
178
+
179
+ 9. Accepting Warranty or Additional Liability. While redistributing
180
+ the Work or Derivative Works thereof, You may choose to offer,
181
+ and charge a fee for, acceptance of support, warranty, indemnity,
182
+ or other liability obligations and/or rights consistent with this
183
+ License. However, in accepting such obligations, You may act only
184
+ on Your own behalf and on Your sole responsibility, not on behalf
185
+ of any other Contributor, and only if You agree to indemnify,
186
+ defend, and hold each Contributor harmless for any liability
187
+ incurred by, or claims asserted against, such Contributor by reason
188
+ of your accepting any such warranty or additional liability.
189
+
190
+ END OF TERMS AND CONDITIONS
data/README.md ADDED
@@ -0,0 +1,136 @@
1
+ [![Build Status](https://travis-ci.org/kontena/kontena-websocket-client.svg?branch=master)](https://travis-ci.org/kontena/kontena-websocket-client)
2
+
3
+ # Kontena::Websocket::Client
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'kontena-websocket-client'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install kontena-websocket-client
20
+
21
+ ## Usage
22
+
23
+ The high-level `Kontena::Websocket::Client#connect` API uses a synchronous programming model instead of the event-based `on(:event) do ...` model used by the browser WebSockets API:
24
+
25
+ * The `connect` class method yields the connected `Kontena::Websocket::Client` object
26
+ * The `read` method yields received websocket messages and returns once the websocket is closed
27
+ * The `send` and `close` methods can be called from the `connect` block, the `read` block, or a different thread
28
+ * Any of these functions can also raise `Kontena::Websocket::Error`
29
+
30
+ ```ruby
31
+ require 'kontena-websocket-client'
32
+
33
+ def websocket_connect
34
+ Kontena::Websocket::Client.connect(url, options...) do |client|
35
+ on_open
36
+
37
+ client.send(...)
38
+ client.close(1000)
39
+
40
+ client.read do |message|
41
+ on_message(message)
42
+ end
43
+
44
+ on_close(client.close_code, client.close_reason) # client closed connection
45
+ end
46
+ rescue Kontena::Websocket::CloseError => exc
47
+ on_close(exc.code, exc.reason) # server closed connection
48
+ rescue Kontena::Websocket::Error => exc
49
+ on_error(exc)
50
+ end
51
+ ```
52
+ ### Environment variables
53
+
54
+ The library uses the following environment variables:
55
+
56
+ * `SSL_CERT_FILE`: Default value for `ssl_params: { ca_file: ... }`
57
+ * `SSL_CERT_PATH`: Default value for `ssl_params: { ca_path: ... }`
58
+
59
+ ### Threadsafe
60
+
61
+ The `Kontena::Websocket::Client` is threadsafe: while a single read thread is blocking on `read`, other threads may safely call `send`, `ping` and `close`.
62
+
63
+ The `read` block may also call `send`, `ping` and `close`.
64
+ Do not call `read` from multiple threads, or the websocket messages may get corrupted.
65
+
66
+ The library uses an internal `Mutex` to protect the internal `Websocket::Driver` state, and prevent socket read/write reordering/corruption.
67
+
68
+ XXX: The `on_pong` callback is called with the mutex held; do not call any client methods from the `on_pong` block.
69
+
70
+ ### Timeouts
71
+
72
+ The `Kontena::Websocket::Client` uses timeouts (given in `options`) to deal with network errors and not leave the client hanging. Timeouts will raise a descriptive `Kontena::Websocket::TimeoutError` from either the `connect`, `read` or `send/close` methods.
73
+
74
+ * `connect_timeout` is used for both TCP, SSL `connect` operations
75
+ * `open_timeout` is used for the websocket `open` handshake
76
+ * `write_timeout` is used for each socket `write` operation
77
+ * `ping_timeout` is used for each websocket `ping` request
78
+
79
+ The `Kontena::Websocket::Client` supports keepalive pings, where the `read` method will send a websocket ping request every `ping_interval` seconds, and raise a `Kontena::Websocket::TimeoutError` if it does not receive a websocket pong response within `ping_timeout` seconds.
80
+
81
+ ### SSL Validation
82
+
83
+ The `Kontena::Websocket::Client` validates `wss://` server SSL certificates by default, using the OpenSSL APIs to provide useful `Kontena::Websocket::SSLVerifyError` messages. It also provides methods to inspect and validate the server SSL certificates, even when not using strict SSL validation.
84
+
85
+ Example code and resulting messages:
86
+
87
+ ```ruby
88
+ begin
89
+ ssl_cert = ws.ssl_cert!
90
+ rescue Kontena::Websocket::SSLVerifyError => ssl_error
91
+ ssl_cert = ssl_error.cert
92
+ else
93
+ ssl_error = nil
94
+ end
95
+
96
+ if ssl_cert && ssl_error
97
+ $logger.warn "Connected to #{url} with ssl errors: #{ssl_error} (subject #{ssl_cert.subject}, issuer #{ssl_cert.issuer})"
98
+ elsif ssl_error
99
+ $logger.warn "Connected to #{url} with ssl errors: #{ssl_error}"
100
+ elsif ssl_cert && !ws.ssl_verify?
101
+ $logger.warn "Connected to #{url} without ssl verify: #{ssl_cert.subject} (issuer #{ssl_cert.issuer})"
102
+ elsif ssl_cert
103
+ $logger.info "Connected to #{url} with ssl verify: #{ssl_cert.subject} (issuer #{ssl_cert.issuer})"
104
+ else
105
+ $logger.info "Connected to #{url} without ssl"
106
+ end
107
+ ```
108
+
109
+ * `ERROR -- websocket-echo-client: certificate verify failed: self signed certificate`
110
+ * `WARN -- websocket-echo-client: Connected to wss://localhost:9293 with ssl errors: certificate verify failed: self signed certificate (subject /CN=kontena.test, issuer /CN=kontena.test)`
111
+ * `WARN -- websocket-echo-client: Connected to wss://echo.websocket.org without ssl verify: /OU=Domain Control Validated/CN=*.websocket.org (issuer /C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy.com/repository//CN=Go Daddy Secure Certificate Authority - G2)`
112
+ * `INFO -- websocket-echo-client: Connected to wss://echo.websocket.org with ssl verify: /OU=Domain Control Validated/CN=*.websocket.org (issuer /C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy.com/repository//CN=Go Daddy Secure Certificate Authority - G2)`
113
+ * `INFO -- websocket-echo-client: Connected to ws://echo.websocket.org without ssl`
114
+
115
+ ### Examples
116
+
117
+ Use `bundle exec ./examples/...` to run the examples.
118
+
119
+ #### [Websocket Echo Client](./examples/websocket-echo-client.rb)
120
+
121
+ Connect to a websocket server, displaying the server SSL certificate. Send lines from `stdin`, and write messages to `stdout`. Close on `EOF`.
122
+
123
+ ```
124
+ I, [2017-07-21T17:06:48.353944 #17507] INFO -- : Connecting to wss://echo.websocket.org...
125
+ I, [2017-07-21T17:06:49.329616 #17507] INFO -- : Connected to wss://echo.websocket.org with ssl verify: /OU=Domain Control Validated/CN=*.websocket.org (issuer /C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy.com/repository//CN=Go Daddy Secure Certificate Authority - G2)
126
+ hello
127
+ D, [2017-07-21T17:06:50.447423 #17507] DEBUG -- : websocket write: "hello\n"
128
+ D, [2017-07-21T17:06:50.578683 #17507] DEBUG -- : websocket read: "hello\n"
129
+ hello
130
+ D, [2017-07-21T17:06:51.135395 #17507] DEBUG -- : websocket close: EOF
131
+ I, [2017-07-21T17:06:51.375279 #17507] INFO -- : Client closed connection with code 1000: EOF
132
+ ```
133
+
134
+ ## Contributing
135
+
136
+ Bug reports and pull requests are welcome on GitHub at https://github.com/kontena/kontena-websocket-client.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'logger'
4
+ require 'kontena-websocket-client'
5
+
6
+ $logger = Logger.new(STDERR)
7
+ $logger.progname = 'websocket-echo-client'
8
+
9
+ def websocket_echo_writer(ws)
10
+ while line = $stdin.gets
11
+ $logger.debug "websocket write: #{line.inspect}"
12
+ ws.send(line)
13
+ end
14
+ $logger.debug "websocket close: EOF"
15
+
16
+ ws.close(1000, "EOF")
17
+ end
18
+
19
+ def websocket_echo_reader(ws)
20
+ ws.read do |msg|
21
+ $logger.debug "websocket read: #{msg.inspect}"
22
+ $stderr.puts msg
23
+ end
24
+ end
25
+
26
+ def websocket_echo_client(url, **options)
27
+ write_thread = nil
28
+
29
+ $logger.info "Connecting to #{url}..."
30
+
31
+ Kontena::Websocket::Client.connect(url, **options) do |ws|
32
+ begin
33
+ ssl_cert = ws.ssl_cert!
34
+ rescue Kontena::Websocket::SSLVerifyError => ssl_error
35
+ ssl_cert = ssl_error.cert
36
+ else
37
+ ssl_error = nil
38
+ end
39
+
40
+ if ssl_cert && ssl_error
41
+ $logger.warn "Connected to #{url} with ssl errors: #{ssl_error} (subject #{ssl_cert.subject}, issuer #{ssl_cert.issuer})"
42
+ elsif ssl_error
43
+ $logger.warn "Connected to #{url} with ssl errors: #{ssl_error}"
44
+ elsif ssl_cert && !ws.ssl_verify?
45
+ $logger.warn "Connected to #{url} without ssl verify: #{ssl_cert.subject} (issuer #{ssl_cert.issuer})"
46
+ elsif ssl_cert
47
+ $logger.info "Connected to #{url} with ssl verify: #{ssl_cert.subject} (issuer #{ssl_cert.issuer})"
48
+ else
49
+ $logger.info "Connected to #{url} without ssl"
50
+ end
51
+
52
+ write_thread = Thread.new {
53
+ websocket_echo_writer(ws)
54
+ }
55
+
56
+ websocket_echo_reader(ws)
57
+
58
+ $logger.info "Client closed connection with code #{ws.close_code}: #{ws.close_reason}"
59
+ end
60
+
61
+ rescue Kontena::Websocket::CloseError => exc
62
+ $logger.info "#{exc}"
63
+ rescue Kontena::Websocket::Error => exc
64
+ $logger.error "#{exc}"
65
+ ensure
66
+ if write_thread
67
+ write_thread.kill
68
+ write_thread.join
69
+ end
70
+ end
71
+
72
+ url = ARGV[0] || ENV['WEBSOCKET_URL'] || 'wss://echo.websocket.org'
73
+ ssl_verify = ENV['WEBSOCKET_SSL_VERIFY']
74
+ ssl_verify = !ssl_verify.nil? && !ssl_verify.empty?
75
+
76
+ websocket_echo_client(url,
77
+ ssl_params: {
78
+ verify_mode: ssl_verify ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE,
79
+ },
80
+ )
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ Gem::Specification.new do |spec|
3
+ spec.name = "kontena-websocket-client"
4
+ spec.version = '0.0.0'
5
+ spec.authors = ["Kontena, Inc"]
6
+ spec.email = ["info@kontena.io"]
7
+
8
+ spec.summary = %q{Websocket client library}
9
+ spec.homepage = "https://github.com/kontena/kontena-websocket-client"
10
+ spec.license = "Apache-2.0"
11
+
12
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
13
+ f.match(%r{^(test|spec|features)/})
14
+ end
15
+ spec.bindir = "bin"
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_dependency "websocket-driver", '~> 0.6.5'
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.15"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "~> 3.0"
24
+ end
@@ -0,0 +1,119 @@
1
+ # WebSocket::Driver.client(...) API
2
+ class Kontena::Websocket::Client::Connection
3
+ include Kontena::Websocket::Logging
4
+
5
+ # ruby version >= 2.3
6
+ module Waitable
7
+ # @param socket [Socket]
8
+ # @param timeout [Float] default (nil) blocks indefinitely
9
+ # @raise [Kontena::Websocket::TimeoutError]
10
+ def wait_socket_readable!(socket, timeout = nil)
11
+ debug "wait read: timeout=#{timeout}"
12
+
13
+ unless @socket.wait_readable(timeout)
14
+ raise Kontena::Websocket::TimeoutError, "read timeout after #{timeout}s"
15
+ end
16
+ end
17
+
18
+ # @param socket [Socket]
19
+ # @param timeout [Float] default (nil) blocks indefinitely
20
+ # @raise [Kontena::Websocket::TimeoutError]
21
+ def wait_socket_writable!(socket, timeout = nil)
22
+ debug "wait write: timeout=#{timeout}"
23
+
24
+ unless @socket.wait_writable(timeout)
25
+ raise Kontena::Websocket::TimeoutError, "write timeout after #{timeout}s"
26
+ end
27
+ end
28
+ end
29
+
30
+ # ruby version <= 2.2
31
+ #
32
+ # io/wait IO#wait_readable returns nil on EOF
33
+ module Waitable_Ruby2_2
34
+ # @param socket [Socket]
35
+ # @param timeout [Float] default (nil) blocks indefinitely
36
+ # @raise [Kontena::Websocket::TimeoutError]
37
+ def wait_socket_readable!(socket, timeout = nil)
38
+ debug "wait read: timeout=#{timeout}"
39
+
40
+ unless IO.select([socket], nil, nil, timeout)
41
+ raise Kontena::Websocket::TimeoutError, "read timeout after #{timeout}s"
42
+ end
43
+ end
44
+
45
+ # @param socket [Socket]
46
+ # @param timeout [Float] default (nil) blocks indefinitely
47
+ # @raise [Kontena::Websocket::TimeoutError]
48
+ def wait_socket_writable!(socket, timeout = nil)
49
+ debug "wait write: timeout=#{timeout}"
50
+
51
+ unless IO.select(nil, [socket], nil, timeout)
52
+ raise Kontena::Websocket::TimeoutError, "write timeout after #{timeout}s"
53
+ end
54
+ end
55
+ end
56
+
57
+ if Kontena::Websocket::Client.ruby_version? '2.3'
58
+ require 'io/wait'
59
+ include Waitable
60
+ else
61
+ include Waitable_Ruby2_2
62
+ end
63
+
64
+ attr_reader :uri
65
+
66
+ # @param uri [URI]
67
+ # @param socket [TCPSocket, OpenSSL::SSL::SSLSocket]
68
+ # @param write_timeout [Float] per each write syscall
69
+ def initialize(uri, socket, write_timeout: nil)
70
+ @uri = uri
71
+ @socket = socket
72
+ @write_timeout = write_timeout
73
+ end
74
+
75
+ # @return [String]
76
+ def url
77
+ @uri.to_s
78
+ end
79
+
80
+ # Wait up to timeout before retrying any blocking operation.
81
+ #
82
+ # @param timeout [Float] default (nil) blocks indefinitely
83
+ # @raise [Kontena::Websocket::TimeoutError]
84
+ def nonblocking_timeout(timeout = nil, &block)
85
+ return yield
86
+ rescue IO::WaitReadable
87
+ wait_socket_readable!(@socket, timeout) # raises Kontena::Websocket::TimeoutError
88
+ retry
89
+ rescue IO::WaitWritable
90
+ wait_socket_writable!(@socket, timeout) # raises Kontena::Websocket::TimeoutError
91
+ retry
92
+ end
93
+
94
+ # @param size [Integer]
95
+ # @param timeout [Float]
96
+ # @raise [EOFError]
97
+ # @return [String] 0..size bytes
98
+ def read(size, timeout: nil)
99
+ buf = nonblocking_timeout(timeout) do
100
+ @socket.read_nonblock(size)
101
+ end
102
+
103
+ debug "read size=#{size}: #buf=#{buf.size}"
104
+
105
+ return buf
106
+ end
107
+
108
+ # @param buf [String]
109
+ def write(buf)
110
+ until buf.empty?
111
+ # it can take more than the timeout to write out the entire buffer
112
+ size = nonblocking_timeout(@write_timeout) do
113
+ @socket.write_nonblock(buf)
114
+ end
115
+ debug "write #buf=#{buf.size}: size=#{size}"
116
+ buf = buf[size..-1]
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,13 @@
1
+ class Kontena::Websocket::Client
2
+ VERSION = "0.0.0"
3
+
4
+ # Running ruby >= version?
5
+ # @param gte_version [String]
6
+ # @return [Boolean]
7
+ def self.ruby_version?(gte_version)
8
+ ruby_version = RUBY_VERSION.split('.').map{|x|x.to_i}
9
+ gte_version = gte_version.split('.').map{|x|x.to_i}
10
+
11
+ return (ruby_version <=> gte_version) >= 0
12
+ end
13
+ end