isomorfeus-iodine 0.7.44

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. data/.gitignore +20 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +32 -0
  6. data/.yardopts +8 -0
  7. data/CHANGELOG.md +1038 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE.txt +21 -0
  10. data/LIMITS.md +41 -0
  11. data/README.md +782 -0
  12. data/Rakefile +44 -0
  13. data/SPEC-PubSub-Draft.md +159 -0
  14. data/SPEC-WebSocket-Draft.md +239 -0
  15. data/bin/console +22 -0
  16. data/bin/info.md +353 -0
  17. data/bin/mustache_bench.rb +100 -0
  18. data/bin/poc/Gemfile.lock +23 -0
  19. data/bin/poc/README.md +37 -0
  20. data/bin/poc/config.ru +66 -0
  21. data/bin/poc/gemfile +1 -0
  22. data/bin/poc/www/index.html +57 -0
  23. data/examples/async_task.ru +92 -0
  24. data/examples/config.ru +56 -0
  25. data/examples/echo.ru +59 -0
  26. data/examples/hello.ru +29 -0
  27. data/examples/pubsub_engine.ru +81 -0
  28. data/examples/redis.ru +70 -0
  29. data/examples/shootout.ru +73 -0
  30. data/examples/sub-protocols.ru +90 -0
  31. data/examples/tcp_client.rb +66 -0
  32. data/examples/x-sendfile.ru +14 -0
  33. data/exe/iodine +277 -0
  34. data/ext/iodine/extconf.rb +109 -0
  35. data/ext/iodine/fio.c +11985 -0
  36. data/ext/iodine/fio.h +6373 -0
  37. data/ext/iodine/fio_cli.c +431 -0
  38. data/ext/iodine/fio_cli.h +189 -0
  39. data/ext/iodine/fio_json_parser.h +687 -0
  40. data/ext/iodine/fio_siphash.c +157 -0
  41. data/ext/iodine/fio_siphash.h +37 -0
  42. data/ext/iodine/fio_tls.h +129 -0
  43. data/ext/iodine/fio_tls_missing.c +649 -0
  44. data/ext/iodine/fio_tls_openssl.c +1056 -0
  45. data/ext/iodine/fio_tmpfile.h +50 -0
  46. data/ext/iodine/fiobj.h +44 -0
  47. data/ext/iodine/fiobj4fio.h +21 -0
  48. data/ext/iodine/fiobj_ary.c +333 -0
  49. data/ext/iodine/fiobj_ary.h +139 -0
  50. data/ext/iodine/fiobj_data.c +1185 -0
  51. data/ext/iodine/fiobj_data.h +167 -0
  52. data/ext/iodine/fiobj_hash.c +409 -0
  53. data/ext/iodine/fiobj_hash.h +176 -0
  54. data/ext/iodine/fiobj_json.c +622 -0
  55. data/ext/iodine/fiobj_json.h +68 -0
  56. data/ext/iodine/fiobj_mem.h +71 -0
  57. data/ext/iodine/fiobj_mustache.c +317 -0
  58. data/ext/iodine/fiobj_mustache.h +62 -0
  59. data/ext/iodine/fiobj_numbers.c +344 -0
  60. data/ext/iodine/fiobj_numbers.h +127 -0
  61. data/ext/iodine/fiobj_str.c +433 -0
  62. data/ext/iodine/fiobj_str.h +172 -0
  63. data/ext/iodine/fiobject.c +620 -0
  64. data/ext/iodine/fiobject.h +654 -0
  65. data/ext/iodine/hpack.h +1923 -0
  66. data/ext/iodine/http.c +2754 -0
  67. data/ext/iodine/http.h +1002 -0
  68. data/ext/iodine/http1.c +912 -0
  69. data/ext/iodine/http1.h +29 -0
  70. data/ext/iodine/http1_parser.h +873 -0
  71. data/ext/iodine/http_internal.c +1278 -0
  72. data/ext/iodine/http_internal.h +237 -0
  73. data/ext/iodine/http_mime_parser.h +350 -0
  74. data/ext/iodine/iodine.c +1430 -0
  75. data/ext/iodine/iodine.h +63 -0
  76. data/ext/iodine/iodine_caller.c +218 -0
  77. data/ext/iodine/iodine_caller.h +27 -0
  78. data/ext/iodine/iodine_connection.c +933 -0
  79. data/ext/iodine/iodine_connection.h +55 -0
  80. data/ext/iodine/iodine_defer.c +420 -0
  81. data/ext/iodine/iodine_defer.h +6 -0
  82. data/ext/iodine/iodine_fiobj2rb.h +120 -0
  83. data/ext/iodine/iodine_helpers.c +282 -0
  84. data/ext/iodine/iodine_helpers.h +12 -0
  85. data/ext/iodine/iodine_http.c +1171 -0
  86. data/ext/iodine/iodine_http.h +23 -0
  87. data/ext/iodine/iodine_json.c +302 -0
  88. data/ext/iodine/iodine_json.h +6 -0
  89. data/ext/iodine/iodine_mustache.c +567 -0
  90. data/ext/iodine/iodine_mustache.h +6 -0
  91. data/ext/iodine/iodine_pubsub.c +580 -0
  92. data/ext/iodine/iodine_pubsub.h +26 -0
  93. data/ext/iodine/iodine_rack_io.c +281 -0
  94. data/ext/iodine/iodine_rack_io.h +20 -0
  95. data/ext/iodine/iodine_store.c +142 -0
  96. data/ext/iodine/iodine_store.h +20 -0
  97. data/ext/iodine/iodine_tcp.c +346 -0
  98. data/ext/iodine/iodine_tcp.h +13 -0
  99. data/ext/iodine/iodine_tls.c +261 -0
  100. data/ext/iodine/iodine_tls.h +13 -0
  101. data/ext/iodine/mustache_parser.h +1546 -0
  102. data/ext/iodine/redis_engine.c +957 -0
  103. data/ext/iodine/redis_engine.h +79 -0
  104. data/ext/iodine/resp_parser.h +317 -0
  105. data/ext/iodine/websocket_parser.h +505 -0
  106. data/ext/iodine/websockets.c +735 -0
  107. data/ext/iodine/websockets.h +185 -0
  108. data/isomorfeus-iodine.gemspec +42 -0
  109. data/lib/iodine/connection.rb +61 -0
  110. data/lib/iodine/json.rb +42 -0
  111. data/lib/iodine/mustache.rb +113 -0
  112. data/lib/iodine/pubsub.rb +55 -0
  113. data/lib/iodine/rack_utils.rb +43 -0
  114. data/lib/iodine/tls.rb +16 -0
  115. data/lib/iodine/version.rb +3 -0
  116. data/lib/iodine.rb +274 -0
  117. data/lib/rack/handler/iodine.rb +33 -0
  118. data/logo.png +0 -0
  119. metadata +271 -0
@@ -0,0 +1,185 @@
1
+ /*
2
+ copyright: Boaz Segev, 2016-2019
3
+ license: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef H_WEBSOCKETS_H
8
+ #define H_WEBSOCKETS_H
9
+
10
+ #include <http.h>
11
+
12
+ /* support C++ */
13
+ #ifdef __cplusplus
14
+ extern "C" {
15
+ #endif
16
+
17
+ /** used internally: attaches the Websocket protocol to the socket. */
18
+ void websocket_attach(intptr_t uuid, http_settings_s *http_settings,
19
+ websocket_settings_s *args, void *data, size_t length);
20
+
21
+ /* *****************************************************************************
22
+ Websocket information
23
+ ***************************************************************************** */
24
+
25
+ /** Returns the opaque user data associated with the websocket. */
26
+ void *websocket_udata_get(ws_s *ws);
27
+
28
+ /**
29
+ * Sets the opaque user data associated with the websocket.
30
+ *
31
+ * Returns the old value, if any.
32
+ */
33
+ void *websocket_udata_set(ws_s *ws, void *udata);
34
+
35
+ /**
36
+ * Returns the underlying socket UUID.
37
+ *
38
+ * This is only relevant for collecting the protocol object from outside of
39
+ * websocket events, as the socket shouldn't be written to.
40
+ */
41
+ intptr_t websocket_uuid(ws_s *ws);
42
+
43
+ /**
44
+ * Returns 1 if the WebSocket connection is in Client mode (connected to a
45
+ * remote server) and 0 if the connection is in Server mode (a connection
46
+ * established using facil.io's HTTP server).
47
+ */
48
+ uint8_t websocket_is_client(ws_s *ws);
49
+
50
+ /* *****************************************************************************
51
+ Websocket Connection Management (write / close)
52
+ ***************************************************************************** */
53
+
54
+ /** Writes data to the websocket. Returns -1 on failure (0 on success). */
55
+ int websocket_write(ws_s *ws, fio_str_info_s msg, uint8_t is_text);
56
+ /** Closes a websocket connection. */
57
+ void websocket_close(ws_s *ws);
58
+
59
+ /* *****************************************************************************
60
+ Websocket Pub/Sub
61
+ =================
62
+
63
+ API for websocket pub/sub that can be used to publish messages across process
64
+ boundries.
65
+
66
+ Supports pub/sub engines (see {pubsub.h}) that can connect to a backend service
67
+ such as Redis.
68
+
69
+ The default pub/sub engine (if `NULL` or unspecified) will publish the messages
70
+ to the process cluster (all the processes in `fio_run`).
71
+
72
+ To publish to a channel, use the API provided in {pubsub.h}.
73
+ ***************************************************************************** */
74
+
75
+ /** Possible arguments for the {websocket_subscribe} function. */
76
+ struct websocket_subscribe_s {
77
+ /** the websocket receiving the message. REQUIRED. */
78
+ ws_s *ws;
79
+ /** the channel where the message was published. */
80
+ fio_str_info_s channel;
81
+ /**
82
+ * The callback that handles pub/sub notifications.
83
+ *
84
+ * Default: send directly to websocket client.
85
+ */
86
+ void (*on_message)(ws_s *ws, fio_str_info_s channel, fio_str_info_s msg,
87
+ void *udata);
88
+ /**
89
+ * An optional cleanup callback for the `udata`.
90
+ */
91
+ void (*on_unsubscribe)(void *udata);
92
+ /** User opaque data, passed along to the notification. */
93
+ void *udata;
94
+ /** An optional callback for pattern matching. */
95
+ fio_match_fn match;
96
+ /**
97
+ * When using client forwarding (no `on_message` callback), this indicates if
98
+ * messages should be sent to the client as binary blobs, which is the safest
99
+ * approach.
100
+ *
101
+ * Default: tests for UTF-8 data encoding and sends as text if valid UTF-8.
102
+ * Messages above ~32Kb are always assumed to be binary.
103
+ */
104
+ unsigned force_binary : 1;
105
+ /**
106
+ * When using client forwarding (no `on_message` callback), this indicates if
107
+ * messages should be sent to the client as text.
108
+ *
109
+ * `force_binary` has precedence.
110
+ *
111
+ * Default: see above.
112
+ *
113
+ */
114
+ unsigned force_text : 1;
115
+ };
116
+
117
+ /**
118
+ * Subscribes to a channel. See {struct websocket_subscribe_s} for possible
119
+ * arguments.
120
+ *
121
+ * Returns a subscription ID on success and 0 on failure.
122
+ *
123
+ * All subscriptions are automatically revoked once the websocket is closed.
124
+ *
125
+ * If the connections subscribes to the same channel more than once, messages
126
+ * will be merged. However, another subscription ID will be assigned, since two
127
+ * calls to {websocket_unsubscribe} will be required in order to unregister from
128
+ * the channel.
129
+ */
130
+ uintptr_t websocket_subscribe(struct websocket_subscribe_s args);
131
+
132
+ #define websocket_subscribe(wbsckt, ...) \
133
+ websocket_subscribe((struct websocket_subscribe_s){.ws = wbsckt, __VA_ARGS__})
134
+
135
+ /**
136
+ * Unsubscribes from a channel.
137
+ *
138
+ * Failures are silent.
139
+ *
140
+ * All subscriptions are automatically revoked once the websocket is closed. So
141
+ * only use this function to unsubscribe while the websocket is open.
142
+ */
143
+ void websocket_unsubscribe(ws_s *ws, uintptr_t subscription_id);
144
+
145
+ /** Optimize generic broadcasts, for use in websocket_optimize4broadcasts. */
146
+ #define WEBSOCKET_OPTIMIZE_PUBSUB (-32)
147
+ /** Optimize text broadcasts, for use in websocket_optimize4broadcasts. */
148
+ #define WEBSOCKET_OPTIMIZE_PUBSUB_TEXT (-33)
149
+ /** Optimize binary broadcasts, for use in websocket_optimize4broadcasts. */
150
+ #define WEBSOCKET_OPTIMIZE_PUBSUB_BINARY (-34)
151
+
152
+ /**
153
+ * Enables (or disables) broadcast optimizations.
154
+ *
155
+ * This is performed automatically by the `websocket_subscribe` function.
156
+ * However, this function is provided for enabling the pub/sub metadata based
157
+ * optimizations for external connections / subscriptions.
158
+ *
159
+ * This function allows enablement (or disablement) of these optimizations:
160
+ *
161
+ * * WEBSOCKET_OPTIMIZE_PUBSUB - optimize all direct transmission messages,
162
+ * best attempt to detect Text vs. Binary data.
163
+ * * WEBSOCKET_OPTIMIZE_PUBSUB_TEXT - optimize direct pub/sub text messages.
164
+ * * WEBSOCKET_OPTIMIZE_PUBSUB_BINARY - optimize direct pub/sub binary messages.
165
+ *
166
+ * Note: to disable an optimization it should be disabled the same amount of
167
+ * times it was enabled - multiple optimization enablements for the same type
168
+ * are merged, but reference counted (disabled when reference is zero).
169
+ *
170
+ * Note2: The pub/sub metadata type ID will match the optimnization type
171
+ * requested (i.e., `WEBSOCKET_OPTIMIZE_PUBSUB`) and the optimized data is a
172
+ * FIOBJ String containing a pre-encoded WebSocket packet ready to be sent.
173
+ * i.e.:
174
+ *
175
+ * FIOBJ pre_wrapped = (FIOBJ)fio_message_metadata(msg,
176
+ * WEBSOCKET_OPTIMIZE_PUBSUB);
177
+ * fiobj_send_free((intptr_t)msg->udata1, fiobj_dup(pre_wrapped));
178
+ */
179
+ void websocket_optimize4broadcasts(intptr_t type, int enable);
180
+
181
+ #ifdef __cplusplus
182
+ } /* extern "C" */
183
+ #endif
184
+
185
+ #endif
@@ -0,0 +1,42 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'iodine/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'isomorfeus-iodine'
8
+ spec.version = Iodine::VERSION
9
+ spec.authors = ['Jan Biedermann']
10
+ spec.email = ['jan@kursator.de']
11
+
12
+ spec.summary = 'iodine build for Isomorfeus'
13
+ spec.description = 'iodine build for Isomorfeus'
14
+ spec.homepage = 'https://github.com/janbiedermann/iodine'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = 'exe'
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = %w(lib ext)
21
+
22
+ spec.extensions = %w(ext/iodine/extconf.rb)
23
+
24
+ spec.required_ruby_version = '>= 2.2.2' # Because earlier versions had been discontinued
25
+
26
+ spec.requirements << 'A Unix based system: Linux / macOS / BSD.'
27
+ spec.requirements << 'An updated C compiler.'
28
+ spec.requirements << 'Ruby >= 2.3.8 (Ruby EOL).'
29
+ spec.requirements << 'Ruby >= 2.5.0 recommended.'
30
+ spec.requirements << 'TLS requires OpenSSL >= 1.1.0.'
31
+ spec.requirements << 'Or Windows with Ruby >= 3.0.0 build with MingW and MingW as compiler.'
32
+
33
+ # spec.add_development_dependency 'bundler', '>= 1.10', '< 2.0'
34
+ spec.add_development_dependency 'rake', '~> 13.0', '< 14.0'
35
+ spec.add_development_dependency 'minitest', '>=5', '< 6.0'
36
+ spec.add_development_dependency 'rspec', '>=3.9.0', '< 4.0'
37
+ spec.add_development_dependency 'spec', '>=5.3.0', '< 6.0'
38
+ spec.add_development_dependency 'rake-compiler', '>= 1', '< 2.0'
39
+
40
+ spec.post_install_message = "Thank you for installing Iodine #{Iodine::VERSION}.\n" +
41
+ "Remember: if iodine supports your business, it's only fair to give value back (code contributions / donations) to Bo."
42
+ end
@@ -0,0 +1,61 @@
1
+ module Iodine
2
+
3
+ # The default connection settings used by {Iodine.listen} and {Iodine.connect}.
4
+ #
5
+ # It's a Hash object that allows Iodine default values to be manipulated. i.e.:
6
+ #
7
+ # DEFAULT_SETTINGS[:port] = "8080" # replaces the default port, which is `ENV["port"] || "3000"`.
8
+ DEFAULT_SETTINGS = {}
9
+
10
+ # @deprecated use {Iodine::DEFAULT_SETTINGS}.
11
+ #
12
+ # The default connection settings used by {Iodine.listen} and {Iodine.connect}.
13
+ DEFAULT_HTTP_ARGS = DEFAULT_SETTINGS
14
+
15
+ # Iodine's {Iodine::Connection} class is the class that TCP/IP, WebSockets and SSE connections inherit from.
16
+ #
17
+ # Instances of this class are passed to the callback objects. i.e.:
18
+ #
19
+ # module MyConnectionCallbacks
20
+ #
21
+ # # called when the callback object is linked with a new client
22
+ # def on_open client
23
+ # client.is_a?(Iodine::Connection) # => true
24
+ # end
25
+ #
26
+ # # called when data is available
27
+ # def on_message client, data
28
+ # client.is_a?(Iodine::Connection) # => true
29
+ # end
30
+ #
31
+ # # called when the server is shutting down, before closing the client
32
+ # # (it's still possible to send messages to the client)
33
+ # def on_shutdown client
34
+ # client.is_a?(Iodine::Connection) # => true
35
+ # end
36
+ #
37
+ # # called when the client is closed (no longer available)
38
+ # def on_close client
39
+ # client.is_a?(Iodine::Connection) # => true
40
+ # end
41
+ #
42
+ # # called when all the previous calls to `client.write` have completed
43
+ # # (the local buffer was drained and is now empty)
44
+ # def on_drained client
45
+ # client.is_a?(Iodine::Connection) # => true
46
+ # end
47
+ #
48
+ # # called when timeout was reached, llowing a `ping` to be sent
49
+ # def ping client
50
+ # client.is_a?(Iodine::Connection) # => true
51
+ # clint.close() # close connection on timeout is the default
52
+ # end
53
+ #
54
+ # # Allows the module to be used as a static callback object (avoiding object allocation)
55
+ # extend self
56
+ # end
57
+ #
58
+ # All connection related actions can be performed using the methods provided through this class.
59
+ class Connection
60
+ end
61
+ end
@@ -0,0 +1,42 @@
1
+ module Iodine
2
+ # Iodine includes a lenient JSON parser that attempts to ignore JSON errors when possible and adds some extensions such as Hex numerical representations and comments.
3
+ #
4
+ # Depending on content (specifically, the effects of float number parsing), the Iodine JSON parser speed varies.
5
+ #
6
+ # On my system, Iodine is about 20%-30% faster than Ruby MRI's parser (Ruby version 2.5.1 vs. Iodine version 0.7.4). When using symbols the speed increase is even higher (50%-90% faster).
7
+ #
8
+ # It's easy to monkey-patch the system's `JSON.parse` method (not the `JSON.parse!` method) by using `Iodine.patch_json`.
9
+ #
10
+ # You can benchmark the Iodine JSON performance and decide if you wish to monkey-patch the Ruby implementation.
11
+ #
12
+ # JSON_FILENAME="foo.json"
13
+ #
14
+ # require 'json'
15
+ # require 'iodine'
16
+ # TIMES = 100
17
+ # STR = IO.binread(JSON_FILENAME); nil
18
+ #
19
+ # JSON.parse(STR) == Iodine::JSON.parse(STR) # => true
20
+ # JSON.parse!(STR) == Iodine::JSON.parse!(STR) # => undefined, maybe true maybe false
21
+ #
22
+ # # warm-up
23
+ # TIMES.times { JSON.parse STR }
24
+ # TIMES.times { Iodine::JSON.parse STR }
25
+ #
26
+ # puts ""; Benchmark.bm do |b|
27
+ # sys = b.report("system") { TIMES.times { JSON.parse STR } }
28
+ # sys_sym = b.report("system-sym") { TIMES.times { JSON.parse STR, symbolize_names: true } }
29
+ # iodine = b.report("iodine") { TIMES.times { Iodine::JSON.parse STR } }
30
+ # iodine_sym = b.report("iodine-sym") { TIMES.times { Iodine::JSON.parse STR, symbolize_names: true } }
31
+ #
32
+ # puts "----------------------------"
33
+ # puts "Iodine::JSON speed as percent of Ruby's native JSON:"
34
+ # puts "normal: #{sys/iodine}"
35
+ # puts "symolized: #{sys_sym/iodine_sym}"
36
+ # end; nil
37
+ #
38
+ # Note that the bang(!) method should NOT be used for monkey-patching the default JSON parser, since some important features are unsupported by the Iodine parser.
39
+ #
40
+ module JSON
41
+ end
42
+ end
@@ -0,0 +1,113 @@
1
+ module Iodine
2
+ # Iodine includes a safe and fast Mustache templating engine.
3
+ #
4
+ # The engine is simpler and safer to use (and often faster) than the official and feature richer Ruby engine.
5
+ #
6
+ # Note: {Iodine::Mustache} behaves differently than the official Ruby templating engine in a number of ways:
7
+ #
8
+ # * When a partial template can't be found, a `LoadError` exception is raised (the official implementation outputs an empty String).
9
+ #
10
+ # * HTML escaping is more agressive, increasing XSS protection. Read why at: https://wonko.com/post/html-escaping .
11
+ #
12
+ # * Partial template padding in Iodine adds padding to dynamic text as well as static text, unlike the official Ruby mustache engine. i.e., if an argument contains a new line marker, the new line will be padded to match the partial template padding.
13
+ #
14
+ # * Lambda support is significantly different. For example, the text returned from a lambda isn't parsed (no lambda interpolation).
15
+ #
16
+ # * Dot notation is tested in whole as well as in part (i.e. `user.name.first` will be tested as is, than the couplet `"user","name.first"` and than as each `"use","name","first"`), allowing for the Hash data to contain keys with dots while still supporting dot notation shortcuts.
17
+ #
18
+ # * Dot notation supports method names (even chained method names) as long as they don't have or require arguments. For example, `user.class.to_s` will behave differently on Iodine (returns call name as `String`) than on the official mustache engine (fails / returns empty string).
19
+ #
20
+ # Iodine Mustache's engine was designed to play best with basic data structures, such as results from the {Iodine::JSON} parser and doesn't require any special classes or types.
21
+ #
22
+ # Hash data is tested for Symbol keys before being tested for String keys and methods. This means that `:key` has precedence over `"key"`.
23
+ #
24
+ # Note: Although using methods as "keys" (or argument names) is supported, no Ruby code is evaluated. This means that only trusted (pre-existing) code will execute.
25
+ #
26
+ # Iodine's {Iodine::Mustache} engine performes about 5-7 times faster(!) than the official Ruby mustache engine. Tests performed with Ruby 2.6.0, comparing iodine 0.7.33 against mustache 1.1.0 using a 2.9 GHz Intel Core i9 CPU.
27
+ #
28
+ # You can benchmark the Iodine Mustache performance and decide if you wish to switch from the official Ruby implementation.
29
+ #
30
+ # require 'benchmark/ips'
31
+ # require 'mustache'
32
+ # require 'iodine'
33
+ #
34
+ # # Benchmark code was copied, in part, from:
35
+ # # https://github.com/mustache/mustache/blob/master/benchmarks/render_collection_benchmark.rb
36
+ # # The test is, sadly, biased and doesn't test for missing elements, proc/method resolution or template partials.
37
+ # def benchmark_mustache
38
+ # template = """
39
+ # {{#products}}
40
+ # <div class='product_brick'>
41
+ # <div class='container'>
42
+ # <div class='element'>
43
+ # <img src='images/{{image}}' class='product_miniature' />
44
+ # </div>
45
+ # <div class='element description'>
46
+ # <a href={{url}} class='product_name block bold'>
47
+ # {{external_index}}
48
+ # </a>
49
+ # </div>
50
+ # </div>
51
+ # </div>
52
+ # {{/products}}
53
+ # """
54
+ #
55
+ # IO.write "test_template.mustache", template
56
+ # filename = "test_template.mustache"
57
+ #
58
+ # data_1000 = {
59
+ # products: []
60
+ # }
61
+ # data_1000_escaped = {
62
+ # products: []
63
+ # }
64
+ #
65
+ # 1000.times do
66
+ # data_1000[:products] << {
67
+ # :external_index=>"product",
68
+ # :url=>"/products/7",
69
+ # :image=>"products/product.jpg"
70
+ # }
71
+ # data_1000_escaped[:products] << {
72
+ # :external_index=>"This <product> should've been \"properly\" escaped.",
73
+ # :url=>"/products/7",
74
+ # :image=>"products/product.jpg"
75
+ # }
76
+ # end
77
+ #
78
+ # view = Mustache.new
79
+ # view.template = template
80
+ # view.render # Call render once so the template will be compiled
81
+ # iodine_view = Iodine::Mustache.new(template: template)
82
+ #
83
+ # Benchmark.ips do |x|
84
+ # x.report("Ruby Mustache render list of 1000") do |times|
85
+ # view.render(data_1000)
86
+ # end
87
+ # x.report("Iodine::Mustache render list of 1000") do |times|
88
+ # iodine_view.render(data_1000)
89
+ # end
90
+ #
91
+ # x.report("Ruby Mustache render list of 1000 with escaped data") do |times|
92
+ # view.render(data_1000_escaped)
93
+ # end
94
+ # x.report("Iodine::Mustache render list of 1000 with escaped data") do |times|
95
+ # iodine_view.render(data_1000_escaped)
96
+ # end
97
+ #
98
+ # x.report("Ruby Mustache - no caching - render list of 1000") do |times|
99
+ # tmp = Mustache.new
100
+ # tmp.template = template
101
+ # tmp.render(data_1000)
102
+ # end
103
+ # x.report("Iodine::Mustache - no caching - render list of 1000") do |times|
104
+ # Iodine::Mustache.render(nil, data_1000, template)
105
+ # end
106
+ # end
107
+ # nil
108
+ # end
109
+ #
110
+ # benchmark_mustache
111
+ class Mustache
112
+ end
113
+ end
@@ -0,0 +1,55 @@
1
+ module Iodine
2
+ # Iodine is equiped with an internal pub/sub service that allows improved resource management from a deployment perspective.
3
+ #
4
+ # @note From {https://en.wikipedia.org/wiki/Publish–subscribe_pattern Wikipedia}: publish–subscribe is a messaging pattern where senders of messages, called publishers, do not program the messages to be sent directly to specific receivers, called subscribers, but instead characterize published messages into classes without knowledge of which subscribers, if any, there may be. Similarly, subscribers express interest in one or more classes and only receive messages that are of interest, without knowledge of which publishers, if any, there are.
5
+ #
6
+ # The common paradigm, which is implemented by pub/sub services like Redis,
7
+ # is for a "client" to "subscribe" to one or more "channels". Messages are streamed
8
+ # to these "channels" by different "publishers" (the application / other clients) and are
9
+ # broadcasted to the "clients" through their "subscription".
10
+ #
11
+ # Iodine's approach it to offload pub/sub resource costs from the pub/sub service
12
+ # (which is usually expensive to scale) onto the application layer.
13
+ #
14
+ # For example, the default (`nil`) pub/sub {Iodine::PubSub::Engine} implements
15
+ # an internal pub/sub service that manages subscriptions (clients and channels) throughout an Iodine process cluster without any need to connect to an external pub/sub service.
16
+ #
17
+ # If Iodine was runninng with 8 processes and 16 threads per process,
18
+ # a publishing in process A will be delivered to subscribers in process B.
19
+ #
20
+ # In addition, by inheriting the {Iodine::PubSub::Engine} class, it's easy to create pub/sub engines that connect to this
21
+ # underlying pub/sub service. This means that Iodine will call the engine's `subscribe` method only once per
22
+ # channel and once messages arrive, Iodine will distribute the messages to all the subscribed clients.
23
+ module PubSub
24
+ # The {Iodine::PubSub::Engine} class makes it easy to use leverage Iodine's pub/sub system using external services.
25
+ #
26
+ # Iodine comes with two built-in engines:
27
+ #
28
+ # * `Iodine::PubSub::Engine::CLUSTER` will distribute messages to all subscribers in the process cluster.
29
+ # * `Iodine::PubSub::Engine::PROCESS` will distribute messages to all subscribers sharing the same process.
30
+ #
31
+ # It's recommended that {Iodine::PubSub::Engine} instances be initialized only after Iodine
32
+ # started running (or the `fork`ing of the engine's connection will introduce communication issues).
33
+ #
34
+ # For this reason, the best approcah to initialization would be:
35
+ #
36
+ # class MyEngineClass < Iodine::PubSub::Engine
37
+ # # ...
38
+ # end
39
+ #
40
+ # Iodine.run do
41
+ # MyEngine = MyEngineClass.new
42
+ # end
43
+ #
44
+ # {Iodine::PubSub::Engine} child classes MUST override the {Iodine::PubSub::Engine#subscribe}, {Iodine::PubSub::Engine#unsubscribe} and {Iodine::PubSub::Engine#publish}
45
+ # in order to perform this actions using the backend service (i.e. using Redis).
46
+ #
47
+ # Once an {Iodine::PubSub::Engine} instance receives a message from the backend service,
48
+ # it should forward the message to the Iodine distribution layer using the {Iodine.publish} method, setting the 3rd argument to `false`.
49
+ #
50
+ # Iodine will than distribute the message to all registered clients in that specific process (if the engine is cluster wide, set the 3rd argument to {Iodine::PubSub::CLUSTER}.
51
+ #
52
+ class Engine
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,43 @@
1
+ module Iodine
2
+ # Iodine's {Iodine::Rack} module provides a Rack complient interface (connecting Iodine to Rack) for an HTTP and Websocket Server.
3
+ module Rack
4
+ # The methods in this module are designed to be Rack compatible and to provide higher performance than the original Rack methods.
5
+ #
6
+ # Iodine does NOT monkey patch Rack automatically. However, it's possible and recommended to moneky patch Rack::Utils to use the methods in this module.
7
+ #
8
+ # Choosing to monkey patch Rack::Utils could offer significant performance gains for some applications. i.e. (on my machine):
9
+ #
10
+ # require 'iodine'
11
+ # require 'rack'
12
+ # # a String in need of decoding
13
+ # s = '%E3%83%AB%E3%83%93%E3%82%A4%E3%82%B9%E3%81%A8'
14
+ # Benchmark.bm do |bm|
15
+ # # Pre-Patch
16
+ # bm.report(" Rack.unescape") {1_000_000.times { Rack::Utils.unescape s } }
17
+ # bm.report(" Rack.rfc2822") {1_000_000.times { Rack::Utils.rfc2822(Time.now) } }
18
+ # bm.report(" Rack.rfc2109") {1_000_000.times { Rack::Utils.rfc2109(Time.now) } }
19
+ # # Perform Patch
20
+ # Iodine.patch_rack
21
+ # puts " --- Monkey Patching Rack ---"
22
+ # # Post Patch
23
+ # bm.report("Patched.unescape") {1_000_000.times { Rack::Utils.unescape s } }
24
+ # bm.report(" Patched.rfc2822") {1_000_000.times { Rack::Utils.rfc2822(Time.now) } }
25
+ # bm.report(" Patched.rfc2109") {1_000_000.times { Rack::Utils.rfc2109(Time.now) } }
26
+ # end && nil
27
+ #
28
+ # Results:
29
+ #
30
+ # user system total real
31
+ # Rack.unescape 8.706881 0.019995 8.726876 ( 8.740530)
32
+ # Rack.rfc2822 3.270305 0.007519 3.277824 ( 3.279416)
33
+ # Rack.rfc2109 3.152188 0.003852 3.156040 ( 3.157975)
34
+ # --- Monkey Patching Rack ---
35
+ # Patched.unescape 0.327231 0.003125 0.330356 ( 0.337090)
36
+ # Patched.rfc2822 0.691304 0.003330 0.694634 ( 0.701172)
37
+ # Patched.rfc2109 0.685029 0.001956 0.686985 ( 0.687607)
38
+ #
39
+ # Iodine uses the same code internally for HTTP timestamping (adding missing `Date` headers) and logging.
40
+ module Utils
41
+ end
42
+ end
43
+ end
data/lib/iodine/tls.rb ADDED
@@ -0,0 +1,16 @@
1
+ module Iodine
2
+ # Iodine's {Iodine::TLS} instances hold SSL/TLS settings for secure connections.
3
+ #
4
+ # This allows both secure client connections and secure server connections to be established.
5
+ #
6
+ # tls = Iodine::TLS.new "localhost" # self-signed certificate
7
+ # Iodine.listen service: "http", handler: APP, tls: tls
8
+ #
9
+ # Iodine abstracts away the underlying SSL/TLS library to minimize the risk of misuse and insecure settings.
10
+ #
11
+ # Calling TLS methods when no SSL/TLS library is available should result in iodine crashing. This is expected behavior.
12
+ #
13
+ # At the moment, only OpenSSL is supported. BearSSL support is planned.
14
+ class TLS
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module Iodine
2
+ VERSION = '0.7.44'.freeze
3
+ end