rage-iodine 1.7.58
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- data/.github/workflows/ruby.yml +42 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.yardopts +8 -0
- data/CHANGELOG.md +1098 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/LIMITS.md +41 -0
- data/README.md +782 -0
- data/Rakefile +23 -0
- data/SPEC-PubSub-Draft.md +159 -0
- data/SPEC-WebSocket-Draft.md +239 -0
- data/bin/console +22 -0
- data/bin/info.md +353 -0
- data/bin/mustache_bench.rb +100 -0
- data/bin/poc/Gemfile.lock +23 -0
- data/bin/poc/README.md +37 -0
- data/bin/poc/config.ru +66 -0
- data/bin/poc/gemfile +1 -0
- data/bin/poc/www/index.html +57 -0
- data/examples/async_task.ru +92 -0
- data/examples/bates/README.md +3 -0
- data/examples/bates/config.ru +342 -0
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +600 -0
- data/examples/config.ru +59 -0
- data/examples/echo.ru +59 -0
- data/examples/etag.ru +16 -0
- data/examples/hello.ru +29 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/rack3.ru +12 -0
- data/examples/redis.ru +70 -0
- data/examples/shootout.ru +73 -0
- data/examples/sub-protocols.ru +90 -0
- data/examples/tcp_client.rb +66 -0
- data/examples/x-sendfile.ru +14 -0
- data/exe/iodine +280 -0
- data/ext/iodine/extconf.rb +110 -0
- data/ext/iodine/fio.c +12096 -0
- data/ext/iodine/fio.h +6390 -0
- data/ext/iodine/fio_cli.c +431 -0
- data/ext/iodine/fio_cli.h +189 -0
- data/ext/iodine/fio_json_parser.h +687 -0
- data/ext/iodine/fio_siphash.c +157 -0
- data/ext/iodine/fio_siphash.h +37 -0
- data/ext/iodine/fio_tls.h +129 -0
- data/ext/iodine/fio_tls_missing.c +649 -0
- data/ext/iodine/fio_tls_openssl.c +1056 -0
- data/ext/iodine/fio_tmpfile.h +50 -0
- data/ext/iodine/fiobj.h +44 -0
- data/ext/iodine/fiobj4fio.h +21 -0
- data/ext/iodine/fiobj_ary.c +333 -0
- data/ext/iodine/fiobj_ary.h +139 -0
- data/ext/iodine/fiobj_data.c +1185 -0
- data/ext/iodine/fiobj_data.h +167 -0
- data/ext/iodine/fiobj_hash.c +409 -0
- data/ext/iodine/fiobj_hash.h +176 -0
- data/ext/iodine/fiobj_json.c +622 -0
- data/ext/iodine/fiobj_json.h +68 -0
- data/ext/iodine/fiobj_mem.h +71 -0
- data/ext/iodine/fiobj_mustache.c +317 -0
- data/ext/iodine/fiobj_mustache.h +62 -0
- data/ext/iodine/fiobj_numbers.c +344 -0
- data/ext/iodine/fiobj_numbers.h +127 -0
- data/ext/iodine/fiobj_str.c +433 -0
- data/ext/iodine/fiobj_str.h +172 -0
- data/ext/iodine/fiobject.c +620 -0
- data/ext/iodine/fiobject.h +654 -0
- data/ext/iodine/hpack.h +1923 -0
- data/ext/iodine/http.c +2736 -0
- data/ext/iodine/http.h +1019 -0
- data/ext/iodine/http1.c +825 -0
- data/ext/iodine/http1.h +29 -0
- data/ext/iodine/http1_parser.h +1835 -0
- data/ext/iodine/http_internal.c +1279 -0
- data/ext/iodine/http_internal.h +248 -0
- data/ext/iodine/http_mime_parser.h +350 -0
- data/ext/iodine/iodine.c +1433 -0
- data/ext/iodine/iodine.h +64 -0
- data/ext/iodine/iodine_caller.c +218 -0
- data/ext/iodine/iodine_caller.h +27 -0
- data/ext/iodine/iodine_connection.c +941 -0
- data/ext/iodine/iodine_connection.h +55 -0
- data/ext/iodine/iodine_defer.c +420 -0
- data/ext/iodine/iodine_defer.h +6 -0
- data/ext/iodine/iodine_fiobj2rb.h +120 -0
- data/ext/iodine/iodine_helpers.c +282 -0
- data/ext/iodine/iodine_helpers.h +12 -0
- data/ext/iodine/iodine_http.c +1280 -0
- data/ext/iodine/iodine_http.h +23 -0
- data/ext/iodine/iodine_json.c +302 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_mustache.c +567 -0
- data/ext/iodine/iodine_mustache.h +6 -0
- data/ext/iodine/iodine_pubsub.c +580 -0
- data/ext/iodine/iodine_pubsub.h +26 -0
- data/ext/iodine/iodine_rack_io.c +273 -0
- data/ext/iodine/iodine_rack_io.h +20 -0
- data/ext/iodine/iodine_store.c +142 -0
- data/ext/iodine/iodine_store.h +20 -0
- data/ext/iodine/iodine_tcp.c +346 -0
- data/ext/iodine/iodine_tcp.h +13 -0
- data/ext/iodine/iodine_tls.c +261 -0
- data/ext/iodine/iodine_tls.h +13 -0
- data/ext/iodine/mustache_parser.h +1546 -0
- data/ext/iodine/redis_engine.c +957 -0
- data/ext/iodine/redis_engine.h +79 -0
- data/ext/iodine/resp_parser.h +317 -0
- data/ext/iodine/scheduler.c +173 -0
- data/ext/iodine/scheduler.h +6 -0
- data/ext/iodine/websocket_parser.h +506 -0
- data/ext/iodine/websockets.c +752 -0
- data/ext/iodine/websockets.h +185 -0
- data/iodine.gemspec +50 -0
- data/lib/iodine/connection.rb +61 -0
- data/lib/iodine/json.rb +42 -0
- data/lib/iodine/mustache.rb +113 -0
- data/lib/iodine/pubsub.rb +55 -0
- data/lib/iodine/rack_utils.rb +43 -0
- data/lib/iodine/tls.rb +16 -0
- data/lib/iodine/version.rb +3 -0
- data/lib/iodine.rb +274 -0
- data/lib/rack/handler/iodine.rb +33 -0
- data/logo.png +0 -0
- metadata +284 -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
|
data/iodine.gemspec
ADDED
@@ -0,0 +1,50 @@
|
|
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 = 'rage-iodine'
|
8
|
+
spec.version = Iodine::VERSION
|
9
|
+
spec.authors = ['Boaz Segev']
|
10
|
+
spec.email = ['bo@plezi.io']
|
11
|
+
|
12
|
+
spec.summary = 'iodine - a fast HTTP / Websocket Server with Pub/Sub support, optimized for Ruby MRI on Linux / BSD / Windows'
|
13
|
+
spec.description = 'A fast HTTP / Websocket Server with built-in Pub/Sub support (with or without Redis), static file support and many other features, optimized for Ruby MRI on Linux / BSD / macOS / Windows'
|
14
|
+
spec.homepage = 'https://github.com/rage-rb/iodine'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
18
|
+
# delete this section to allow pushing this gem to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
21
|
+
else
|
22
|
+
raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
|
23
|
+
end
|
24
|
+
|
25
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
26
|
+
spec.bindir = 'exe'
|
27
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
|
+
spec.require_paths = %w(lib ext)
|
29
|
+
|
30
|
+
spec.extensions = %w(ext/iodine/extconf.rb)
|
31
|
+
|
32
|
+
spec.required_ruby_version = '>= 2.2.2' # Because earlier versions had been discontinued
|
33
|
+
|
34
|
+
spec.requirements << 'A Unix based system: Linux / macOS / BSD.'
|
35
|
+
spec.requirements << 'An updated C compiler.'
|
36
|
+
spec.requirements << 'Ruby >= 2.3.8 (Ruby EOL).'
|
37
|
+
spec.requirements << 'Ruby >= 2.5.0 recommended.'
|
38
|
+
spec.requirements << 'TLS requires OpenSSL >= 1.1.0.'
|
39
|
+
spec.requirements << 'Or Windows with Ruby >= 3.0.0 build with MingW and MingW as compiler.'
|
40
|
+
|
41
|
+
# spec.add_development_dependency 'bundler', '>= 1.10', '< 2.0'
|
42
|
+
spec.add_development_dependency 'rake', '>= 12.0', '< 14.0'
|
43
|
+
spec.add_development_dependency 'minitest', '>=5', '< 6.0'
|
44
|
+
spec.add_development_dependency 'rspec', '>=3.9.0', '< 4.0'
|
45
|
+
spec.add_development_dependency 'spec', '>=5.3.0', '< 6.0'
|
46
|
+
spec.add_development_dependency 'rake-compiler', '>= 1', '< 2.0'
|
47
|
+
|
48
|
+
spec.post_install_message = "Thank you for installing Iodine #{Iodine::VERSION}.\n" +
|
49
|
+
"Remember: if iodine supports your business, it's only fair to give value back (code contributions / donations)."
|
50
|
+
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
|
data/lib/iodine/json.rb
ADDED
@@ -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
|