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.
- checksums.yaml +4 -4
- data/.clang-format +15 -0
- data/.rspec +1 -0
- data/.yardopts +1 -3
- data/CHANGELOG.md +132 -12
- data/Gemfile +5 -7
- data/LICENSE.txt +1 -1
- data/README.md +147 -671
- data/Rakefile +9 -16
- data/examples/chatroom.nru +9 -0
- data/examples/client +74 -0
- data/examples/config-hello.nru +9 -0
- data/examples/config-hello.ru +11 -0
- data/examples/config-hijack.ru +13 -0
- data/examples/config-shootout.ru +43 -0
- data/examples/config.nru +120 -0
- data/examples/config.rb +37 -0
- data/examples/config.ru +42 -53
- data/examples/fake +24 -0
- data/examples/hello_neorack +137 -0
- data/examples/routes.nru +29 -0
- data/examples/routes.ru +42 -0
- data/exe/iodine +218 -258
- data/ext/iodine/extconf.rb +27 -100
- data/ext/iodine/fio-stl.h +128336 -0
- data/ext/iodine/fio-stl.md +26396 -0
- data/ext/iodine/iodine.c +213 -1393
- data/ext/iodine/iodine.h +250 -55
- data/ext/iodine/iodine_arg_helper.h +329 -0
- data/ext/iodine/iodine_caller.h +234 -25
- data/ext/iodine/iodine_cli.h +434 -0
- data/ext/iodine/iodine_compression.h +312 -0
- data/ext/iodine/iodine_connection.h +3484 -34
- data/ext/iodine/iodine_core.h +363 -0
- data/ext/iodine/iodine_crypto.h +1441 -0
- data/ext/iodine/iodine_defer.h +225 -4
- data/ext/iodine/iodine_json.h +738 -4
- data/ext/iodine/iodine_listener.h +263 -0
- data/ext/iodine/iodine_log.h +297 -0
- data/ext/iodine/iodine_minimap.h +543 -0
- data/ext/iodine/iodine_mustache.h +461 -4
- data/ext/iodine/iodine_pubsub_eng.h +729 -0
- data/ext/iodine/iodine_pubsub_history.h +547 -0
- data/ext/iodine/iodine_pubsub_msg.h +228 -0
- data/ext/iodine/iodine_redis.h +322 -0
- data/ext/iodine/iodine_store.h +418 -16
- data/ext/iodine/iodine_threads.h +279 -0
- data/ext/iodine/iodine_tls.h +515 -6
- data/ext/iodine/iodine_utils.h +923 -0
- data/iodine.gemspec +37 -42
- data/lib/iodine/benchmark.rb +1690 -0
- data/lib/iodine/documentation.rb +3085 -0
- data/lib/iodine/version.rb +17 -1
- data/lib/iodine.rb +54 -255
- data/logo.svg +36 -0
- data/mustache.md +55 -0
- data/sig/iodine.rbs +4 -0
- metadata +58 -184
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- data/.github/workflows/ruby.yml +0 -42
- data/.gitignore +0 -20
- data/LIMITS.md +0 -41
- data/SPEC-PubSub-Draft.md +0 -159
- data/SPEC-WebSocket-Draft.md +0 -239
- data/bin/console +0 -22
- data/bin/info.md +0 -353
- data/bin/mustache_bench.rb +0 -100
- data/bin/poc/Gemfile.lock +0 -23
- data/bin/poc/README.md +0 -37
- data/bin/poc/config.ru +0 -66
- data/bin/poc/gemfile +0 -1
- data/bin/poc/www/index.html +0 -57
- data/examples/async_task.ru +0 -92
- data/examples/bates/README.md +0 -3
- data/examples/bates/config.ru +0 -342
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +0 -600
- data/examples/echo.ru +0 -59
- data/examples/etag.ru +0 -16
- data/examples/hello.ru +0 -29
- data/examples/pubsub_engine.ru +0 -81
- data/examples/rack3.ru +0 -12
- data/examples/redis.ru +0 -70
- data/examples/shootout.ru +0 -73
- data/examples/sub-protocols.ru +0 -90
- data/examples/tcp_client.rb +0 -66
- data/examples/x-sendfile.ru +0 -14
- data/ext/iodine/fio.c +0 -11995
- data/ext/iodine/fio.h +0 -6371
- data/ext/iodine/fio_cli.c +0 -431
- data/ext/iodine/fio_cli.h +0 -189
- data/ext/iodine/fio_json_parser.h +0 -688
- data/ext/iodine/fio_siphash.c +0 -157
- data/ext/iodine/fio_siphash.h +0 -37
- data/ext/iodine/fio_tls.h +0 -129
- data/ext/iodine/fio_tls_missing.c +0 -649
- data/ext/iodine/fio_tls_openssl.c +0 -1106
- data/ext/iodine/fio_tmpfile.h +0 -50
- data/ext/iodine/fiobj.h +0 -44
- data/ext/iodine/fiobj4fio.h +0 -21
- data/ext/iodine/fiobj_ary.c +0 -333
- data/ext/iodine/fiobj_ary.h +0 -139
- data/ext/iodine/fiobj_data.c +0 -1187
- data/ext/iodine/fiobj_data.h +0 -167
- data/ext/iodine/fiobj_hash.c +0 -409
- data/ext/iodine/fiobj_hash.h +0 -176
- data/ext/iodine/fiobj_json.c +0 -622
- data/ext/iodine/fiobj_json.h +0 -68
- data/ext/iodine/fiobj_mem.h +0 -71
- data/ext/iodine/fiobj_mustache.c +0 -317
- data/ext/iodine/fiobj_mustache.h +0 -62
- data/ext/iodine/fiobj_numbers.c +0 -344
- data/ext/iodine/fiobj_numbers.h +0 -127
- data/ext/iodine/fiobj_str.c +0 -433
- data/ext/iodine/fiobj_str.h +0 -172
- data/ext/iodine/fiobject.c +0 -620
- data/ext/iodine/fiobject.h +0 -655
- data/ext/iodine/hpack.h +0 -1923
- data/ext/iodine/http.c +0 -2743
- data/ext/iodine/http.h +0 -1002
- data/ext/iodine/http1.c +0 -910
- data/ext/iodine/http1.h +0 -29
- data/ext/iodine/http1_parser.h +0 -878
- data/ext/iodine/http_internal.c +0 -1279
- data/ext/iodine/http_internal.h +0 -237
- data/ext/iodine/http_mime_parser.h +0 -350
- data/ext/iodine/iodine_caller.c +0 -230
- data/ext/iodine/iodine_connection.c +0 -941
- data/ext/iodine/iodine_defer.c +0 -420
- data/ext/iodine/iodine_fiobj2rb.h +0 -120
- data/ext/iodine/iodine_helpers.c +0 -282
- data/ext/iodine/iodine_helpers.h +0 -12
- data/ext/iodine/iodine_http.c +0 -1181
- data/ext/iodine/iodine_http.h +0 -23
- data/ext/iodine/iodine_json.c +0 -302
- data/ext/iodine/iodine_mustache.c +0 -567
- data/ext/iodine/iodine_pubsub.c +0 -580
- data/ext/iodine/iodine_pubsub.h +0 -26
- data/ext/iodine/iodine_rack_io.c +0 -273
- data/ext/iodine/iodine_rack_io.h +0 -20
- data/ext/iodine/iodine_store.c +0 -146
- data/ext/iodine/iodine_tcp.c +0 -346
- data/ext/iodine/iodine_tcp.h +0 -13
- data/ext/iodine/iodine_tls.c +0 -261
- data/ext/iodine/mustache_parser.h +0 -1546
- data/ext/iodine/redis_engine.c +0 -957
- data/ext/iodine/redis_engine.h +0 -79
- data/ext/iodine/resp_parser.h +0 -317
- data/ext/iodine/websocket_parser.h +0 -506
- data/ext/iodine/websockets.c +0 -743
- data/ext/iodine/websockets.h +0 -185
- data/lib/iodine/connection.rb +0 -61
- data/lib/iodine/json.rb +0 -42
- data/lib/iodine/mustache.rb +0 -113
- data/lib/iodine/pubsub.rb +0 -55
- data/lib/iodine/rack_utils.rb +0 -43
- data/lib/iodine/tls.rb +0 -16
- data/lib/rack/handler/iodine.rb +0 -33
- data/logo.png +0 -0
data/examples/routes.ru
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# This is a multi-route example using NeoRack vs Rack `map`
|
|
2
|
+
|
|
3
|
+
class RouteApp
|
|
4
|
+
def initialize(name)
|
|
5
|
+
@response = [200, {}, "Hello #{name}"]
|
|
6
|
+
end
|
|
7
|
+
def call(e)
|
|
8
|
+
@response
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
ROUTE_COUNT = 100
|
|
13
|
+
BSET_CASE_FOR_TRI = true
|
|
14
|
+
|
|
15
|
+
if BSET_CASE_FOR_TRI # best case
|
|
16
|
+
ROUTE_COUNT.times {|i| url = "/try_this_route-#{i}" ; map(url) { run RouteApp.new(url) } }
|
|
17
|
+
puts "\t test with:"
|
|
18
|
+
puts "\t wrk -c200 -d4 -t2 http://localhost:3000/try_this_route-0"
|
|
19
|
+
puts "\t wrk -c200 -d4 -t2 http://localhost:3000/try_this_route-#{ROUTE_COUNT / 2}"
|
|
20
|
+
puts "\t wrk -c200 -d4 -t2 http://localhost:3000/try_this_route-#{ROUTE_COUNT - 1}"
|
|
21
|
+
else
|
|
22
|
+
ROUTE_COUNT.times {|i| url = "/#{i}-try_this_route" ; map(url) { run RouteApp.new(url) } }
|
|
23
|
+
puts "\t test with:"
|
|
24
|
+
puts "\t wrk -c200 -d4 -t2 http://localhost:3000/0-try_this_route"
|
|
25
|
+
puts "\t wrk -c200 -d4 -t2 http://localhost:3000/#{ROUTE_COUNT / 2}-try_this_route"
|
|
26
|
+
puts "\t wrk -c200 -d4 -t2 http://localhost:3000/#{ROUTE_COUNT - 1}-try_this_route"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
run {[404, {}, nil]}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
#### Rails Approach
|
|
33
|
+
# GET / => index
|
|
34
|
+
# GET /new => new
|
|
35
|
+
# POST / => create
|
|
36
|
+
# GET /:id => show
|
|
37
|
+
# GET /:id/edit => edit
|
|
38
|
+
# PATCH/PUT /:id => update
|
|
39
|
+
# DELETE /:id => destroy
|
|
40
|
+
# Historical?
|
|
41
|
+
# POST /:id => update
|
|
42
|
+
|
data/exe/iodine
CHANGED
|
@@ -1,280 +1,240 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
|
-
IODINE_PARSE_CLI = true
|
|
4
3
|
require 'iodine'
|
|
4
|
+
require 'pathname'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
require 'rack'
|
|
11
|
-
module Iodine
|
|
12
|
-
module Base
|
|
13
|
-
module Rack
|
|
14
|
-
Builder = ::Rack::Builder
|
|
6
|
+
module Iodine
|
|
7
|
+
class Base
|
|
8
|
+
class NeoRackUp
|
|
9
|
+
module NOOPHandler
|
|
15
10
|
end
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
module Rack
|
|
23
|
-
# The Rack::Builder code is used when Rack isn't available.
|
|
24
|
-
#
|
|
25
|
-
# The code was copied (with minor adjustments) from the Rack source code and is licensed under the MIT license.
|
|
26
|
-
# Copyright (C) 2007-2019 Leah Neukirchen <http://leahneukirchen.org/infopage.html>
|
|
27
|
-
#
|
|
28
|
-
# ====
|
|
29
|
-
#
|
|
30
|
-
# Rack::Builder implements a small DSL to iteratively construct Rack
|
|
31
|
-
# applications.
|
|
32
|
-
#
|
|
33
|
-
# Example:
|
|
34
|
-
#
|
|
35
|
-
# require 'rack/lobster'
|
|
36
|
-
# app = Rack::Builder.new do
|
|
37
|
-
# use Rack::CommonLogger
|
|
38
|
-
# use Rack::ShowExceptions
|
|
39
|
-
# map "/lobster" do
|
|
40
|
-
# use Rack::Lint
|
|
41
|
-
# run Rack::Lobster.new
|
|
42
|
-
# end
|
|
43
|
-
# end
|
|
44
|
-
#
|
|
45
|
-
# run app
|
|
46
|
-
#
|
|
47
|
-
# Or
|
|
48
|
-
#
|
|
49
|
-
# app = Rack::Builder.app do
|
|
50
|
-
# use Rack::CommonLogger
|
|
51
|
-
# run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] }
|
|
52
|
-
# end
|
|
53
|
-
#
|
|
54
|
-
# run app
|
|
55
|
-
#
|
|
56
|
-
# +use+ adds middleware to the stack, +run+ dispatches to an application.
|
|
57
|
-
# You can use +map+ to construct a Rack::URLMap in a convenient way.
|
|
58
|
-
class Builder
|
|
59
|
-
# https://stackoverflow.com/questions/2223882/whats-the-difference-between-utf-8-and-utf-8-without-bom
|
|
60
|
-
UTF_8_BOM = '\xef\xbb\xbf'
|
|
61
|
-
|
|
62
|
-
def self.parse_file(config, opts = Hash.new)
|
|
63
|
-
if config =~ /\.ru$/
|
|
64
|
-
return self.load_file(config, opts)
|
|
65
|
-
else
|
|
66
|
-
require config
|
|
67
|
-
app = Object.const_get(::File.basename(config, '.rb').split('_').map(&:capitalize).join(''))
|
|
68
|
-
return app, {}
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def self.load_file(path, options = Hash.new)
|
|
73
|
-
cfgfile = ::File.read(path)
|
|
74
|
-
cfgfile.slice!(/\A#{UTF_8_BOM}/) if cfgfile.encoding == Encoding::UTF_8
|
|
75
|
-
|
|
76
|
-
cfgfile.sub!(/^__END__\n.*\Z/m, '')
|
|
77
|
-
app = new_from_string cfgfile, path
|
|
78
|
-
|
|
79
|
-
return app, options
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def self.new_from_string(builder_script, file = "(rackup)")
|
|
83
|
-
eval "Iodine::Base::Rack::Builder.new {\n" + builder_script + "\n}.to_app",
|
|
84
|
-
TOPLEVEL_BINDING, file, 0
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def initialize(default_app = nil, &block)
|
|
88
|
-
@use, @map, @run, @warmup, @freeze_app = [], nil, default_app, nil, false
|
|
89
|
-
instance_eval(&block) if block_given?
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def self.app(default_app = nil, &block)
|
|
93
|
-
self.new(default_app, &block).to_app
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# Specifies middleware to use in a stack.
|
|
97
|
-
#
|
|
98
|
-
# class Middleware
|
|
99
|
-
# def initialize(app)
|
|
100
|
-
# @app = app
|
|
101
|
-
# end
|
|
102
|
-
#
|
|
103
|
-
# def call(env)
|
|
104
|
-
# env["rack.some_header"] = "setting an example"
|
|
105
|
-
# @app.call(env)
|
|
106
|
-
# end
|
|
107
|
-
# end
|
|
108
|
-
#
|
|
109
|
-
# use Middleware
|
|
110
|
-
# run lambda { |env| [200, { "Content-Type" => "text/plain" }, ["OK"]] }
|
|
111
|
-
#
|
|
112
|
-
# All requests through to this application will first be processed by the middleware class.
|
|
113
|
-
# The +call+ method in this example sets an additional environment key which then can be
|
|
114
|
-
# referenced in the application if required.
|
|
115
|
-
def use(middleware, *args, &block)
|
|
116
|
-
if @map
|
|
117
|
-
mapping, @map = @map, nil
|
|
118
|
-
@use << proc { |app| generate_map(app, mapping) }
|
|
119
|
-
end
|
|
120
|
-
@use << proc { |app| middleware.new(app, *args, &block) }
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
# Takes an argument that is an object that responds to #call and returns a Rack response.
|
|
124
|
-
# The simplest form of this is a lambda object:
|
|
125
|
-
#
|
|
126
|
-
# run lambda { |env| [200, { "Content-Type" => "text/plain" }, ["OK"]] }
|
|
127
|
-
#
|
|
128
|
-
# However this could also be a class:
|
|
129
|
-
#
|
|
130
|
-
# class Heartbeat
|
|
131
|
-
# def self.call(env)
|
|
132
|
-
# [200, { "Content-Type" => "text/plain" }, ["OK"]]
|
|
133
|
-
# end
|
|
134
|
-
# end
|
|
135
|
-
#
|
|
136
|
-
# run Heartbeat
|
|
137
|
-
def run(app = nil, &block)
|
|
138
|
-
raise ArgumentError, "Both app and block given!" if app && block_given?
|
|
139
|
-
@run = app || block
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
# Takes a lambda or block that is used to warm-up the application.
|
|
143
|
-
#
|
|
144
|
-
# warmup do |app|
|
|
145
|
-
# client = Rack::MockRequest.new(app)
|
|
146
|
-
# client.get('/')
|
|
147
|
-
# end
|
|
148
|
-
#
|
|
149
|
-
# use SomeMiddleware
|
|
150
|
-
# run MyApp
|
|
151
|
-
def warmup(prc = nil, &block)
|
|
152
|
-
@warmup = prc || block
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
# Creates a route within the application.
|
|
156
|
-
#
|
|
157
|
-
# Rack::Builder.app do
|
|
158
|
-
# map '/' do
|
|
159
|
-
# run Heartbeat
|
|
160
|
-
# end
|
|
161
|
-
# end
|
|
162
|
-
#
|
|
163
|
-
# The +use+ method can also be used here to specify middleware to run under a specific path:
|
|
164
|
-
#
|
|
165
|
-
# Rack::Builder.app do
|
|
166
|
-
# map '/' do
|
|
167
|
-
# use Middleware
|
|
168
|
-
# run Heartbeat
|
|
169
|
-
# end
|
|
170
|
-
# end
|
|
171
|
-
#
|
|
172
|
-
# This example includes a piece of middleware which will run before requests hit +Heartbeat+.
|
|
173
|
-
#
|
|
174
|
-
def map(path, &block)
|
|
175
|
-
@map ||= {}
|
|
176
|
-
@map[path] = block
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
# Freeze the app (set using run) and all middleware instances when building the application
|
|
180
|
-
# in to_app.
|
|
181
|
-
def freeze_app
|
|
182
|
-
@freeze_app = true
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
def to_app
|
|
186
|
-
app = @map ? generate_map(@run, @map) : @run
|
|
187
|
-
fail "missing run or map statement" unless app
|
|
188
|
-
app.freeze if @freeze_app
|
|
189
|
-
app = @use.reverse.inject(app) { |a, e| e[a].tap { |x| x.freeze if @freeze_app } }
|
|
190
|
-
@warmup.call(app) if @warmup
|
|
191
|
-
app
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
def call(env)
|
|
195
|
-
to_app.call(env)
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
private
|
|
199
|
-
|
|
200
|
-
def generate_map(default_app, mapping)
|
|
201
|
-
mapped = default_app ? { '/' => default_app } : {}
|
|
202
|
-
mapping.each { |r, b| mapped[r] = self.class.new(default_app, &b).to_app }
|
|
203
|
-
URLMap.new(mapped)
|
|
204
|
-
end
|
|
11
|
+
module MiddleWarePre__
|
|
12
|
+
def initialize(*args, &blk); @app = args[0]; super; end
|
|
13
|
+
private
|
|
14
|
+
def method_missing(method_name, *arguments)
|
|
15
|
+
m = self.define_singleton_method(method_name) {|*args| @app.send(method_name, *args) }
|
|
16
|
+
m.call(*arguments)
|
|
205
17
|
end
|
|
206
18
|
end
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
end
|
|
19
|
+
module MiddleWareDef__
|
|
20
|
+
def on_http(e); @app.on_http(e); end
|
|
21
|
+
def on_finish(e); @app.on_finish(e); end
|
|
22
|
+
def call(env); @app.call(env); end
|
|
23
|
+
def on_authenticate(e); @app.on_authenticate(e); end
|
|
24
|
+
def on_authenticate_sse(e); @app.on_authenticate_sse(e); end
|
|
25
|
+
def on_authenticate_websocket(e); @app.on_authenticate_websocket(e); end
|
|
26
|
+
def on_open(e); @app.on_open(e); end
|
|
27
|
+
def on_message(e, data); @app.on_message(e, data); end
|
|
28
|
+
def on_drained(e); @app.on_drained(e); end
|
|
29
|
+
def on_timeout(e); @app.on_timeout(e); end
|
|
30
|
+
def on_shutdown(e); @app.on_shutdown(e); end
|
|
31
|
+
def on_close(e); @app.on_close(e); end
|
|
32
|
+
def on_eventsource(e, message); @app.on_eventsource(e, message); end
|
|
33
|
+
def on_eventsource_reconnect(e,id); @app.on_eventsource_reconnect(e,id); end
|
|
34
|
+
end
|
|
210
35
|
|
|
211
|
-
|
|
212
|
-
# The Iodine::Base namespace is reserved for internal use and is NOT part of the public API.
|
|
213
|
-
module Base
|
|
214
|
-
# Command line interface. The Ruby CLI might be changed in future versions.
|
|
215
|
-
module CLI
|
|
36
|
+
UTF_8_BOM = "\xef\xbb\xbf"
|
|
216
37
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
38
|
+
# uses specified Middleware class.
|
|
39
|
+
def use(mw, *args, &block)
|
|
40
|
+
@middleware ||= []
|
|
41
|
+
mw.prepend ::Iodine::Base::NeoRackUp::MiddleWarePre__
|
|
42
|
+
mw.include ::Iodine::Base::NeoRackUp::MiddleWareDef__
|
|
43
|
+
@middleware <<[mw, args, block]
|
|
222
44
|
end
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
45
|
+
# Maps a URL path to a specific NeoRack Application
|
|
46
|
+
#
|
|
47
|
+
# Accepts an optional block for nested URL mapping and middleware use.
|
|
48
|
+
def map(path, handler = nil, &block)
|
|
49
|
+
path = "/" if path.nil?
|
|
50
|
+
raise "`path` must be a String in a call to map(path)." unless path.is_a?(String)
|
|
51
|
+
@middleware ||= []
|
|
52
|
+
@app ||= nil
|
|
53
|
+
@map ||= {}
|
|
54
|
+
@path_prefix ||= "".dup
|
|
55
|
+
old_middleware = @middleware.dup
|
|
56
|
+
old_app = @app
|
|
57
|
+
old_path_prefix = @path_prefix.dup
|
|
58
|
+
# prevent excess '/' and ignore special '*' allowances
|
|
59
|
+
@path_prefix.slice!(0, @path_prefix.length - 2) if (@path_prefix[-1] == '*' && @path_prefix[-2] == '/')
|
|
60
|
+
@path_prefix.slice!(0, @path_prefix.length - 1) if (@path_prefix[-1] == '/')
|
|
61
|
+
@path_prefix << '/' unless (path[0] == '/')
|
|
62
|
+
@path_prefix << path
|
|
63
|
+
@app = handler
|
|
64
|
+
block.call if block
|
|
65
|
+
puts "WARNING: overwriting existing route mapping @ #{@path_prefix}" if(@map[@path_prefix])
|
|
66
|
+
@map[@path_prefix] = @app
|
|
67
|
+
@middleware = old_middleware
|
|
68
|
+
@app = old_app
|
|
69
|
+
@path_prefix = old_path_prefix
|
|
70
|
+
rescue Exception => e
|
|
71
|
+
err = e
|
|
72
|
+
@app = nil
|
|
73
|
+
Iodine.stop
|
|
74
|
+
raise e
|
|
75
|
+
end
|
|
76
|
+
# Makes the calling module into a NeoRack Resource handler.
|
|
77
|
+
def neorack_resource(*ha)
|
|
78
|
+
ha.each {|handler| Server.make_resource(handler) }
|
|
79
|
+
end
|
|
80
|
+
# Sets NeoRack application for the server to run.
|
|
81
|
+
def run(a = nil, &block)
|
|
82
|
+
a = block if(!a)
|
|
83
|
+
raise "`run` called more than once." if @app
|
|
84
|
+
raise "Missing either an application handler or a block for the `run` instruction." unless a
|
|
85
|
+
raise "Missing both `on_http(e)` and `call(env)` in provided App." unless (a.respond_to?(:on_http) || a.respond_to?(:call))
|
|
86
|
+
Iodine::Base.add_missing_handler_methods(a)
|
|
87
|
+
while(@middleware && !@middleware.empty?)
|
|
88
|
+
m = @middleware.pop
|
|
89
|
+
a = m[0].new(a, *m[1], &m[2])
|
|
236
90
|
end
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
91
|
+
@app = a
|
|
92
|
+
rescue Exception => e
|
|
93
|
+
err = e
|
|
94
|
+
@app = nil
|
|
95
|
+
Iodine.stop
|
|
96
|
+
raise e
|
|
97
|
+
end
|
|
98
|
+
# Returns either the NeoRack Application or the routing map (if using a map) to be used.
|
|
99
|
+
def app
|
|
100
|
+
@map ||= {}
|
|
101
|
+
@map['/'] ||= @app
|
|
102
|
+
@map
|
|
103
|
+
end
|
|
104
|
+
# validate name of Web Application configuration file.
|
|
105
|
+
def self.iodine___detect_filename
|
|
106
|
+
file_name = Iodine::Base::CLI.parse(true)[0];
|
|
107
|
+
if(!file_name || File.directory?(file_name))
|
|
108
|
+
file_name ||= './'
|
|
109
|
+
file_name += '/' unless file_name.end_with?('/')
|
|
110
|
+
defaults = ["#{file_name}config.ru", "#{file_name}config.nru"]
|
|
111
|
+
defaults.pop if Iodine::Base::CLI['-R']
|
|
112
|
+
file_name = nil;
|
|
113
|
+
defaults.each {|n| file_name = n if(File.exist?(n))}
|
|
114
|
+
file_name ||= defaults[-1]
|
|
249
115
|
end
|
|
250
|
-
|
|
116
|
+
file_name = nil if(!File.exist?(file_name))
|
|
117
|
+
raise("Web App not found: #{(Iodine::Base::CLI[0].nil? || File.directory?(Iodine::Base::CLI[0])) ? "missing config.#{Iodine::Base::CLI['-R'] ? '' : 'n'}ru?" : "#{Iodine::Base::CLI[0]} not found"}") if (file_name.nil? && (!Iodine::Base::CLI[0].nil? || Iodine::Base::CLI['-www'].nil?))
|
|
118
|
+
file_name
|
|
251
119
|
end
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
120
|
+
def self.iodine___load_rack fn
|
|
121
|
+
return nil unless fn
|
|
122
|
+
return nil unless (Iodine::Base::CLI['-R'] || File.extname(fn) == '.ru')
|
|
123
|
+
begin
|
|
124
|
+
require 'rack'
|
|
125
|
+
rescue LoadError => e
|
|
126
|
+
return nil unless Iodine::Base::CLI['-R']
|
|
127
|
+
puts "ERROR: rack not installed."
|
|
128
|
+
puts " iodine -R requires the rack gem to be installed.\n"
|
|
129
|
+
raise e
|
|
130
|
+
end
|
|
131
|
+
Iodine::Utils.monkey_patch ::Rack::Utils
|
|
132
|
+
app = ::Rack::Builder.parse_file(fn)
|
|
133
|
+
app = app[0] if(app.is_a?(Array)) # Rack 2.x vs. Rack 3.x
|
|
134
|
+
app = {'/' => app} if app
|
|
135
|
+
end
|
|
136
|
+
def self.iodine___load_neorack fn
|
|
137
|
+
return nil unless fn
|
|
138
|
+
cfgfile = ::File.read(fn)
|
|
139
|
+
cfgfile.slice!(/\A#{UTF_8_BOM}/) if cfgfile.encoding == Encoding::UTF_8
|
|
140
|
+
cfgfile.sub!(/^__END__\n.*\Z/m, '')
|
|
141
|
+
builder = self.new
|
|
142
|
+
builder.instance_eval(cfgfile, fn, 0)
|
|
143
|
+
builder.app
|
|
144
|
+
end
|
|
145
|
+
def self.iodine___load_app_code(listener, fn, root_pid)
|
|
146
|
+
err = "Web App not found (no `run`?) in #{fn}"
|
|
147
|
+
app = nil
|
|
148
|
+
if(fn)
|
|
149
|
+
begin
|
|
150
|
+
old_wd = Dir.getwd
|
|
151
|
+
fn_name = File.basename(fn)
|
|
152
|
+
Dir.chdir(File.dirname(fn))
|
|
153
|
+
require 'bundler/setup' if File.exist?('Gemfile') && !defined?(::Bundler)
|
|
154
|
+
app = ::Iodine::Base::NeoRackUp.iodine___load_rack(fn_name)
|
|
155
|
+
unless app || Iodine::Base::CLI['-R']
|
|
156
|
+
app = ::Iodine::Base::NeoRackUp.iodine___load_neorack(fn_name)
|
|
266
157
|
end
|
|
158
|
+
Dir.chdir(old_wd)
|
|
159
|
+
rescue Exception => e
|
|
160
|
+
err = e
|
|
161
|
+
app = nil
|
|
267
162
|
end
|
|
268
163
|
end
|
|
164
|
+
unless !fn || app
|
|
165
|
+
Iodine.stop
|
|
166
|
+
Process.kill("TERM", root_pid)
|
|
167
|
+
raise(err)
|
|
168
|
+
end
|
|
169
|
+
if app && app.is_a?(Hash) && app['/']
|
|
170
|
+
app.each {|k,v| listener.map(k,v) }
|
|
171
|
+
elsif app
|
|
172
|
+
listener.map nil, app
|
|
173
|
+
else
|
|
174
|
+
puts "WARNING: (#{Process.pid}) no App found."
|
|
175
|
+
end
|
|
269
176
|
end
|
|
270
|
-
|
|
271
|
-
def self.
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
Iodine::
|
|
177
|
+
### loads auto-detected configuration file (iodine.rb)
|
|
178
|
+
def self.iodine___load_conf(fn = "./")
|
|
179
|
+
old_wd = Dir.getwd
|
|
180
|
+
Dir.chdir(File.dirname(fn)) if(fn && File.dirname(fn))
|
|
181
|
+
conf = Iodine::Base::CLI['--config']
|
|
182
|
+
conf = (::Pathname.new(File.dirname(fn.to_s).to_s) + Iodine::Base::CLI['--config']).to_s if(!conf || !File.exist?(conf))
|
|
183
|
+
unless File.exist?(conf)
|
|
184
|
+
puts "* No configuration file found @ #{conf}"
|
|
185
|
+
Dir.chdir(old_wd)
|
|
186
|
+
return
|
|
187
|
+
end
|
|
188
|
+
puts "* Pre-loading configuration @ #{conf}"
|
|
189
|
+
load conf
|
|
190
|
+
Dir.chdir(old_wd)
|
|
191
|
+
end
|
|
192
|
+
def self.iodine___start
|
|
193
|
+
fn = ::Iodine::Base::NeoRackUp.iodine___detect_filename
|
|
194
|
+
raise "No App and no Static File Service (-www) detected." if(fn.nil? && !Iodine::Base::CLI['-www'])
|
|
195
|
+
### load configuration file
|
|
196
|
+
::Iodine::Base::NeoRackUp.iodine___load_conf(fn)
|
|
197
|
+
puts "* Starting iodine @ #{fn ? fn : Iodine::Base::CLI['-www']}"
|
|
198
|
+
listener = Iodine.listen(nil, Iodine::Base::NeoRackUp::NOOPHandler)
|
|
199
|
+
root_pid = Process.pid
|
|
200
|
+
### hot code reloading?
|
|
201
|
+
if Iodine::Base::CLI['-preload']
|
|
202
|
+
iodine___load_app_code(listener, fn, root_pid)
|
|
203
|
+
else
|
|
204
|
+
Iodine.on_state(:start) do
|
|
205
|
+
iodine___load_app_code(listener, fn, root_pid)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
Iodine.start
|
|
275
209
|
end
|
|
276
210
|
end
|
|
277
211
|
end
|
|
278
212
|
end
|
|
279
213
|
|
|
280
|
-
|
|
214
|
+
### .pid file
|
|
215
|
+
if Iodine::Base::CLI['-pid']
|
|
216
|
+
pid_filename = Iodine::Base::CLI['-pid']
|
|
217
|
+
pid_filename << "iodine.pid" if(pid_filename[-1] == '/')
|
|
218
|
+
if File.exist?(pid_filename)
|
|
219
|
+
raise "pid filename should point to a valid file name (not a folder)!" if(!File.file?(pid_filename))
|
|
220
|
+
raise "pid filename error! (doesn't end with .pid)" if(File.extname(pid_filename) != '.pid')
|
|
221
|
+
File.delete(pid_filename)
|
|
222
|
+
end
|
|
223
|
+
Iodine.on_state(:pre_start) do
|
|
224
|
+
IO.binwrite(pid_filename, "#{Process.pid}\r\n")
|
|
225
|
+
end
|
|
226
|
+
Iodine.on_state(:exit) do
|
|
227
|
+
File.delete(pid_filename)
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
### hot restarts
|
|
231
|
+
if Iodine::Base::CLI['-hr'] && Iodine::Base::CLI['-w'].to_i != 0
|
|
232
|
+
Iodine.run_after(Iodine::Base::CLI['-hr'] * 1000, -1) do
|
|
233
|
+
Iodine.master? ? Process.kill("USR1", Process.pid) : false
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
### Start
|
|
238
|
+
Iodine::Base::NeoRackUp.iodine___start
|
|
239
|
+
|
|
240
|
+
|
data/ext/iodine/extconf.rb
CHANGED
|
@@ -1,110 +1,37 @@
|
|
|
1
1
|
require 'mkmf'
|
|
2
|
-
require 'fileutils'
|
|
3
2
|
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
\#include <sys/types.h>
|
|
20
|
-
\#include <sys/stat.h>
|
|
21
|
-
\#include <fcntl.h>
|
|
22
|
-
\#include <sys/epoll.h>
|
|
23
|
-
int main(void) {
|
|
24
|
-
int fd = epoll_create1(EPOLL_CLOEXEC);
|
|
25
|
-
}
|
|
26
|
-
EOS
|
|
27
|
-
|
|
28
|
-
iodine_poll_test_poll = <<EOS
|
|
29
|
-
\#define _GNU_SOURCE
|
|
30
|
-
\#include <stdlib.h>
|
|
31
|
-
\#include <poll.h>
|
|
32
|
-
int main(void) {
|
|
33
|
-
struct pollfd plist[18];
|
|
34
|
-
memset(plist, 0, sizeof(plist[0]) * 18);
|
|
35
|
-
poll(plist, 1, 1);
|
|
36
|
-
}
|
|
37
|
-
EOS
|
|
38
|
-
|
|
39
|
-
# Test for manual selection and then TRY_COMPILE with each polling engine
|
|
40
|
-
if Gem.win_platform?
|
|
41
|
-
puts "skipping polling tests, using WSAPOLL on Windows"
|
|
42
|
-
$defs << "-DFIO_ENGINE_WSAPOLL"
|
|
43
|
-
elsif ENV['FIO_POLL']
|
|
44
|
-
puts "skipping polling tests, enforcing manual selection of: poll"
|
|
45
|
-
$defs << "-DFIO_ENGINE_POLL"
|
|
46
|
-
elsif ENV['FIO_FORCE_POLL']
|
|
47
|
-
puts "skipping polling tests, enforcing manual selection of: poll"
|
|
48
|
-
$defs << "-DFIO_ENGINE_POLL"
|
|
49
|
-
elsif ENV['FIO_FORCE_EPOLL']
|
|
50
|
-
puts "skipping polling tests, enforcing manual selection of: epoll"
|
|
51
|
-
$defs << "-DFIO_ENGINE_EPOLL"
|
|
52
|
-
elsif ENV['FIO_FORCE_KQUEUE']
|
|
53
|
-
puts "* Skipping polling tests, enforcing manual selection of: kqueue"
|
|
54
|
-
$defs << "-DFIO_ENGINE_KQUEUE"
|
|
55
|
-
elsif try_compile(iodine_poll_test_epoll)
|
|
56
|
-
puts "detected `epoll`"
|
|
57
|
-
$defs << "-DFIO_ENGINE_EPOLL"
|
|
58
|
-
elsif try_compile(iodine_poll_test_kqueue)
|
|
59
|
-
puts "detected `kqueue`"
|
|
60
|
-
$defs << "-DFIO_ENGINE_KQUEUE"
|
|
61
|
-
elsif try_compile(iodine_poll_test_poll)
|
|
62
|
-
puts "detected `poll` - this is suboptimal fallback!"
|
|
63
|
-
$defs << "-DFIO_ENGINE_POLL"
|
|
3
|
+
# TLS backend selection:
|
|
4
|
+
# - IODINE_USE_EMBEDDED_TLS=1 forces embedded TLS 1.3 as default
|
|
5
|
+
# - Otherwise, OpenSSL is used if available and compatible
|
|
6
|
+
begin
|
|
7
|
+
dir_config('openssl')
|
|
8
|
+
if ENV['IODINE_USE_EMBEDDED_TLS']
|
|
9
|
+
puts '* Default TLS: embedded (fio_tls13) - forced by IODINE_USE_EMBEDDED_TLS'
|
|
10
|
+
elsif have_library('crypto') && have_library('ssl')
|
|
11
|
+
require 'openssl'
|
|
12
|
+
if OpenSSL::VERSION.to_i > 2
|
|
13
|
+
puts "* Detected OpenSSL version >= 3 (#{OpenSSL::VERSION}), setting the HAVE_OPENSSL flag."
|
|
14
|
+
$defs << '-DHAVE_OPENSSL'
|
|
15
|
+
else
|
|
16
|
+
puts "* OpenSSL version incompatible (#{OpenSSL::VERSION}), using embedded TLS 1.3"
|
|
17
|
+
end
|
|
64
18
|
else
|
|
65
|
-
puts
|
|
19
|
+
puts '* OpenSSL not found, using embedded TLS 1.3'
|
|
66
20
|
end
|
|
21
|
+
rescue StandardError => e
|
|
22
|
+
puts "* Couldn't find OpenSSL - using embedded TLS 1.3\n\t Err: #{e.message}"
|
|
67
23
|
end
|
|
68
24
|
|
|
69
|
-
|
|
25
|
+
$defs << '-DDEBUG' if ENV['DEBUG']
|
|
70
26
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
unless ENV['NO_SSL'] || ENV['NO_TLS'] || ENV["DISABLE_SSL"]
|
|
74
|
-
OPENSSL_TEST_CODE = <<EOS
|
|
75
|
-
\#include <openssl/bio.h>
|
|
76
|
-
\#include <openssl/err.h>
|
|
77
|
-
\#include <openssl/ssl.h>
|
|
78
|
-
\#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
79
|
-
\#error "OpenSSL version too small"
|
|
80
|
-
\#endif
|
|
81
|
-
int main(void) {
|
|
82
|
-
SSL_library_init();
|
|
83
|
-
SSL_CTX *ctx = SSL_CTX_new(TLS_method());
|
|
84
|
-
SSL *ssl = SSL_new(ctx);
|
|
85
|
-
BIO *bio = BIO_new_socket(3, 0);
|
|
86
|
-
BIO_up_ref(bio);
|
|
87
|
-
SSL_set0_rbio(ssl, bio);
|
|
88
|
-
SSL_set0_wbio(ssl, bio);
|
|
89
|
-
}
|
|
90
|
-
EOS
|
|
27
|
+
append_cflags('-Wno-undef')
|
|
28
|
+
append_cflags('-Wno-missing-noreturn')
|
|
91
29
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if have_library('crypto') && have_library('ssl')
|
|
98
|
-
puts "detected OpenSSL library, testing for version and required functions."
|
|
99
|
-
if try_compile(OPENSSL_TEST_CODE)
|
|
100
|
-
$defs << "-DHAVE_OPENSSL"
|
|
101
|
-
puts "confirmed OpenSSL to be version 1.1.0 or above (#{OpenSSL::OPENSSL_LIBRARY_VERSION})...\n* compiling with HAVE_OPENSSL."
|
|
102
|
-
else
|
|
103
|
-
puts "FAILED: OpenSSL version not supported (#{OpenSSL::OPENSSL_LIBRARY_VERSION} is too old)."
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
end
|
|
30
|
+
# Windows requires crypt32 for cryptographic primitives used by the TLS backend
|
|
31
|
+
# and bcrypt for the system CSPRNG (`BCryptGenRandom` in `fio_rand_bytes_secure`)
|
|
32
|
+
if /mingw|mswin/ =~ RUBY_PLATFORM
|
|
33
|
+
$libs = append_library($libs, 'crypt32')
|
|
34
|
+
$libs = append_library($libs, 'bcrypt')
|
|
108
35
|
end
|
|
109
36
|
|
|
110
|
-
create_makefile 'iodine/
|
|
37
|
+
create_makefile 'iodine/iodine'
|