iodine 0.3.6 → 0.4.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.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +46 -0
- data/LIMITS.md +25 -0
- data/README.md +39 -80
- data/SPEC-Websocket-Draft.md +129 -4
- data/bin/echo +2 -2
- data/bin/http-hello +1 -0
- data/bin/updated api +113 -0
- data/bin/ws-echo +0 -1
- data/examples/broadcast.ru +56 -0
- data/examples/echo.ru +57 -0
- data/examples/hello.ru +30 -0
- data/examples/redis.ru +69 -0
- data/examples/shootout.ru +53 -0
- data/exe/iodine +2 -80
- data/ext/iodine/defer.c +11 -5
- data/ext/iodine/empty.h +26 -0
- data/ext/iodine/evio.h +1 -1
- data/ext/iodine/facil.c +103 -61
- data/ext/iodine/facil.h +20 -12
- data/ext/iodine/fio_dict.c +446 -0
- data/ext/iodine/fio_dict.h +90 -0
- data/ext/iodine/fio_hash_table.h +370 -0
- data/ext/iodine/fio_list.h +30 -3
- data/ext/iodine/http.c +169 -37
- data/ext/iodine/http.h +33 -10
- data/ext/iodine/http1.c +78 -42
- data/ext/iodine/http_request.c +6 -0
- data/ext/iodine/http_request.h +3 -0
- data/ext/iodine/http_response.c +43 -11
- data/ext/iodine/iodine.c +380 -0
- data/ext/iodine/iodine.h +62 -0
- data/ext/iodine/iodine_helpers.c +235 -0
- data/ext/iodine/iodine_helpers.h +13 -0
- data/ext/iodine/iodine_http.c +409 -241
- data/ext/iodine/iodine_http.h +7 -14
- data/ext/iodine/iodine_protocol.c +626 -0
- data/ext/iodine/iodine_protocol.h +13 -0
- data/ext/iodine/iodine_pubsub.c +646 -0
- data/ext/iodine/iodine_pubsub.h +27 -0
- data/ext/iodine/iodine_websockets.c +796 -0
- data/ext/iodine/iodine_websockets.h +19 -0
- data/ext/iodine/pubsub.c +544 -0
- data/ext/iodine/pubsub.h +215 -0
- data/ext/iodine/random.c +4 -4
- data/ext/iodine/rb-call.c +1 -5
- data/ext/iodine/rb-defer.c +3 -20
- data/ext/iodine/rb-rack-io.c +22 -22
- data/ext/iodine/rb-rack-io.h +3 -4
- data/ext/iodine/rb-registry.c +111 -118
- data/ext/iodine/redis_connection.c +277 -0
- data/ext/iodine/redis_connection.h +77 -0
- data/ext/iodine/redis_engine.c +398 -0
- data/ext/iodine/redis_engine.h +68 -0
- data/ext/iodine/resp.c +842 -0
- data/ext/iodine/resp.h +253 -0
- data/ext/iodine/sock.c +26 -12
- data/ext/iodine/sock.h +14 -3
- data/ext/iodine/spnlock.inc +19 -2
- data/ext/iodine/websockets.c +299 -11
- data/ext/iodine/websockets.h +159 -6
- data/lib/iodine.rb +104 -1
- data/lib/iodine/cli.rb +106 -0
- data/lib/iodine/monkeypatch.rb +40 -0
- data/lib/iodine/pubsub.rb +70 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/iodine/websocket.rb +12 -0
- data/lib/rack/handler/iodine.rb +33 -7
- metadata +35 -7
- data/ext/iodine/iodine_core.c +0 -760
- data/ext/iodine/iodine_core.h +0 -79
- data/ext/iodine/iodine_websocket.c +0 -551
- data/ext/iodine/iodine_websocket.h +0 -22
- data/lib/iodine/http.rb +0 -4
data/ext/iodine/websockets.h
CHANGED
@@ -9,6 +9,10 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
9
9
|
|
10
10
|
#include "http.h"
|
11
11
|
|
12
|
+
/* *****************************************************************************
|
13
|
+
Upgrading from HTTP to Websockets
|
14
|
+
***************************************************************************** */
|
15
|
+
|
12
16
|
/**
|
13
17
|
The Websocket type is an opaque type used by the websocket API to provide
|
14
18
|
identify a specific Websocket connection and manage it's internal state.
|
@@ -109,8 +113,20 @@ ssize_t websocket_upgrade(websocket_settings_s settings);
|
|
109
113
|
#define websocket_upgrade(...) \
|
110
114
|
websocket_upgrade((websocket_settings_s){__VA_ARGS__})
|
111
115
|
|
116
|
+
/* *****************************************************************************
|
117
|
+
Websocket information
|
118
|
+
***************************************************************************** */
|
119
|
+
|
112
120
|
/** Returns the opaque user data associated with the websocket. */
|
113
121
|
void *websocket_udata(ws_s *ws);
|
122
|
+
|
123
|
+
/**
|
124
|
+
Sets the opaque user data associated with the websocket.
|
125
|
+
|
126
|
+
Returns the old value, if any.
|
127
|
+
*/
|
128
|
+
void *websocket_udata_set(ws_s *ws, void *udata);
|
129
|
+
|
114
130
|
/**
|
115
131
|
Returns the underlying socket UUID.
|
116
132
|
|
@@ -118,20 +134,157 @@ This is only relevant for collecting the protocol object from outside of
|
|
118
134
|
websocket events, as the socket shouldn't be written to.
|
119
135
|
*/
|
120
136
|
intptr_t websocket_uuid(ws_s *ws);
|
121
|
-
/**
|
122
|
-
Sets the opaque user data associated with the websocket.
|
123
137
|
|
124
|
-
|
138
|
+
/**
|
139
|
+
Counts the number of websocket connections.
|
125
140
|
*/
|
126
|
-
|
141
|
+
size_t websocket_count(void);
|
142
|
+
|
143
|
+
/* *****************************************************************************
|
144
|
+
Websocket Connection Management (write / close)
|
145
|
+
***************************************************************************** */
|
146
|
+
|
127
147
|
/** Writes data to the websocket. Returns -1 on failure (0 on success). */
|
128
148
|
int websocket_write(ws_s *ws, void *data, size_t size, uint8_t is_text);
|
129
149
|
/** Closes a websocket connection. */
|
130
150
|
void websocket_close(ws_s *ws);
|
151
|
+
|
152
|
+
/* *****************************************************************************
|
153
|
+
Websocket Pub/Sub
|
154
|
+
=================
|
155
|
+
|
156
|
+
API for websocket pub/sub that can be used to publish messages across process
|
157
|
+
boundries.
|
158
|
+
|
159
|
+
Supports pub/sub engines (see {pubsub.h}) that can connect to a backend service
|
160
|
+
such as Redis.
|
161
|
+
|
162
|
+
The default pub/sub engine (if `NULL` or unspecified) will publish the messages
|
163
|
+
to the process cluster (all the processes in `facil_run`).
|
164
|
+
|
165
|
+
To publish to a channel, use the API provided in {pubsub.h}.
|
166
|
+
***************************************************************************** */
|
167
|
+
|
168
|
+
/** Pub/sub engine type. Engine documentation is in `pubsub.h` */
|
169
|
+
typedef struct pubsub_engine_s pubsub_engine_s;
|
170
|
+
|
171
|
+
/** Incoming pub/sub messages will be passed along using this data structure. */
|
172
|
+
typedef struct {
|
173
|
+
/** the websocket receiving the message. */
|
174
|
+
ws_s *ws;
|
175
|
+
/** the pub/sub engine from which where the message originated. */
|
176
|
+
struct pubsub_engine_s *engine;
|
177
|
+
/** the Websocket pub/sub subscription ID. */
|
178
|
+
uintptr_t subscription_id;
|
179
|
+
/** the channel where the message was published. */
|
180
|
+
struct {
|
181
|
+
const char *name;
|
182
|
+
size_t len;
|
183
|
+
} channel;
|
184
|
+
/** the published message. */
|
185
|
+
struct {
|
186
|
+
const char *data;
|
187
|
+
size_t len;
|
188
|
+
} msg;
|
189
|
+
/** Pattern matching was used for channel subscription. */
|
190
|
+
unsigned use_pattern : 1;
|
191
|
+
/** user opaque data. */
|
192
|
+
void *udata;
|
193
|
+
} websocket_pubsub_notification_s;
|
194
|
+
|
195
|
+
/** Possible arguments for the {websocket_subscribe} function. */
|
196
|
+
struct websocket_subscribe_s {
|
197
|
+
/** the websocket receiving the message. REQUIRED. */
|
198
|
+
ws_s *ws;
|
199
|
+
/**
|
200
|
+
* The pub/sub engine to use.
|
201
|
+
*
|
202
|
+
* Default: engine will publish messages throughout the facil process cluster.
|
203
|
+
*/
|
204
|
+
struct pubsub_engine_s *engine;
|
205
|
+
/** the channel where the message was published. */
|
206
|
+
struct {
|
207
|
+
const char *name;
|
208
|
+
size_t len;
|
209
|
+
} channel;
|
210
|
+
/**
|
211
|
+
* The callback that handles pub/sub notifications.
|
212
|
+
*
|
213
|
+
* Default: send directly to websocket client.
|
214
|
+
*/
|
215
|
+
void (*on_message)(websocket_pubsub_notification_s notification);
|
216
|
+
/**
|
217
|
+
* An optional cleanup callback for the `udata`.
|
218
|
+
*/
|
219
|
+
void (*on_unsubscribe)(void *udata);
|
220
|
+
/** User opaque data, passed along to the notification. */
|
221
|
+
void *udata;
|
222
|
+
/** Use pattern matching for channel subscription. */
|
223
|
+
unsigned use_pattern : 1;
|
224
|
+
/**
|
225
|
+
* When using client forwarding (no `on_message` callback), this indicates if
|
226
|
+
* messages should be sent to the client as binary blobs, which is the safest
|
227
|
+
* approach.
|
228
|
+
*
|
229
|
+
* Default: tests for UTF-8 data encoding and sends as text if valid UTF-8.
|
230
|
+
* Messages above ~32Kb are always assumed to be binary.
|
231
|
+
*/
|
232
|
+
unsigned force_binary : 1;
|
233
|
+
/**
|
234
|
+
* When using client forwarding (no `on_message` callback), this indicates if
|
235
|
+
* messages should be sent to the client as text.
|
236
|
+
*
|
237
|
+
* `force_binary` has precedence.
|
238
|
+
*
|
239
|
+
* Default: see above.
|
240
|
+
*
|
241
|
+
*/
|
242
|
+
unsigned force_text : 1;
|
243
|
+
};
|
244
|
+
|
131
245
|
/**
|
132
|
-
|
246
|
+
* Subscribes to a channel. See {struct websocket_subscribe_s} for possible
|
247
|
+
* arguments.
|
248
|
+
*
|
249
|
+
* Returns a subscription ID on success and 0 on failure.
|
250
|
+
*
|
251
|
+
* All subscriptions are automatically revoked once the websocket is closed.
|
252
|
+
*
|
253
|
+
* If the connections subscribes to the same channel more than once, messages
|
254
|
+
* will be merged. However, another subscription ID will be assigned, since two
|
255
|
+
* calls to {websocket_unsubscribe} will be required in order to unregister from
|
256
|
+
* the channel.
|
257
|
+
*/
|
258
|
+
uintptr_t websocket_subscribe(struct websocket_subscribe_s args);
|
259
|
+
|
260
|
+
#define websocket_subscribe(wbsckt, ...) \
|
261
|
+
websocket_subscribe((struct websocket_subscribe_s){.ws = wbsckt, __VA_ARGS__})
|
262
|
+
|
263
|
+
/**
|
264
|
+
* Finds an existing subscription (in case the subscription ID wasn't saved).
|
265
|
+
* See {struct websocket_subscribe_s} for possible arguments.
|
266
|
+
*
|
267
|
+
* Returns the existing subscription's ID (if exists) or 0 (no subscription).
|
268
|
+
*/
|
269
|
+
uintptr_t websocket_find_sub(struct websocket_subscribe_s args);
|
270
|
+
|
271
|
+
#define websocket_find_sub(wbsckt, ...) \
|
272
|
+
websocket_find_sub((struct websocket_subscribe_s){.ws = wbsckt, __VA_ARGS__})
|
273
|
+
|
274
|
+
/**
|
275
|
+
* Unsubscribes from a channel.
|
276
|
+
*
|
277
|
+
* Failures are silent.
|
278
|
+
*
|
279
|
+
* All subscriptions are automatically revoked once the websocket is closed. So
|
280
|
+
* only use this function to unsubscribe while the websocket is open.
|
281
|
+
*/
|
282
|
+
void websocket_unsubscribe(ws_s *ws, uintptr_t subscription_id);
|
283
|
+
|
284
|
+
/* *****************************************************************************
|
285
|
+
Websocket Tasks - within a single process scope, NOT and entire cluster
|
286
|
+
*****************************************************************************
|
133
287
|
*/
|
134
|
-
size_t websocket_count(void);
|
135
288
|
|
136
289
|
/** The named arguments for `websocket_each` */
|
137
290
|
struct websocket_each_args_s {
|
data/lib/iodine.rb
CHANGED
@@ -26,6 +26,92 @@ require 'iodine/iodine'
|
|
26
26
|
#
|
27
27
|
#
|
28
28
|
# Please read the {file:README.md} file for an introduction to Iodine and an overview of it's API.
|
29
|
+
#
|
30
|
+
# == The API
|
31
|
+
#
|
32
|
+
# The main API methods for the top {Iodine} namesapce are grouped here by subject.
|
33
|
+
#
|
34
|
+
# === Event Loop / Concurrency
|
35
|
+
#
|
36
|
+
# Iodine manages an internal event-loop and reactor pattern. The following API
|
37
|
+
# manages Iodine's behavior.
|
38
|
+
#
|
39
|
+
# * {Iodine.threads}, {Iodine.threads=} gets or sets the amount of threads iodine will use in it's working thread pool.
|
40
|
+
# * {Iodine.processes}, {Iodine.processes} gets or sets the amount of processes iodine will utilize (`fork`) to handle connections.
|
41
|
+
# * {Iodine.start} starts iodine's event loop and reactor pattern. At this point, it's impossible to change the number of threads or processes used.
|
42
|
+
#
|
43
|
+
# === Event and Task Scheduling
|
44
|
+
#
|
45
|
+
# * {Iodine.run} schedules a block of code to run asynchronously.
|
46
|
+
# * {Iodine.run_after}, {Iodine.run_every} schedules a block of code to run (asynchronously) using a timer.
|
47
|
+
# * {Iodine.start} starts iodine's event loop and reactor pattern. At this point, it's impossible to change the number of threads or processes used.
|
48
|
+
#
|
49
|
+
# In addition to the top level API, there's also the connection class and connection instance API, as specified in the {Iodine::Protocol} and {Iodine::Websocket} documentation, which allows for a connection bound task(s) to be scheduled to run within the connection's lock (for example, {Iodine::Websocket#defer} and {Iodine::Websocket#each}).
|
50
|
+
#
|
51
|
+
# === Connection Handling
|
52
|
+
#
|
53
|
+
# Iodine handles connections using {Iodine::Protocol} objects. The following API
|
54
|
+
# manages either built-in or custom {Protocol} objects (classes / instances) in relation to their network sockets.
|
55
|
+
#
|
56
|
+
# * {Iodine.attach_fd}, {Iodine.attach_io} allows Iodine to take controll of an IO object (i.e., a TCP/IP Socket, a Unix Socket or a pipe).
|
57
|
+
# * {Iodine.connect} creates a new TCP/IP connection using the specified Protocol.
|
58
|
+
# * {Iodine.listen} listens to new TCP/IP connections using the specified Protocol.
|
59
|
+
# * {Iodine.listen2http} listens to new TCP/IP connections using the buildin HTTP / Websocket Protocol.
|
60
|
+
# * {Iodine.warmup} warms up and HTTP Rack applications.
|
61
|
+
# * {Iodine.count} counts the number of connections (including HTTP / Websocket connections).
|
62
|
+
# * {Iodine::Protocol.each} runs a code of block for every connection sharing the process (except HTTP / Websocket connections).
|
63
|
+
# * {Iodine::Websocket.each} runs a code of block for every existing websocket sharing the process.
|
64
|
+
#
|
65
|
+
# In addition to the top level API, there's also the connection class and connection instance API, as specified in the {Iodine::Protocol} and {Iodine::Websocket} documentation.
|
66
|
+
#
|
67
|
+
# === Pub/Sub
|
68
|
+
#
|
69
|
+
# Iodine offers a native Pub/Sub engine (no database required) that can be easily extended by implementing a Pub/Sub {Iodine::PubSub::Engine}.
|
70
|
+
#
|
71
|
+
# The following methods offect server side Pub/Sub that allows the server code to react to channel event.
|
72
|
+
#
|
73
|
+
# * {Iodine.subscribe}, {Iodine.unsubscribe} manages a process's subscription to a channel (which is different than a connection's subscription, such as employed by {Iodine::Websocket}).
|
74
|
+
# * {Iodine.publish} publishes a message to a Pub/Sub channel. The message will be sent to all subscribers - connections, other processes in the cluster and even other machines (when using the {Iodine::PubSub::RedisEngine}).
|
75
|
+
# * {Iodine.default_pubsub=}, {Iodine.default_pubsub} sets or gets the default Pub/Sub {Iodine::PubSub::Engine}. i.e., when set to a new {Iodine::PubSub::RedisEngine} instance, all Pub/Sub method calls will use the Redis engine (unless explicitly requiring a different engine).
|
76
|
+
#
|
77
|
+
# {Iodine::Websocket} objects have a seperate Pub/Sub implementation that manages the subscription's lifetime to match the connection's lifetime and allows direct client Pub/Sub (forwards the message to the client directly).
|
78
|
+
#
|
79
|
+
# == Patching Rack
|
80
|
+
#
|
81
|
+
# Although Iodine offers Rack::Utils optimizations using monkey patching, Iodine does NOT monkey patch Rack automatically.
|
82
|
+
#
|
83
|
+
# Choosing to monkey patch Rack::Utils could offer significant performance gains for some applications. i.e. (on my machine):
|
84
|
+
#
|
85
|
+
# require 'iodine'
|
86
|
+
# require 'rack'
|
87
|
+
# # a String in need of decoding
|
88
|
+
# s = '%E3%83%AB%E3%83%93%E3%82%A4%E3%82%B9%E3%81%A8'
|
89
|
+
# Benchmark.bm do |bm|
|
90
|
+
# # Pre-Patch
|
91
|
+
# bm.report(" Rack.unescape") {1_000_000.times { Rack::Utils.unescape s } }
|
92
|
+
# bm.report(" Rack.rfc2822") {1_000_000.times { Rack::Utils.rfc2822(Time.now) } }
|
93
|
+
# bm.report(" Rack.rfc2109") {1_000_000.times { Rack::Utils.rfc2109(Time.now) } }
|
94
|
+
# # Perform Patch
|
95
|
+
# Iodine.patch_rack
|
96
|
+
# puts " --- Monkey Patching Rack ---"
|
97
|
+
# # Post Patch
|
98
|
+
# bm.report("Patched.unescape") {1_000_000.times { Rack::Utils.unescape s } }
|
99
|
+
# bm.report(" Patched.rfc2822") {1_000_000.times { Rack::Utils.rfc2822(Time.now) } }
|
100
|
+
# bm.report(" Patched.rfc2109") {1_000_000.times { Rack::Utils.rfc2109(Time.now) } }
|
101
|
+
# end && nil
|
102
|
+
#
|
103
|
+
# Results:
|
104
|
+
# user system total real
|
105
|
+
# Rack.unescape 8.660000 0.010000 8.670000 ( 8.687807)
|
106
|
+
# Rack.rfc2822 3.730000 0.000000 3.730000 ( 3.727732)
|
107
|
+
# Rack.rfc2109 3.020000 0.010000 3.030000 ( 3.031940)
|
108
|
+
# --- Monkey Patching Rack ---
|
109
|
+
# Patched.unescape 0.340000 0.000000 0.340000 ( 0.341506)
|
110
|
+
# Patched.rfc2822 0.740000 0.000000 0.740000 ( 0.737796)
|
111
|
+
# Patched.rfc2109 0.690000 0.010000 0.700000 ( 0.700155)
|
112
|
+
#
|
113
|
+
# At the moment, the extent of the monkey patching offered is very limited.
|
114
|
+
# As new optimizations are added, the policy regarding monkey patching (benifit vs. risks) might be re-evaluated.
|
29
115
|
module Iodine
|
30
116
|
@threads = (ARGV.index('-t') && ARGV[ARGV.index('-t') + 1]) || ENV['MAX_THREADS']
|
31
117
|
@processes = (ARGV.index('-w') && ARGV[ARGV.index('-w') + 1]) || ENV['MAX_WORKERS']
|
@@ -64,7 +150,7 @@ module Iodine
|
|
64
150
|
# file to load now instead of waiting for "first access". This allows multi-threaded safety and better memory utilization during forking.
|
65
151
|
#
|
66
152
|
# Use {warmup} when either {processes} or {threads} are set to more then 1.
|
67
|
-
def self.warmup
|
153
|
+
def self.warmup app
|
68
154
|
# load anything marked with `autoload`, since autoload isn't thread safe nor fork friendly.
|
69
155
|
Module.constants.each do |n|
|
70
156
|
begin
|
@@ -72,7 +158,24 @@ module Iodine
|
|
72
158
|
rescue Exception => _e
|
73
159
|
end
|
74
160
|
end
|
161
|
+
::Rack::Builder.new(app) do |r|
|
162
|
+
r.warmup do |a|
|
163
|
+
client = ::Rack::MockRequest.new(a)
|
164
|
+
client.get('/')
|
165
|
+
end
|
166
|
+
end
|
75
167
|
end
|
168
|
+
|
169
|
+
def self.patch_rack
|
170
|
+
::Rack::Utils.class_eval do
|
171
|
+
Iodine::Base::MonkeyPatch::RackUtils.methods(false).each do |m|
|
172
|
+
::Rack::Utils.define_singleton_method(m,
|
173
|
+
Iodine::Base::MonkeyPatch::RackUtils.instance_method(m) )
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
self.default_pubsub = ::Iodine::PubSub::CLUSTER
|
76
179
|
end
|
77
180
|
|
78
181
|
require 'rack/handler/iodine' unless defined? ::Iodine::Rack::IODINE_RACK_LOADED
|
data/lib/iodine/cli.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'iodine'
|
2
|
+
require 'rack'
|
3
|
+
|
4
|
+
module Iodine
|
5
|
+
module Base
|
6
|
+
# Command line interface
|
7
|
+
module CLI
|
8
|
+
|
9
|
+
def print_help
|
10
|
+
puts <<-EOS
|
11
|
+
|
12
|
+
Iodine's HTTP/Websocket server version #{Iodine::VERSION}
|
13
|
+
|
14
|
+
Use:
|
15
|
+
|
16
|
+
iodine <options> <filename>
|
17
|
+
|
18
|
+
Both <options> and <filename> are optional.
|
19
|
+
|
20
|
+
Available options:
|
21
|
+
-p Port number. Default: 3000.
|
22
|
+
-t Number of threads. Default: CPU core count.
|
23
|
+
-w Number of worker processes. Default: CPU core count.
|
24
|
+
-www Public folder for static file serving. Default: nil (none).
|
25
|
+
-v Log responses. Default: never log responses.
|
26
|
+
-warmup Warmup invokes autoloading (lazy loading) during server startup.
|
27
|
+
-tout HTTP inactivity connection timeout. Default: 5 seconds.
|
28
|
+
-maxbd Maximum Mb per HTTP message (max body size). Default: 50Mib.
|
29
|
+
-maxms Maximum Bytes per Websocket message. Default: 250Kib.
|
30
|
+
-ping Websocket ping interval in seconds. Default: 40 seconds.
|
31
|
+
<filename> Defaults to: config.ru
|
32
|
+
|
33
|
+
Example:
|
34
|
+
|
35
|
+
iodine -p 80
|
36
|
+
|
37
|
+
iodine -p 8080 path/to/app/conf.ru
|
38
|
+
|
39
|
+
iodine -p 8080 -w 4 -t 16
|
40
|
+
|
41
|
+
EOS
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def try_file filename
|
46
|
+
return nil unless File.exist? filename
|
47
|
+
return ::Rack::Builder.parse_file filename
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
def call
|
53
|
+
if ARGV[0] =~ /(\-\?)|(help)|(\?)|(h)|(\-h)$/
|
54
|
+
return print_help
|
55
|
+
end
|
56
|
+
|
57
|
+
app, opt = nil, nil
|
58
|
+
filename = ((ARGV[-2].to_s[0] != '-' || ARGV[-2].to_s == '-warmup') && ARGV[-1].to_s[0] != '-' && ARGV[-1])
|
59
|
+
if filename
|
60
|
+
app, opt = try_file filename;
|
61
|
+
unless opt
|
62
|
+
puts "* Couldn't find #{filename}\n testing for config.ru\n"
|
63
|
+
app, opt = try_file "config.ru"
|
64
|
+
end
|
65
|
+
else
|
66
|
+
app, opt = try_file "config.ru";
|
67
|
+
end
|
68
|
+
|
69
|
+
unless opt
|
70
|
+
puts "WARNING: Ruby application not found#{ filename ? " - missing both #{filename} and config.ru" : " - missing config.ru"}."
|
71
|
+
if ARGV.index('-www') && ARGV[ARGV.index('-www') + 1]
|
72
|
+
puts " Running only static file service."
|
73
|
+
opt = ::Rack::Server::Options.new.parse!([])
|
74
|
+
else
|
75
|
+
puts "For help run:"
|
76
|
+
puts " iodine -?"
|
77
|
+
return
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
if ARGV.index('-maxbd') && ARGV[ARGV.index('-maxbd') + 1]
|
82
|
+
Iodine::Rack.max_body_size = ARGV[ARGV.index('-maxbd') + 1].to_i
|
83
|
+
end
|
84
|
+
if ARGV.index('-maxms') && ARGV[ARGV.index('-maxms') + 1]
|
85
|
+
Iodine::Rack.max_msg_size = ARGV[ARGV.index('-maxms') + 1].to_i
|
86
|
+
end
|
87
|
+
if ARGV.index('-ping') && ARGV[ARGV.index('-ping') + 1]
|
88
|
+
Iodine::Rack.ws_timeout = ARGV[ARGV.index('-ping') + 1].to_i
|
89
|
+
end
|
90
|
+
if ARGV.index('-www') && ARGV[ARGV.index('-www') + 1]
|
91
|
+
Iodine::Rack.public = ARGV[ARGV.index('-www') + 1]
|
92
|
+
end
|
93
|
+
if ARGV.index('-tout') && ARGV[ARGV.index('-tout') + 1]
|
94
|
+
Iodine::Rack.timeout = ARGV[ARGV.index('-tout') + 1].to_i
|
95
|
+
puts "WARNNING: Iodine::Rack.timeout set to 0 (ignored, timeout will be ~5 seconds)."
|
96
|
+
end
|
97
|
+
Iodine::Rack.log = true if ARGV.index('-v')
|
98
|
+
Iodine::Rack.log = false if ARGV.index('-q')
|
99
|
+
Iodine.warmup(app) if ARGV.index('-warmup')
|
100
|
+
Iodine::Rack.run(app, opt)
|
101
|
+
end
|
102
|
+
|
103
|
+
extend self
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Iodine
|
2
|
+
module Base
|
3
|
+
# Iodine does NOT monkey patch automatically at this time.
|
4
|
+
#
|
5
|
+
# This may change in future releases, but that's unlikely.
|
6
|
+
module MonkeyPatch
|
7
|
+
# Iodine does NOT monkey patch Rack automatically. However, it's possible to
|
8
|
+
# moneky patch Rack::Utils using this module.
|
9
|
+
#
|
10
|
+
# Choosing to monkey patch Rack::Utils could offer significant performance gains for some applications. i.e. (on my machine):
|
11
|
+
#
|
12
|
+
# require 'iodine'
|
13
|
+
# require 'rack'
|
14
|
+
# s = '%E3%83%AB%E3%83%93%E3%82%A4%E3%82%B9%E3%81%A8'
|
15
|
+
# Benchmark.bm do |bm|
|
16
|
+
# # Pre-Patch
|
17
|
+
# bm.report("Rack") {1_000_000.times { Rack::Utils.unescape s } }
|
18
|
+
#
|
19
|
+
# # Perform Patch
|
20
|
+
# Rack::Utils.class_eval do
|
21
|
+
# Iodine::Base::MonkeyPatch::RackUtils.methods(false).each do |m|
|
22
|
+
# define_singleton_method(m,
|
23
|
+
# Iodine::Base::MonkeyPatch::RackUtils.instance_method(m) )
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# # Post Patch
|
28
|
+
# bm.report("Patched") {1_000_000.times { Rack::Utils.unescape s } }
|
29
|
+
# end && nil
|
30
|
+
#
|
31
|
+
# Results:
|
32
|
+
# user system total real
|
33
|
+
# Rack 8.620000 0.020000 8.640000 ( 8.636676)
|
34
|
+
# Patched 0.320000 0.000000 0.320000 ( 0.322377)
|
35
|
+
module RackUtils
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
\
|