iodine 0.7.59 → 0.8.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.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/.clang-format +15 -0
  3. data/.rspec +1 -0
  4. data/.yardopts +1 -3
  5. data/CHANGELOG.md +132 -12
  6. data/Gemfile +5 -7
  7. data/LICENSE.txt +1 -1
  8. data/README.md +147 -671
  9. data/Rakefile +9 -16
  10. data/examples/chatroom.nru +9 -0
  11. data/examples/client +74 -0
  12. data/examples/config-hello.nru +9 -0
  13. data/examples/config-hello.ru +11 -0
  14. data/examples/config-hijack.ru +13 -0
  15. data/examples/config-shootout.ru +43 -0
  16. data/examples/config.nru +120 -0
  17. data/examples/config.rb +37 -0
  18. data/examples/config.ru +42 -53
  19. data/examples/fake +24 -0
  20. data/examples/hello_neorack +137 -0
  21. data/examples/routes.nru +29 -0
  22. data/examples/routes.ru +42 -0
  23. data/exe/iodine +218 -258
  24. data/ext/iodine/extconf.rb +27 -100
  25. data/ext/iodine/fio-stl.h +128336 -0
  26. data/ext/iodine/fio-stl.md +26396 -0
  27. data/ext/iodine/iodine.c +213 -1393
  28. data/ext/iodine/iodine.h +250 -55
  29. data/ext/iodine/iodine_arg_helper.h +329 -0
  30. data/ext/iodine/iodine_caller.h +234 -25
  31. data/ext/iodine/iodine_cli.h +434 -0
  32. data/ext/iodine/iodine_compression.h +312 -0
  33. data/ext/iodine/iodine_connection.h +3484 -34
  34. data/ext/iodine/iodine_core.h +363 -0
  35. data/ext/iodine/iodine_crypto.h +1441 -0
  36. data/ext/iodine/iodine_defer.h +225 -4
  37. data/ext/iodine/iodine_json.h +738 -4
  38. data/ext/iodine/iodine_listener.h +263 -0
  39. data/ext/iodine/iodine_log.h +297 -0
  40. data/ext/iodine/iodine_minimap.h +543 -0
  41. data/ext/iodine/iodine_mustache.h +461 -4
  42. data/ext/iodine/iodine_pubsub_eng.h +729 -0
  43. data/ext/iodine/iodine_pubsub_history.h +547 -0
  44. data/ext/iodine/iodine_pubsub_msg.h +228 -0
  45. data/ext/iodine/iodine_redis.h +322 -0
  46. data/ext/iodine/iodine_store.h +418 -16
  47. data/ext/iodine/iodine_threads.h +279 -0
  48. data/ext/iodine/iodine_tls.h +515 -6
  49. data/ext/iodine/iodine_utils.h +923 -0
  50. data/iodine.gemspec +37 -42
  51. data/lib/iodine/benchmark.rb +1690 -0
  52. data/lib/iodine/documentation.rb +3085 -0
  53. data/lib/iodine/version.rb +17 -1
  54. data/lib/iodine.rb +54 -255
  55. data/logo.svg +36 -0
  56. data/mustache.md +55 -0
  57. data/sig/iodine.rbs +4 -0
  58. metadata +58 -184
  59. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
  60. data/.github/workflows/ruby.yml +0 -42
  61. data/.gitignore +0 -20
  62. data/LIMITS.md +0 -41
  63. data/SPEC-PubSub-Draft.md +0 -159
  64. data/SPEC-WebSocket-Draft.md +0 -239
  65. data/bin/console +0 -22
  66. data/bin/info.md +0 -353
  67. data/bin/mustache_bench.rb +0 -100
  68. data/bin/poc/Gemfile.lock +0 -23
  69. data/bin/poc/README.md +0 -37
  70. data/bin/poc/config.ru +0 -66
  71. data/bin/poc/gemfile +0 -1
  72. data/bin/poc/www/index.html +0 -57
  73. data/examples/async_task.ru +0 -92
  74. data/examples/bates/README.md +0 -3
  75. data/examples/bates/config.ru +0 -342
  76. data/examples/bates/david+bold.pdf +0 -0
  77. data/examples/bates/public/drop-pdf.png +0 -0
  78. data/examples/bates/public/index.html +0 -600
  79. data/examples/echo.ru +0 -59
  80. data/examples/etag.ru +0 -16
  81. data/examples/hello.ru +0 -29
  82. data/examples/pubsub_engine.ru +0 -81
  83. data/examples/rack3.ru +0 -12
  84. data/examples/redis.ru +0 -70
  85. data/examples/shootout.ru +0 -73
  86. data/examples/sub-protocols.ru +0 -90
  87. data/examples/tcp_client.rb +0 -66
  88. data/examples/x-sendfile.ru +0 -14
  89. data/ext/iodine/fio.c +0 -11995
  90. data/ext/iodine/fio.h +0 -6371
  91. data/ext/iodine/fio_cli.c +0 -431
  92. data/ext/iodine/fio_cli.h +0 -189
  93. data/ext/iodine/fio_json_parser.h +0 -688
  94. data/ext/iodine/fio_siphash.c +0 -157
  95. data/ext/iodine/fio_siphash.h +0 -37
  96. data/ext/iodine/fio_tls.h +0 -129
  97. data/ext/iodine/fio_tls_missing.c +0 -649
  98. data/ext/iodine/fio_tls_openssl.c +0 -1106
  99. data/ext/iodine/fio_tmpfile.h +0 -50
  100. data/ext/iodine/fiobj.h +0 -44
  101. data/ext/iodine/fiobj4fio.h +0 -21
  102. data/ext/iodine/fiobj_ary.c +0 -333
  103. data/ext/iodine/fiobj_ary.h +0 -139
  104. data/ext/iodine/fiobj_data.c +0 -1187
  105. data/ext/iodine/fiobj_data.h +0 -167
  106. data/ext/iodine/fiobj_hash.c +0 -409
  107. data/ext/iodine/fiobj_hash.h +0 -176
  108. data/ext/iodine/fiobj_json.c +0 -622
  109. data/ext/iodine/fiobj_json.h +0 -68
  110. data/ext/iodine/fiobj_mem.h +0 -71
  111. data/ext/iodine/fiobj_mustache.c +0 -317
  112. data/ext/iodine/fiobj_mustache.h +0 -62
  113. data/ext/iodine/fiobj_numbers.c +0 -344
  114. data/ext/iodine/fiobj_numbers.h +0 -127
  115. data/ext/iodine/fiobj_str.c +0 -433
  116. data/ext/iodine/fiobj_str.h +0 -172
  117. data/ext/iodine/fiobject.c +0 -620
  118. data/ext/iodine/fiobject.h +0 -655
  119. data/ext/iodine/hpack.h +0 -1923
  120. data/ext/iodine/http.c +0 -2743
  121. data/ext/iodine/http.h +0 -1002
  122. data/ext/iodine/http1.c +0 -910
  123. data/ext/iodine/http1.h +0 -29
  124. data/ext/iodine/http1_parser.h +0 -878
  125. data/ext/iodine/http_internal.c +0 -1279
  126. data/ext/iodine/http_internal.h +0 -237
  127. data/ext/iodine/http_mime_parser.h +0 -350
  128. data/ext/iodine/iodine_caller.c +0 -230
  129. data/ext/iodine/iodine_connection.c +0 -941
  130. data/ext/iodine/iodine_defer.c +0 -420
  131. data/ext/iodine/iodine_fiobj2rb.h +0 -120
  132. data/ext/iodine/iodine_helpers.c +0 -282
  133. data/ext/iodine/iodine_helpers.h +0 -12
  134. data/ext/iodine/iodine_http.c +0 -1181
  135. data/ext/iodine/iodine_http.h +0 -23
  136. data/ext/iodine/iodine_json.c +0 -302
  137. data/ext/iodine/iodine_mustache.c +0 -567
  138. data/ext/iodine/iodine_pubsub.c +0 -580
  139. data/ext/iodine/iodine_pubsub.h +0 -26
  140. data/ext/iodine/iodine_rack_io.c +0 -273
  141. data/ext/iodine/iodine_rack_io.h +0 -20
  142. data/ext/iodine/iodine_store.c +0 -146
  143. data/ext/iodine/iodine_tcp.c +0 -346
  144. data/ext/iodine/iodine_tcp.h +0 -13
  145. data/ext/iodine/iodine_tls.c +0 -261
  146. data/ext/iodine/mustache_parser.h +0 -1546
  147. data/ext/iodine/redis_engine.c +0 -957
  148. data/ext/iodine/redis_engine.h +0 -79
  149. data/ext/iodine/resp_parser.h +0 -317
  150. data/ext/iodine/websocket_parser.h +0 -506
  151. data/ext/iodine/websockets.c +0 -743
  152. data/ext/iodine/websockets.h +0 -185
  153. data/lib/iodine/connection.rb +0 -61
  154. data/lib/iodine/json.rb +0 -42
  155. data/lib/iodine/mustache.rb +0 -113
  156. data/lib/iodine/pubsub.rb +0 -55
  157. data/lib/iodine/rack_utils.rb +0 -43
  158. data/lib/iodine/tls.rb +0 -16
  159. data/lib/rack/handler/iodine.rb +0 -33
  160. data/logo.png +0 -0
@@ -1,100 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'benchmark/ips'
4
- require 'mustache'
5
- require 'iodine'
6
-
7
- def benchmark_mustache
8
- # benchmark code was copied, in part, from:
9
- # https://github.com/mustache/mustache/blob/master/benchmarks/render_collection_benchmark.rb
10
- template = """
11
- {{#products}}
12
- <div class='product_brick'>
13
- <div class='container'>
14
- <div class='element'>
15
- <img src='images/{{image}}' class='product_miniature' />
16
- </div>
17
- <div class='element description'>
18
- <a href={{url}} class='product_name block bold'>
19
- {{external_index}}
20
- </a>
21
- </div>
22
- </div>
23
- </div>
24
- {{/products}}
25
- """
26
-
27
- IO.write "test_template.mustache", template
28
- filename = "test_template.mustache"
29
-
30
- data_1 = {
31
- products: [ {
32
- :external_index=>"This <product> should've been \"properly\" escaped.",
33
- :url=>"/products/7",
34
- :image=>"products/product.jpg"
35
- } ]
36
- }
37
- data_1000 = {
38
- products: []
39
- }
40
-
41
- 1000.times do
42
- data_1000[:products] << {
43
- :external_index=>"product",
44
- :url=>"/products/7",
45
- :image=>"products/product.jpg"
46
- }
47
- end
48
-
49
- data_1000_escaped = {
50
- products: []
51
- }
52
-
53
- 1000.times do
54
- data_1000_escaped[:products] << {
55
- :external_index=>"This <product> should've been \"properly\" escaped.",
56
- :url=>"/products/7",
57
- :image=>"products/product.jpg"
58
- }
59
- end
60
-
61
- view = Mustache.new
62
- view.template = template
63
- view.render # Call render once so the template will be compiled
64
- iodine_view = Iodine::Mustache.new(filename)
65
-
66
- puts "Ruby Mustache rendering (and HTML escaping) results in:",
67
- view.render(data_1), "",
68
- "Notice that Iodine::Mustache rendering (and HTML escaping) results in agressive escaping:",
69
- iodine_view.render(data_1), "", "----"
70
-
71
- # return;
72
-
73
- Benchmark.ips do |x|
74
- x.report("Ruby Mustache render list of 1000") do |times|
75
- view.render(data_1000)
76
- end
77
- x.report("Iodine::Mustache render list of 1000") do |times|
78
- iodine_view.render(data_1000)
79
- end
80
-
81
- x.report("Ruby Mustache render list of 1000 with escaped data") do |times|
82
- view.render(data_1000_escaped)
83
- end
84
- x.report("Iodine::Mustache render list of 1000 with escaped data") do |times|
85
- iodine_view.render(data_1000_escaped)
86
- end
87
-
88
- x.report("Ruby Mustache - no chaching - render list of 1000") do |times|
89
- tmp = Mustache.new
90
- tmp.template = template
91
- tmp.render(data_1000)
92
- end
93
- x.report("Iodine::Mustache - no chaching - render list of 1000") do |times|
94
- Iodine::Mustache.render(nil, data_1000, template)
95
- end
96
-
97
- end
98
- end
99
-
100
- benchmark_mustache
data/bin/poc/Gemfile.lock DELETED
@@ -1,23 +0,0 @@
1
- GIT
2
- remote: https://github.com/boazsegev/iodine.git
3
- revision: 8b57a5f2008b9c35f2cf589be7ed121823a84582
4
- specs:
5
- iodine (0.2.0)
6
- rack
7
- rake-compiler
8
-
9
- GEM
10
- specs:
11
- rack (2.0.1)
12
- rake (11.2.2)
13
- rake-compiler (0.9.5)
14
- rake
15
-
16
- PLATFORMS
17
- ruby
18
-
19
- DEPENDENCIES
20
- iodine!
21
-
22
- BUNDLED WITH
23
- 1.12.5
data/bin/poc/README.md DELETED
@@ -1,37 +0,0 @@
1
- # A proof of concept for Rack's `env['rack.websocket']`
2
-
3
- This is a proof of concept for Rack based Websocket connections, showing how the Rack API can be adjusted to support server native real-time connections.
4
-
5
- The chosen proof of concept was the ugliest chatroom I could find.
6
-
7
- Although my hope is that Rack will adopt the concept and make `env['rack.websocket?']` and `env['rack.websocket']` part of it's standard, at the moment it's an Iodine specific feature, implemented using `env['iodine.websocket']`.
8
-
9
- ## Install
10
-
11
- Install required gems using:
12
-
13
- ```sh
14
- bundler install
15
- ```
16
-
17
- ## Run
18
-
19
- Run this application single threaded:
20
-
21
- ```sh
22
- bundler exec iodine -- -www ./www
23
- ```
24
-
25
- Or both multi-threaded and forked (you'll notice that memory barriers for forked processes prevent websocket broadcasting from reaching websockets connected to a different process).
26
-
27
- ```sh
28
- bundler exec iodine -- -www ./www -t 16 -w 4
29
- ```
30
-
31
- ## Further reading
32
-
33
- * https://github.com/rack/rack/issues/1093
34
-
35
- * https://bowild.wordpress.com/2016/07/31/the-dark-side-of-the-rack
36
-
37
- * https://github.com/boazsegev/iodine
data/bin/poc/config.ru DELETED
@@ -1,66 +0,0 @@
1
- # The Rack Application container
2
- module MyRackApplication
3
- # Rack applications use the `call` callback to handle HTTP requests.
4
- def self.call(env)
5
- # if upgrading...
6
- if env['HTTP_UPGRADE'.freeze] =~ /websocket/i
7
- # We can assign a class or an instance that implements callbacks.
8
- # We will assign an object, passing it the request information (`env`)
9
- env['iodine.websocket'.freeze] = MyWebsocket.new(env)
10
- # Rack responses must be a 3 item array
11
- # [status, {http: :headers}, ["response body"]]
12
- return [0, {}, []]
13
- end
14
- # a semi-regualr HTTP response
15
- out = File.open File.expand_path('../www/index.html', __FILE__)
16
- [200, { 'X-Sendfile' => File.expand_path('../www/index.html', __FILE__),
17
- 'Content-Length' => out.size }, out]
18
- end
19
- end
20
-
21
- # The Websocket Callback Object
22
- class MyWebsocket
23
- # this is optional, but I wanted the object to have the nickname provided in
24
- # the HTTP request
25
- def initialize(env)
26
- # we need to change the ASCI Rack encoding to UTF-8,
27
- # otherwise everything with the nickname will be a binary "blob" in the
28
- # Javascript layer
29
- @nickname = env['PATH_INFO'][1..-1].force_encoding 'UTF-8'
30
- end
31
-
32
- # A classic websocket callback, called when the connection is opened and
33
- # linked to this object
34
- def on_open
35
- puts 'We have a websocket connection'
36
- end
37
-
38
- # A classic websocket callback, called when the connection is closed
39
- # (after disconnection).
40
- def on_close
41
- puts "Bye Bye... #{count} connections left..."
42
- end
43
-
44
- # A server-side niceness, called when the server if shutting down,
45
- # to gracefully disconnect (before disconnection).
46
- def on_shutdown
47
- write 'The server is shutting down, goodbye.'
48
- end
49
-
50
- def on_message(data)
51
- puts "got message: #{data} encoded as #{data.encoding}"
52
- # data is a temporary string, it's buffer cleared as soon as we return.
53
- # So we make a copy with the desired format.
54
- tmp = "#{@nickname}: #{data}"
55
- # The `write` method was added by the server and writes to the current
56
- # connection
57
- write tmp
58
- puts "broadcasting #{tmp.bytesize} bytes with encoding #{tmp.encoding}"
59
- # `each` was added by the server and excludes this connection
60
- # (each except self).
61
- each { |h| h.write tmp }
62
- end
63
- end
64
-
65
- # `run` is a Rack API command, telling Rack where the `call(env)` callback is located.
66
- run MyRackApplication
data/bin/poc/gemfile DELETED
@@ -1 +0,0 @@
1
- gem 'iodine', git: 'https://github.com/boazsegev/iodine.git'
@@ -1,57 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
5
- <script>
6
- ws = NaN
7
- handle = ''
8
- function onsubmit(e) {
9
- e.preventDefault();
10
- if($('#text')[0].value == '') {return false}
11
- if(ws && ws.readyState == 1) {
12
- ws.send($('#text')[0].value);
13
- $('#text')[0].value = '';
14
- } else {
15
- handle = $('#text')[0].value
16
- var url = (window.location.protocol.match(/https/) ? 'wss' : 'ws') +
17
- '://' + window.document.location.host +
18
- '/' + $('#text')[0].value
19
- ws = new WebSocket(url)
20
- ws.onopen = function(e) {
21
- output("<b>Connected :-)</b>");
22
- $('#text')[0].value = '';
23
- $('#text')[0].placeholder = 'your message';
24
- }
25
- ws.onclose = function(e) {
26
- output("<b>Disonnected :-/</b>")
27
- $('#text')[0].value = '';
28
- $('#text')[0].placeholder = 'nickname';
29
- $('#text')[0].value = handle
30
- }
31
- ws.onmessage = function(e) {
32
- output(e.data);
33
- }
34
- }
35
- return false;
36
- }
37
- function output(data) {
38
- $('#output').append("<li>" + data + "</li>")
39
- $('#output').animate({ scrollTop:
40
- $('#output')[0].scrollHeight }, "slow");
41
- }
42
- </script>
43
- <style>
44
- html, body {width:100%; height: 100%; background-color: #ddd; color: #111;}
45
- h3, form {text-align: center;}
46
- input {background-color: #fff; color: #111; padding: 0.3em;}
47
- </style>
48
- </head><body>
49
- <h3>The Ugly Chatroom POC</h3>
50
- <form id='form'>
51
- <input type='text' id='text' name='text' placeholder='nickname'></input>
52
- <input type='submit' value='send'></input>
53
- </form>
54
- <script> $('#form')[0].onsubmit = onsubmit </script>
55
- <ul id='output'></ul>
56
- </body>
57
- </html>
@@ -1,92 +0,0 @@
1
- # This is a task scheduling WebSocket push example application.
2
- #
3
- # Benchmark HTTPP with `ab` or `wrk` (a 5 seconds benchmark with 2000 concurrent clients):
4
- #
5
- # ab -c 2000 -t 5 -n 1000000 -k http://127.0.0.1:3000/
6
- # wrk -c2000 -d5 -t12 http://localhost:3000/
7
- #
8
- # Test websocket tasks using the browser. For example:
9
- # ws = new WebSocket("ws://localhost:3000/userID"); ws.onmessage = function(e) {console.log(e.data);}; ws.onclose = function(e) {console.log("closed")};
10
- # ws.onopen = function(e) {ws.send(JSON.stringify({'task': 'echo', 'data': 'Hello!'}));};
11
- require 'iodine'
12
- require 'json'
13
-
14
- TASK_PUBLISHING_ENGINE = Iodine::PubSub::PROCESS
15
-
16
- # This module handles tasks and send them back to the frontend
17
- module TaskHandler
18
- def echo msg
19
- msg = Iodine::JSON.parse(msg, symbolize_names: true)
20
- publish_to = msg.delete(:from)
21
- Iodine.publish(publish_to, msg.to_json, TASK_PUBLISHING_ENGINE) if publish_to
22
- puts "performed 'echo' task"
23
- rescue => e
24
- puts "JSON task message error? #{e.message} - under attack?"
25
- end
26
-
27
- def add msg
28
- msg = Iodine::JSON.parse(msg, symbolize_names: true)
29
- raise "addition task requires an array of numbers" unless msg[:data].is_a?(Array)
30
- msg[:data] = msg[:data].inject(0){|sum,x| sum + x }
31
- publish_to = msg.delete(:from)
32
- Iodine.publish(publish_to, msg.to_json, TASK_PUBLISHING_ENGINE) if publish_to
33
- puts "performed 'add' task"
34
- rescue => e
35
- puts
36
- "JSON task message error? #{e.message} - under attack?"
37
- end
38
-
39
- def listen2tasks
40
- Iodine.subscribe(:echo) {|ch,msg| TaskHandler.echo(msg) }
41
- Iodine.subscribe(:add) {|ch,msg| TaskHandler.add(msg) }
42
- end
43
-
44
- extend self
45
- end
46
-
47
- module WebsocketClient
48
- def on_open client
49
- # Pub/Sub directly to the client (or use a block to process the messages)
50
- client.subscribe client.env['PATH_INFO'.freeze]
51
- end
52
- def on_message client, data
53
- # Strings and symbol channel names are equivalent.
54
- msg = Iodine::JSON.parse(data, symbolize_names: true)
55
- raise "no valid task" unless ["echo".freeze, "add".freeze].include? msg[:task]
56
- msg[:from] = client.env['PATH_INFO'.freeze]
57
- client.publish msg[:task], msg.to_json, TASK_PUBLISHING_ENGINE
58
- rescue => e
59
- puts "JSON message error? #{e.message}\n\t#{data}\n\t#{msg}"
60
- end
61
- extend self
62
- end
63
-
64
- APP = Proc.new do |env|
65
- if env['rack.upgrade?'.freeze] == :websocket
66
- env['rack.upgrade'.freeze] = WebsocketClient
67
- [0,{}, []] # It's possible to set cookies for the response.
68
- elsif env['rack.upgrade?'.freeze] == :sse
69
- puts "SSE connections can only receive data from the server, the can't write."
70
- env['rack.upgrade'.freeze] = WebsocketClient
71
- [0,{}, []] # It's possible to set cookies for the response.
72
- else
73
- [200, {"Content-Type" => "text/plain"}, ["Send messages with WebSockets using JSON.\ni.e.: {\"task\":\"add\", \"data\":[1,2]}"]]
74
- end
75
- end
76
-
77
- # test automatically for Redis extensions.
78
- if(Iodine::PubSub.default.is_a? Iodine::PubSub::Redis)
79
- TASK_PUBLISHING_ENGINE = Iodine::PubSub.default
80
- if(ARGV.include? "worker")
81
- TaskHandler.listen2tasks
82
- Iodine.workers = 1
83
- Iodine.threads = 16 if Iodine.threads == 0
84
- Iodine.start
85
- exit(0)
86
- end
87
- else
88
- TaskHandler.listen2tasks
89
- end
90
-
91
- # # or in config.ru
92
- run APP
@@ -1,3 +0,0 @@
1
- # Bates numbering with CombinePDF and Rack
2
-
3
- This folder holds a demo application combining Iodine's `X-Sendfile` support with the [`combine_pdf` gem](https://github.com/boazsegev/combine_pdf) to create a "bates numbering" tool that is common to the one used by some court systems (such as Israeli courts).