kinetic_sdk 0.1.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.
Files changed (245) hide show
  1. checksums.yaml +7 -0
  2. data/GettingStarted.md +64 -0
  3. data/README.md +383 -0
  4. data/bin/console +12 -0
  5. data/bin/setup +8 -0
  6. data/gems/kontena-websocket-client-0.1.1/Gemfile +4 -0
  7. data/gems/kontena-websocket-client-0.1.1/LICENSE +190 -0
  8. data/gems/kontena-websocket-client-0.1.1/README.md +138 -0
  9. data/gems/kontena-websocket-client-0.1.1/Rakefile +6 -0
  10. data/gems/kontena-websocket-client-0.1.1/benchmark/benchmark-client.rb +45 -0
  11. data/gems/kontena-websocket-client-0.1.1/benchmark/benchmark-em.rb +66 -0
  12. data/gems/kontena-websocket-client-0.1.1/benchmark/benchmark.rb +161 -0
  13. data/gems/kontena-websocket-client-0.1.1/benchmark/benchmark.sh +17 -0
  14. data/gems/kontena-websocket-client-0.1.1/benchmark/websocket-echo-server.go +207 -0
  15. data/gems/kontena-websocket-client-0.1.1/examples/websocket-echo-client.rb +80 -0
  16. data/gems/kontena-websocket-client-0.1.1/kontena-websocket-client.gemspec +24 -0
  17. data/gems/kontena-websocket-client-0.1.1/lib/kontena/websocket/client/connection.rb +119 -0
  18. data/gems/kontena-websocket-client-0.1.1/lib/kontena/websocket/client/version.rb +13 -0
  19. data/gems/kontena-websocket-client-0.1.1/lib/kontena/websocket/client.rb +848 -0
  20. data/gems/kontena-websocket-client-0.1.1/lib/kontena/websocket/error.rb +81 -0
  21. data/gems/kontena-websocket-client-0.1.1/lib/kontena/websocket/logging.rb +55 -0
  22. data/gems/kontena-websocket-client-0.1.1/lib/kontena/websocket/openssl_patch.rb +10 -0
  23. data/gems/kontena-websocket-client-0.1.1/lib/kontena-websocket-client.rb +15 -0
  24. data/gems/mime-types-3.1/Code-of-Conduct.rdoc +74 -0
  25. data/gems/mime-types-3.1/Contributing.rdoc +130 -0
  26. data/gems/mime-types-3.1/History.rdoc +658 -0
  27. data/gems/mime-types-3.1/Licence.rdoc +25 -0
  28. data/gems/mime-types-3.1/Manifest.txt +31 -0
  29. data/gems/mime-types-3.1/README.rdoc +202 -0
  30. data/gems/mime-types-3.1/Rakefile +254 -0
  31. data/gems/mime-types-3.1/lib/mime/type/columnar.rb +55 -0
  32. data/gems/mime-types-3.1/lib/mime/type.rb +573 -0
  33. data/gems/mime-types-3.1/lib/mime/types/_columnar.rb +135 -0
  34. data/gems/mime-types-3.1/lib/mime/types/cache.rb +56 -0
  35. data/gems/mime-types-3.1/lib/mime/types/columnar.rb +1 -0
  36. data/gems/mime-types-3.1/lib/mime/types/container.rb +30 -0
  37. data/gems/mime-types-3.1/lib/mime/types/deprecations.rb +32 -0
  38. data/gems/mime-types-3.1/lib/mime/types/full.rb +17 -0
  39. data/gems/mime-types-3.1/lib/mime/types/loader.rb +148 -0
  40. data/gems/mime-types-3.1/lib/mime/types/logger.rb +37 -0
  41. data/gems/mime-types-3.1/lib/mime/types/registry.rb +81 -0
  42. data/gems/mime-types-3.1/lib/mime/types.rb +228 -0
  43. data/gems/mime-types-3.1/lib/mime-types.rb +1 -0
  44. data/gems/mime-types-3.1/test/bad-fixtures/malformed +9 -0
  45. data/gems/mime-types-3.1/test/fixture/json.json +1 -0
  46. data/gems/mime-types-3.1/test/fixture/old-data +9 -0
  47. data/gems/mime-types-3.1/test/fixture/yaml.yaml +55 -0
  48. data/gems/mime-types-3.1/test/minitest_helper.rb +13 -0
  49. data/gems/mime-types-3.1/test/test_mime_type.rb +603 -0
  50. data/gems/mime-types-3.1/test/test_mime_types.rb +161 -0
  51. data/gems/mime-types-3.1/test/test_mime_types_cache.rb +109 -0
  52. data/gems/mime-types-3.1/test/test_mime_types_class.rb +155 -0
  53. data/gems/mime-types-3.1/test/test_mime_types_lazy.rb +43 -0
  54. data/gems/mime-types-3.1/test/test_mime_types_loader.rb +32 -0
  55. data/gems/mime-types-data-3.2016.0521/Code-of-Conduct.md +75 -0
  56. data/gems/mime-types-data-3.2016.0521/Contributing.md +157 -0
  57. data/gems/mime-types-data-3.2016.0521/History.md +351 -0
  58. data/gems/mime-types-data-3.2016.0521/Licence.md +25 -0
  59. data/gems/mime-types-data-3.2016.0521/Manifest.txt +18 -0
  60. data/gems/mime-types-data-3.2016.0521/README.md +61 -0
  61. data/gems/mime-types-data-3.2016.0521/Rakefile +80 -0
  62. data/gems/mime-types-data-3.2016.0521/data/mime-types.json +1 -0
  63. data/gems/mime-types-data-3.2016.0521/data/mime.content_type.column +1964 -0
  64. data/gems/mime-types-data-3.2016.0521/data/mime.docs.column +1964 -0
  65. data/gems/mime-types-data-3.2016.0521/data/mime.encoding.column +1964 -0
  66. data/gems/mime-types-data-3.2016.0521/data/mime.flags.column +1964 -0
  67. data/gems/mime-types-data-3.2016.0521/data/mime.friendly.column +1964 -0
  68. data/gems/mime-types-data-3.2016.0521/data/mime.pext.column +1964 -0
  69. data/gems/mime-types-data-3.2016.0521/data/mime.use_instead.column +1964 -0
  70. data/gems/mime-types-data-3.2016.0521/data/mime.xrefs.column +1964 -0
  71. data/gems/mime-types-data-3.2016.0521/lib/mime/types/data.rb +21 -0
  72. data/gems/mime-types-data-3.2016.0521/lib/mime-types-data.rb +3 -0
  73. data/gems/multipart-post-2.0.0/Gemfile +14 -0
  74. data/gems/multipart-post-2.0.0/History.txt +60 -0
  75. data/gems/multipart-post-2.0.0/Manifest.txt +9 -0
  76. data/gems/multipart-post-2.0.0/README.md +77 -0
  77. data/gems/multipart-post-2.0.0/Rakefile +9 -0
  78. data/gems/multipart-post-2.0.0/lib/composite_io.rb +108 -0
  79. data/gems/multipart-post-2.0.0/lib/multipart_post.rb +9 -0
  80. data/gems/multipart-post-2.0.0/lib/multipartable.rb +29 -0
  81. data/gems/multipart-post-2.0.0/lib/net/http/post/multipart.rb +27 -0
  82. data/gems/multipart-post-2.0.0/lib/parts.rb +96 -0
  83. data/gems/multipart-post-2.0.0/multipart-post.gemspec +22 -0
  84. data/gems/multipart-post-2.0.0/test/multibyte.txt +1 -0
  85. data/gems/multipart-post-2.0.0/test/net/http/post/test_multipart.rb +110 -0
  86. data/gems/multipart-post-2.0.0/test/test_composite_io.rb +115 -0
  87. data/gems/multipart-post-2.0.0/test/test_parts.rb +86 -0
  88. data/gems/parallel-1.12.1/MIT-LICENSE.txt +20 -0
  89. data/gems/parallel-1.12.1/lib/parallel/processor_count.rb +93 -0
  90. data/gems/parallel-1.12.1/lib/parallel/version.rb +3 -0
  91. data/gems/parallel-1.12.1/lib/parallel.rb +500 -0
  92. data/gems/ruby-progressbar-1.9.0/LICENSE.txt +19 -0
  93. data/gems/ruby-progressbar-1.9.0/README.md +38 -0
  94. data/gems/ruby-progressbar-1.9.0/Rakefile +2 -0
  95. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/base.rb +183 -0
  96. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/calculators/length.rb +99 -0
  97. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/calculators/running_average.rb +9 -0
  98. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/components/bar.rb +96 -0
  99. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/components/percentage.rb +29 -0
  100. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/components/rate.rb +43 -0
  101. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/components/time.rb +107 -0
  102. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/components/title.rb +13 -0
  103. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/components.rb +5 -0
  104. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/errors/invalid_progress_error.rb +4 -0
  105. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/format/formatter.rb +27 -0
  106. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/format/molecule.rb +59 -0
  107. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/format/string.rb +36 -0
  108. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/format.rb +3 -0
  109. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/output.rb +68 -0
  110. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/outputs/non_tty.rb +47 -0
  111. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/outputs/null.rb +33 -0
  112. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/outputs/tty.rb +32 -0
  113. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/progress.rb +118 -0
  114. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/refinements/enumerator.rb +25 -0
  115. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/refinements.rb +1 -0
  116. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/throttle.rb +25 -0
  117. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/time.rb +30 -0
  118. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/timer.rb +72 -0
  119. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar/version.rb +3 -0
  120. data/gems/ruby-progressbar-1.9.0/lib/ruby-progressbar.rb +18 -0
  121. data/gems/slugify-1.0.7/README +18 -0
  122. data/gems/slugify-1.0.7/lib/slugify.rb +1012 -0
  123. data/gems/slugify-1.0.7/lib/slugify_string.rb +11 -0
  124. data/gems/slugify-1.0.7/tests/slugify_test.rb +116 -0
  125. data/gems/slugify-1.0.7/tests/string_slugify_test.rb +23 -0
  126. data/gems/websocket-driver-0.6.5/CHANGELOG.md +123 -0
  127. data/gems/websocket-driver-0.6.5/LICENSE.md +22 -0
  128. data/gems/websocket-driver-0.6.5/README.md +369 -0
  129. data/gems/websocket-driver-0.6.5/examples/tcp_server.rb +28 -0
  130. data/gems/websocket-driver-0.6.5/ext/websocket-driver/Makefile +264 -0
  131. data/gems/websocket-driver-0.6.5/ext/websocket-driver/WebsocketMaskService.java +55 -0
  132. data/gems/websocket-driver-0.6.5/ext/websocket-driver/extconf.rb +4 -0
  133. data/gems/websocket-driver-0.6.5/ext/websocket-driver/websocket_mask.bundle +0 -0
  134. data/gems/websocket-driver-0.6.5/ext/websocket-driver/websocket_mask.c +41 -0
  135. data/gems/websocket-driver-0.6.5/ext/websocket-driver/websocket_mask.o +0 -0
  136. data/gems/websocket-driver-0.6.5/lib/websocket/driver/client.rb +140 -0
  137. data/gems/websocket-driver-0.6.5/lib/websocket/driver/draft75.rb +102 -0
  138. data/gems/websocket-driver-0.6.5/lib/websocket/driver/draft76.rb +96 -0
  139. data/gems/websocket-driver-0.6.5/lib/websocket/driver/event_emitter.rb +54 -0
  140. data/gems/websocket-driver-0.6.5/lib/websocket/driver/headers.rb +45 -0
  141. data/gems/websocket-driver-0.6.5/lib/websocket/driver/hybi/frame.rb +20 -0
  142. data/gems/websocket-driver-0.6.5/lib/websocket/driver/hybi/message.rb +31 -0
  143. data/gems/websocket-driver-0.6.5/lib/websocket/driver/hybi.rb +406 -0
  144. data/gems/websocket-driver-0.6.5/lib/websocket/driver/proxy.rb +68 -0
  145. data/gems/websocket-driver-0.6.5/lib/websocket/driver/server.rb +80 -0
  146. data/gems/websocket-driver-0.6.5/lib/websocket/driver/stream_reader.rb +55 -0
  147. data/gems/websocket-driver-0.6.5/lib/websocket/driver.rb +199 -0
  148. data/gems/websocket-driver-0.6.5/lib/websocket/http/headers.rb +112 -0
  149. data/gems/websocket-driver-0.6.5/lib/websocket/http/request.rb +45 -0
  150. data/gems/websocket-driver-0.6.5/lib/websocket/http/response.rb +29 -0
  151. data/gems/websocket-driver-0.6.5/lib/websocket/http.rb +15 -0
  152. data/gems/websocket-driver-0.6.5/lib/websocket/mask.rb +14 -0
  153. data/gems/websocket-driver-0.6.5/lib/websocket/websocket_mask.rb +2 -0
  154. data/gems/websocket-driver-0.6.5/lib/websocket_mask.bundle +0 -0
  155. data/gems/websocket-driver-0.6.5-java/CHANGELOG.md +123 -0
  156. data/gems/websocket-driver-0.6.5-java/LICENSE.md +22 -0
  157. data/gems/websocket-driver-0.6.5-java/README.md +369 -0
  158. data/gems/websocket-driver-0.6.5-java/examples/tcp_server.rb +28 -0
  159. data/gems/websocket-driver-0.6.5-java/ext/websocket-driver/WebsocketMaskService.java +55 -0
  160. data/gems/websocket-driver-0.6.5-java/ext/websocket-driver/extconf.rb +4 -0
  161. data/gems/websocket-driver-0.6.5-java/ext/websocket-driver/websocket_mask.c +41 -0
  162. data/gems/websocket-driver-0.6.5-java/lib/websocket/driver/client.rb +140 -0
  163. data/gems/websocket-driver-0.6.5-java/lib/websocket/driver/draft75.rb +102 -0
  164. data/gems/websocket-driver-0.6.5-java/lib/websocket/driver/draft76.rb +96 -0
  165. data/gems/websocket-driver-0.6.5-java/lib/websocket/driver/event_emitter.rb +54 -0
  166. data/gems/websocket-driver-0.6.5-java/lib/websocket/driver/headers.rb +45 -0
  167. data/gems/websocket-driver-0.6.5-java/lib/websocket/driver/hybi/frame.rb +20 -0
  168. data/gems/websocket-driver-0.6.5-java/lib/websocket/driver/hybi/message.rb +31 -0
  169. data/gems/websocket-driver-0.6.5-java/lib/websocket/driver/hybi.rb +406 -0
  170. data/gems/websocket-driver-0.6.5-java/lib/websocket/driver/proxy.rb +68 -0
  171. data/gems/websocket-driver-0.6.5-java/lib/websocket/driver/server.rb +80 -0
  172. data/gems/websocket-driver-0.6.5-java/lib/websocket/driver/stream_reader.rb +55 -0
  173. data/gems/websocket-driver-0.6.5-java/lib/websocket/driver.rb +199 -0
  174. data/gems/websocket-driver-0.6.5-java/lib/websocket/http/headers.rb +112 -0
  175. data/gems/websocket-driver-0.6.5-java/lib/websocket/http/request.rb +45 -0
  176. data/gems/websocket-driver-0.6.5-java/lib/websocket/http/response.rb +29 -0
  177. data/gems/websocket-driver-0.6.5-java/lib/websocket/http.rb +15 -0
  178. data/gems/websocket-driver-0.6.5-java/lib/websocket/mask.rb +14 -0
  179. data/gems/websocket-driver-0.6.5-java/lib/websocket/websocket_mask.rb +2 -0
  180. data/gems/websocket-driver-0.6.5-java/lib/websocket_mask.jar +0 -0
  181. data/gems/websocket-extensions-0.1.3/CHANGELOG.md +15 -0
  182. data/gems/websocket-extensions-0.1.3/LICENSE.md +20 -0
  183. data/gems/websocket-extensions-0.1.3/README.md +313 -0
  184. data/gems/websocket-extensions-0.1.3/lib/websocket/extensions/parser.rb +111 -0
  185. data/gems/websocket-extensions-0.1.3/lib/websocket/extensions.rb +181 -0
  186. data/lib/kinetic-sdk.rb +1 -0
  187. data/lib/kinetic_sdk/bridgehub/bridgehub-sdk.rb +80 -0
  188. data/lib/kinetic_sdk/bridgehub/lib/access_keys.rb +67 -0
  189. data/lib/kinetic_sdk/bridgehub/lib/bridge.rb +69 -0
  190. data/lib/kinetic_sdk/discussions/discussions-sdk.rb +165 -0
  191. data/lib/kinetic_sdk/discussions/lib/discussions.rb +107 -0
  192. data/lib/kinetic_sdk/discussions/lib/invitations.rb +120 -0
  193. data/lib/kinetic_sdk/discussions/lib/messages.rb +190 -0
  194. data/lib/kinetic_sdk/discussions/lib/meta.rb +14 -0
  195. data/lib/kinetic_sdk/discussions/lib/participants.rb +64 -0
  196. data/lib/kinetic_sdk/discussions/lib/related_items.rb +54 -0
  197. data/lib/kinetic_sdk/discussions/lib/websockets.rb +96 -0
  198. data/lib/kinetic_sdk/filehub/filehub-sdk.rb +80 -0
  199. data/lib/kinetic_sdk/filehub/lib/access_keys.rb +67 -0
  200. data/lib/kinetic_sdk/filehub/lib/filestores.rb +67 -0
  201. data/lib/kinetic_sdk/request_ce/lib/attribute_definitions.rb +153 -0
  202. data/lib/kinetic_sdk/request_ce/lib/bridges.rb +66 -0
  203. data/lib/kinetic_sdk/request_ce/lib/categories.rb +34 -0
  204. data/lib/kinetic_sdk/request_ce/lib/datastore_form.rb +110 -0
  205. data/lib/kinetic_sdk/request_ce/lib/datastore_submissions.rb +157 -0
  206. data/lib/kinetic_sdk/request_ce/lib/form.rb +99 -0
  207. data/lib/kinetic_sdk/request_ce/lib/form_types.rb +51 -0
  208. data/lib/kinetic_sdk/request_ce/lib/jwt.rb +55 -0
  209. data/lib/kinetic_sdk/request_ce/lib/kapp.rb +93 -0
  210. data/lib/kinetic_sdk/request_ce/lib/meta.rb +14 -0
  211. data/lib/kinetic_sdk/request_ce/lib/oauth.rb +37 -0
  212. data/lib/kinetic_sdk/request_ce/lib/security_policy_definitions.rb +157 -0
  213. data/lib/kinetic_sdk/request_ce/lib/space.rb +89 -0
  214. data/lib/kinetic_sdk/request_ce/lib/submissions.rb +215 -0
  215. data/lib/kinetic_sdk/request_ce/lib/system_api.rb +69 -0
  216. data/lib/kinetic_sdk/request_ce/lib/teams.rb +113 -0
  217. data/lib/kinetic_sdk/request_ce/lib/users.rb +245 -0
  218. data/lib/kinetic_sdk/request_ce/lib/webhook_jobs.rb +142 -0
  219. data/lib/kinetic_sdk/request_ce/lib/webhooks.rb +192 -0
  220. data/lib/kinetic_sdk/request_ce/request-ce-sdk.rb +153 -0
  221. data/lib/kinetic_sdk/task/lib/access_keys.rb +94 -0
  222. data/lib/kinetic_sdk/task/lib/categories.rb +190 -0
  223. data/lib/kinetic_sdk/task/lib/config.rb +202 -0
  224. data/lib/kinetic_sdk/task/lib/engine.rb +42 -0
  225. data/lib/kinetic_sdk/task/lib/environment.rb +14 -0
  226. data/lib/kinetic_sdk/task/lib/errors.rb +165 -0
  227. data/lib/kinetic_sdk/task/lib/groups.rb +112 -0
  228. data/lib/kinetic_sdk/task/lib/handlers.rb +105 -0
  229. data/lib/kinetic_sdk/task/lib/health.rb +28 -0
  230. data/lib/kinetic_sdk/task/lib/license.rb +52 -0
  231. data/lib/kinetic_sdk/task/lib/policy_rules.rb +166 -0
  232. data/lib/kinetic_sdk/task/lib/setup.rb +56 -0
  233. data/lib/kinetic_sdk/task/lib/sources.rb +134 -0
  234. data/lib/kinetic_sdk/task/lib/tasks.rb +17 -0
  235. data/lib/kinetic_sdk/task/lib/trees.rb +274 -0
  236. data/lib/kinetic_sdk/task/lib/users.rb +75 -0
  237. data/lib/kinetic_sdk/task/task-sdk.rb +92 -0
  238. data/lib/kinetic_sdk/utils/kinetic-http-headers.rb +150 -0
  239. data/lib/kinetic_sdk/utils/kinetic-http-response.rb +75 -0
  240. data/lib/kinetic_sdk/utils/kinetic-http.rb +552 -0
  241. data/lib/kinetic_sdk/utils/logger.rb +70 -0
  242. data/lib/kinetic_sdk/utils/random.rb +30 -0
  243. data/lib/kinetic_sdk/version.rb +7 -0
  244. data/lib/kinetic_sdk.rb +57 -0
  245. metadata +414 -0
@@ -0,0 +1,138 @@
1
+ [![Build Status](https://travis-ci.org/kontena/kontena-websocket-client.svg?branch=master)](https://travis-ci.org/kontena/kontena-websocket-client)
2
+ [![Gem Version](https://badge.fury.io/rb/kontena-websocket-client.svg)](https://badge.fury.io/rb/kontena-websocket-client)
3
+ [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/kontena/kontena-websocket-client/master)
4
+
5
+ # Kontena::Websocket::Client
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'kontena-websocket-client'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install kontena-websocket-client
22
+
23
+ ## Usage
24
+
25
+ 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:
26
+
27
+ * The `connect` class method yields the connected `Kontena::Websocket::Client` object
28
+ * The `read` method yields received websocket messages and returns once the websocket is closed
29
+ * The `send` and `close` methods can be called from the `connect` block, the `read` block, or a different thread
30
+ * Any of these functions can also raise `Kontena::Websocket::Error`
31
+
32
+ ```ruby
33
+ require 'kontena-websocket-client'
34
+
35
+ def websocket_connect
36
+ Kontena::Websocket::Client.connect(url, options...) do |client|
37
+ on_open
38
+
39
+ client.send(...)
40
+ client.close(1000)
41
+
42
+ client.read do |message|
43
+ on_message(message)
44
+ end
45
+
46
+ on_close(client.close_code, client.close_reason) # client closed connection
47
+ end
48
+ rescue Kontena::Websocket::CloseError => exc
49
+ on_close(exc.code, exc.reason) # server closed connection
50
+ rescue Kontena::Websocket::Error => exc
51
+ on_error(exc)
52
+ end
53
+ ```
54
+ ### Environment variables
55
+
56
+ The library uses the following environment variables:
57
+
58
+ * `SSL_CERT_FILE`: Default value for `ssl_params: { ca_file: ... }`
59
+ * `SSL_CERT_PATH`: Default value for `ssl_params: { ca_path: ... }`
60
+
61
+ ### Threadsafe
62
+
63
+ The `Kontena::Websocket::Client` is threadsafe: while a single read thread is blocking on `read`, other threads may safely call `send`, `ping` and `close`.
64
+
65
+ The `read` block may also call `send`, `ping` and `close`.
66
+ Do not call `read` from multiple threads, or the websocket messages may get corrupted.
67
+
68
+ The library uses an internal `Mutex` to protect the internal `Websocket::Driver` state, and prevent socket read/write reordering/corruption.
69
+
70
+ XXX: The `on_pong` callback is called with the mutex held; do not call any client methods from the `on_pong` block.
71
+
72
+ ### Timeouts
73
+
74
+ 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.
75
+
76
+ * `connect_timeout` is used for both TCP, SSL `connect` operations
77
+ * `open_timeout` is used for the websocket `open` handshake
78
+ * `write_timeout` is used for each socket `write` operation
79
+ * `ping_timeout` is used for each websocket `ping` request
80
+
81
+ 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.
82
+
83
+ ### SSL Validation
84
+
85
+ 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.
86
+
87
+ Example code and resulting messages:
88
+
89
+ ```ruby
90
+ begin
91
+ ssl_cert = ws.ssl_cert!
92
+ rescue Kontena::Websocket::SSLVerifyError => ssl_error
93
+ ssl_cert = ssl_error.cert
94
+ else
95
+ ssl_error = nil
96
+ end
97
+
98
+ if ssl_cert && ssl_error
99
+ $logger.warn "Connected to #{url} with ssl errors: #{ssl_error} (subject #{ssl_cert.subject}, issuer #{ssl_cert.issuer})"
100
+ elsif ssl_error
101
+ $logger.warn "Connected to #{url} with ssl errors: #{ssl_error}"
102
+ elsif ssl_cert && !ws.ssl_verify?
103
+ $logger.warn "Connected to #{url} without ssl verify: #{ssl_cert.subject} (issuer #{ssl_cert.issuer})"
104
+ elsif ssl_cert
105
+ $logger.info "Connected to #{url} with ssl verify: #{ssl_cert.subject} (issuer #{ssl_cert.issuer})"
106
+ else
107
+ $logger.info "Connected to #{url} without ssl"
108
+ end
109
+ ```
110
+
111
+ * `ERROR -- websocket-echo-client: certificate verify failed: self signed certificate`
112
+ * `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)`
113
+ * `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)`
114
+ * `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)`
115
+ * `INFO -- websocket-echo-client: Connected to ws://echo.websocket.org without ssl`
116
+
117
+ ### Examples
118
+
119
+ Use `bundle exec ./examples/...` to run the examples.
120
+
121
+ #### [Websocket Echo Client](./examples/websocket-echo-client.rb)
122
+
123
+ Connect to a websocket server, displaying the server SSL certificate. Send lines from `stdin`, and write messages to `stdout`. Close on `EOF`.
124
+
125
+ ```
126
+ I, [2017-07-21T17:06:48.353944 #17507] INFO -- : Connecting to wss://echo.websocket.org...
127
+ 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)
128
+ hello
129
+ D, [2017-07-21T17:06:50.447423 #17507] DEBUG -- : websocket write: "hello\n"
130
+ D, [2017-07-21T17:06:50.578683 #17507] DEBUG -- : websocket read: "hello\n"
131
+ hello
132
+ D, [2017-07-21T17:06:51.135395 #17507] DEBUG -- : websocket close: EOF
133
+ I, [2017-07-21T17:06:51.375279 #17507] INFO -- : Client closed connection with code 1000: EOF
134
+ ```
135
+
136
+ ## Contributing
137
+
138
+ Bug reports and pull requests are welcome on GitHub at https://github.com/kontena/kontena-websocket-client.
@@ -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,45 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ Thread.abort_on_exception = true
4
+
5
+ require 'kontena-websocket-client'
6
+ require_relative './benchmark'
7
+
8
+ WEBSOCKET_OPTIONS = {
9
+ connect_timeout: 1.0,
10
+ open_timeout: 1.0,
11
+ ping_timeout: 1.0,
12
+ ping_interval: nil,
13
+ write_timeout: 5.0,
14
+ }
15
+
16
+ Kontena::Websocket::Logging.initialize_logger(STDERR, LOG_LEVEL)
17
+
18
+ run_benchmark do |url, **options|
19
+ send_thread = nil
20
+ reader = BenchmarkReader.new
21
+
22
+ Kontena::Websocket::Client.connect(url, **WEBSOCKET_OPTIONS) do |client|
23
+ $logger.info "connect: #{client}"
24
+
25
+ send_thread = Thread.new {
26
+ send_stats = benchmark_sender(**options) do |msg, seq|
27
+ client.send(msg)
28
+ end
29
+
30
+ client.close()
31
+
32
+ send_stats
33
+ }
34
+
35
+ reader.start()
36
+ client.read do |message|
37
+ reader.on_message(Time.now, message)
38
+ end
39
+ end
40
+
41
+ read_stats = reader.stop()
42
+ send_stats = send_thread.value
43
+
44
+ next send_stats, read_stats
45
+ end
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ Thread.abort_on_exception = true
4
+
5
+ require 'faye/websocket'
6
+ require 'eventmachine'
7
+ require_relative './benchmark'
8
+
9
+ run_benchmark do |url, **options|
10
+ send_thread = nil
11
+ reader = BenchmarkReader.new
12
+
13
+ EM.run {
14
+ ws = Faye::WebSocket::Client.new(url)
15
+
16
+ ws.on :open do |event|
17
+ $logger.info "open"
18
+
19
+ send_thread = Thread.new {
20
+ send_stats = benchmark_sender(**options) do |msg, seq|
21
+ EM.next_tick {
22
+ $logger.debug "send seq=%d" % [seq]
23
+ ws.send(msg)
24
+ }
25
+ end
26
+
27
+ EM.next_tick {
28
+ $logger.info "close..."
29
+ ws.close()
30
+ }
31
+
32
+ send_stats
33
+ }
34
+
35
+ reader.start()
36
+ end
37
+
38
+ read = 0
39
+
40
+ ws.on :message do |event|
41
+ seq, rtt = reader.on_message(Time.now, event.data)
42
+
43
+ $logger.debug "read %d: seq=%d rtt=%.2f" % [read, seq, rtt]
44
+
45
+ read += 1
46
+ end
47
+
48
+ ws.on :close do |event|
49
+ $logger.info "closed"
50
+
51
+ reader.stop
52
+ EM.stop
53
+ end
54
+
55
+ ws.on :error do |event|
56
+ $logger.warn "error: #{event}"
57
+
58
+ exit 1
59
+ end
60
+ }
61
+
62
+ read_stats = reader.stop()
63
+ send_stats = send_thread.value
64
+
65
+ next send_stats, read_stats
66
+ end
@@ -0,0 +1,161 @@
1
+ require 'logger'
2
+
3
+ LOG_LEVEL = ENV['LOG_LEVEL'] || Logger::WARN
4
+
5
+ $logger = Logger.new(STDERR)
6
+ $logger.level = LOG_LEVEL
7
+ $logger.progname = 'websocket-benchmark'
8
+
9
+ # @yield at given target interval
10
+ # @return after duration
11
+ def with_rate(rate, duration, &block)
12
+ t0 = Time.now
13
+ interval = 1.0 / rate
14
+ count = 0
15
+ count_miss = 0
16
+ total_yield = 0.0
17
+
18
+ while (t = Time.now) < t0 + duration
19
+ yield t
20
+
21
+ t_yield = Time.now
22
+
23
+ count += 1
24
+ total_yield += (t_yield - t)
25
+
26
+ t_next = t0 + count * interval
27
+
28
+ if t_next > t_yield
29
+ sleep t_next - t_yield
30
+ else
31
+ count_miss += 1
32
+ end
33
+ end
34
+
35
+ t_total = t - t0
36
+
37
+ return {
38
+ time: t_total,
39
+ count: count,
40
+ rate: count / t_total,
41
+ util: total_yield / t_total,
42
+ miss: (count_miss / count),
43
+ }
44
+ end
45
+
46
+ # @yield [seq, message]
47
+ # @return [Hash] after duration
48
+ def benchmark_sender(rate: 1000, duration: 5.0, message_size: 1000)
49
+ total_size = 0
50
+ seq = 0
51
+
52
+ padding = 'X'*(message_size - 16 - 16)
53
+
54
+ stats = with_rate(rate, duration) do |t|
55
+ message = '%15.6f %15d %s' % [t.to_f, seq, padding]
56
+
57
+ yield message, seq
58
+
59
+ total_size += message.length
60
+ seq += 1
61
+ end
62
+
63
+ return stats.merge(
64
+ bytes: total_size,
65
+ )
66
+ end
67
+
68
+ class BenchmarkReader
69
+ def initialize
70
+ @count = 0
71
+ @bytes = 0
72
+ @latency_total = 0.0
73
+ end
74
+
75
+ def start
76
+ @t_start = Time.now
77
+ end
78
+
79
+ # @param time [Time] Time.now
80
+ # @param message [String]
81
+ # @return [Integer, Float] seq, rtt
82
+ def on_message(time, message)
83
+ msg_time_s, msg_seq_s, padding = message.split(' ', 3)
84
+ msg_t = msg_time_s.to_f
85
+ msg_seq = msg_seq_s.to_i
86
+ t = time.to_f
87
+
88
+ @count += 1
89
+ @bytes += message.length
90
+ @latency_total += (t - msg_t)
91
+
92
+ return msg_seq, t - msg_t
93
+ end
94
+
95
+ # @return [Hash]
96
+ def stop
97
+ @t_stop = Time.now
98
+ seconds = @t_stop - @t_start
99
+
100
+ return {
101
+ time: seconds,
102
+ count: @count,
103
+ rate: @count / seconds,
104
+ bytes: @bytes,
105
+ bytes_rate: @bytes / seconds,
106
+ latency_avg: @latency_total / @count,
107
+ }
108
+ end
109
+ end
110
+
111
+ URL = 'ws://localhost:8080/echo'
112
+ RATES = [1, 10, 100, 1000, 3000, 5000, 10000]
113
+ DURATION = 5.0
114
+ MESSAGE_SIZE = 1000
115
+
116
+ HEADER = '%5s %6s/s %9s: send @ %9s/s (%6s%% %6s%%) read @ %9s/s (%6s%%) = %12s/s ~%9s'
117
+ FORMAT = '%5.2fs %6d/s %9d: send @ %9.2f/s (%6.2f%% %6.2f%%) read @ %9.2f/s (%6.2f%%) = %12s/s ~%9.6fs'
118
+
119
+ def to_si(val)
120
+ if val > 10**9
121
+ '%.3fG' % [val / 10**9]
122
+ elsif val > 10**6
123
+ '%.3fM' % [val / 10**6]
124
+ elsif val > 10**3
125
+ '%.3fK' % [val / 10**3]
126
+ else
127
+ '%.3f ' % [val]
128
+ end
129
+ end
130
+
131
+ # @yield [url, **options]
132
+ # @yieldreturn [send_stats, read_stats]
133
+ def run_benchmark()
134
+ url = ENV['URL'] || URL
135
+
136
+ rates = (ENV['RATES']&.split&.map{|r| Integer(r)} || RATES)
137
+ duration = (ENV['DURATION'] || DURATION).to_f
138
+ message_size = (ENV['MESSAGE_SIZE'] || MESSAGE_SIZE).to_i
139
+
140
+ puts HEADER % ['TIME ', 'RATE', 'COUNT', 'MESSAGES', 'UTIL', 'MISS', 'MESSAGES', 'DROP', 'BYTES', 'LATENCY']
141
+
142
+ for rate in rates
143
+ options = {
144
+ rate: rate,
145
+ duration: duration,
146
+ message_size: message_size,
147
+ }
148
+
149
+ $logger.info "benchmark: #{url} #{options}"
150
+
151
+ send_stats, read_stats = yield(url, **options)
152
+
153
+ drop_ratio = 1.0 - read_stats[:count].to_f / send_stats[:count].to_f
154
+
155
+ puts FORMAT % [
156
+ duration, rate, send_stats[:count],
157
+ send_stats[:rate], send_stats[:util] * 100.0, send_stats[:miss] * 100.0,
158
+ read_stats[:rate], drop_ratio * 100.0, to_si(read_stats[:bytes_rate]), read_stats[:latency_avg],
159
+ ]
160
+ end
161
+ end
@@ -0,0 +1,17 @@
1
+ #!/bin/sh
2
+
3
+ set -uex
4
+
5
+ DIR=$(dirname $0)
6
+ SERVER_ARGS=${SERVER_ARGS:- -drop}
7
+ BENCHMARK=${BENCHMARK:-benchmark-client.rb}
8
+
9
+ go get -d github.com/gorilla/websocket
10
+ go build -o $DIR/websocket-echo-server $DIR/websocket-echo-server.go
11
+
12
+ killall websocket-echo-server || true
13
+ $DIR/websocket-echo-server -quiet $SERVER_ARGS &
14
+
15
+ $DIR/${BENCHMARK}
16
+
17
+ trap "kill 0" INT EXIT
@@ -0,0 +1,207 @@
1
+ package main
2
+
3
+ import (
4
+ "flag"
5
+ "fmt"
6
+ "github.com/gorilla/websocket"
7
+ "log"
8
+ "net/http"
9
+ "time"
10
+ )
11
+
12
+ var websocketUpgrader = websocket.Upgrader{}
13
+
14
+ // single-goroutine read+write loop
15
+ // blocks reads if writes block
16
+ func websocketEchoSync(conn *websocket.Conn) error {
17
+ for {
18
+ if messageType, data, err := conn.ReadMessage(); err != nil {
19
+ if websocket.IsCloseError(err, 1000) {
20
+ break
21
+ } else {
22
+ return fmt.Errorf("websocket read: %v", err)
23
+ }
24
+ } else if err := conn.WriteMessage(messageType, data); err != nil {
25
+ return fmt.Errorf("websocket write: %v", err)
26
+ } else {
27
+ if options.Verbose {
28
+ log.Printf("websocket echo: %v", data)
29
+ }
30
+ }
31
+ }
32
+
33
+ return nil
34
+ }
35
+
36
+ type websocketMessage struct {
37
+ Type int
38
+ Data []byte
39
+ }
40
+
41
+ func websocketAsyncReader(conn *websocket.Conn, c chan websocketMessage) error {
42
+ defer close(c)
43
+
44
+ var messages, dropped int
45
+ var start = time.Now()
46
+
47
+ for {
48
+ if messageType, data, err := conn.ReadMessage(); err != nil {
49
+ if websocket.IsCloseError(err, 1000) {
50
+ if options.Verbose {
51
+ log.Printf("websocket read close: %v", err)
52
+ }
53
+
54
+ break
55
+ } else {
56
+ return fmt.Errorf("websocket read: %v", err)
57
+ }
58
+ } else {
59
+ var m = websocketMessage{messageType, data}
60
+ messages += 1
61
+
62
+ select {
63
+ case c <- m:
64
+ if options.Verbose {
65
+ log.Printf("websocket read: %v", m.Data)
66
+ }
67
+
68
+ default:
69
+ dropped += 1
70
+
71
+ if options.Verbose {
72
+ log.Printf("websocket drop: %v", m.Data)
73
+ }
74
+ }
75
+ }
76
+ }
77
+
78
+ var end = time.Now()
79
+
80
+ if !options.Quiet {
81
+ var seconds = end.Sub(start).Seconds()
82
+
83
+ log.Printf("websocket read: %d messages in %.1fs (%.2f/s, dropped %.2f%%)",
84
+ messages, seconds,
85
+ float64(messages)/seconds,
86
+ float64(dropped)/float64(messages)*100.0,
87
+ )
88
+ }
89
+
90
+ return nil
91
+ }
92
+
93
+ func websocketAsyncWriter(conn *websocket.Conn, c <-chan websocketMessage) error {
94
+ for m := range c {
95
+ if err := conn.WriteMessage(m.Type, m.Data); err != nil {
96
+ return fmt.Errorf("websocket write: %v", err)
97
+ } else {
98
+ if options.Verbose {
99
+ log.Printf("websocket write: %v", m.Data)
100
+ }
101
+ }
102
+ }
103
+
104
+ return nil
105
+ }
106
+
107
+ func websocketEchoAsync(conn *websocket.Conn) error {
108
+ var messageChan = make(chan websocketMessage, options.DropBuffer)
109
+ var readClose struct {
110
+ code int
111
+ text string
112
+ }
113
+ var readError error
114
+
115
+ // custom close handler to send close frame after reader drains the message queue
116
+ conn.SetCloseHandler(func(code int, text string) error {
117
+ readClose.code = code
118
+ readClose.text = text
119
+
120
+ return nil
121
+ })
122
+
123
+ go func() {
124
+ if err := websocketAsyncReader(conn, messageChan); err != nil {
125
+ readError = err
126
+ log.Printf("websocket read error: %v", err)
127
+ }
128
+ }()
129
+
130
+ if err := websocketAsyncWriter(conn, messageChan); err != nil {
131
+ return err
132
+ } else if readError != nil {
133
+ return readError
134
+ } else {
135
+ var closeMessage = websocket.FormatCloseMessage(readClose.code, readClose.text)
136
+
137
+ if err := conn.WriteControl(websocket.CloseMessage, closeMessage, time.Time{}); err != nil {
138
+ log.Printf("websocket write close: %v", err)
139
+ } else {
140
+ if options.Verbose {
141
+ log.Printf("websocket write close")
142
+ }
143
+ }
144
+
145
+ return nil
146
+ }
147
+ }
148
+
149
+ func EchoHandler(w http.ResponseWriter, r *http.Request) {
150
+ if websocketConn, err := websocketUpgrader.Upgrade(w, r, nil); err != nil {
151
+ log.Printf("Websocket Upgrade error: %v", err)
152
+ w.WriteHeader(500)
153
+ fmt.Fprintf(w, "%v", err)
154
+ } else {
155
+ if !options.Quiet {
156
+ log.Printf("Websocket echo connect: %v", r.RemoteAddr)
157
+ }
158
+
159
+ defer websocketConn.Close()
160
+
161
+ if options.Drop {
162
+ if err := websocketEchoAsync(websocketConn); err != nil {
163
+ log.Printf("Websocket echo error: %v", err)
164
+ return
165
+ }
166
+ } else {
167
+ if err := websocketEchoSync(websocketConn); err != nil {
168
+ log.Printf("Websocket echo error: %v", err)
169
+ return
170
+ }
171
+ }
172
+
173
+ if !options.Quiet {
174
+ log.Printf("Websocket echo close: %v", r.RemoteAddr)
175
+ }
176
+ }
177
+ }
178
+
179
+ var options struct {
180
+ Listen string
181
+ Verbose bool
182
+ Quiet bool
183
+ Drop bool
184
+ DropBuffer uint
185
+ }
186
+
187
+ func init() {
188
+ flag.StringVar(&options.Listen, "listen", "localhost:8080", "HOST:PORT")
189
+ flag.BoolVar(&options.Verbose, "verbose", false, "log echo messages")
190
+ flag.BoolVar(&options.Quiet, "quiet", false, "do not log connects")
191
+ flag.BoolVar(&options.Drop, "drop", false, "drop messages if client is sending faster than reading")
192
+ flag.UintVar(&options.DropBuffer, "drop-buffer", 1000, "message buffer length")
193
+ }
194
+
195
+ func main() {
196
+ flag.Parse()
197
+
198
+ if !options.Quiet {
199
+ log.Printf("Websocket listen: %v", options.Listen)
200
+ }
201
+
202
+ http.HandleFunc("/echo", EchoHandler)
203
+
204
+ if err := http.ListenAndServe(options.Listen, nil); err != nil {
205
+ log.Fatalf("http listen: %v", err)
206
+ }
207
+ }