rage-iodine 1.7.58

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. data/.github/workflows/ruby.yml +42 -0
  4. data/.gitignore +20 -0
  5. data/.rspec +2 -0
  6. data/.yardopts +8 -0
  7. data/CHANGELOG.md +1098 -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 +23 -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/bates/README.md +3 -0
  25. data/examples/bates/config.ru +342 -0
  26. data/examples/bates/david+bold.pdf +0 -0
  27. data/examples/bates/public/drop-pdf.png +0 -0
  28. data/examples/bates/public/index.html +600 -0
  29. data/examples/config.ru +59 -0
  30. data/examples/echo.ru +59 -0
  31. data/examples/etag.ru +16 -0
  32. data/examples/hello.ru +29 -0
  33. data/examples/pubsub_engine.ru +81 -0
  34. data/examples/rack3.ru +12 -0
  35. data/examples/redis.ru +70 -0
  36. data/examples/shootout.ru +73 -0
  37. data/examples/sub-protocols.ru +90 -0
  38. data/examples/tcp_client.rb +66 -0
  39. data/examples/x-sendfile.ru +14 -0
  40. data/exe/iodine +280 -0
  41. data/ext/iodine/extconf.rb +110 -0
  42. data/ext/iodine/fio.c +12096 -0
  43. data/ext/iodine/fio.h +6390 -0
  44. data/ext/iodine/fio_cli.c +431 -0
  45. data/ext/iodine/fio_cli.h +189 -0
  46. data/ext/iodine/fio_json_parser.h +687 -0
  47. data/ext/iodine/fio_siphash.c +157 -0
  48. data/ext/iodine/fio_siphash.h +37 -0
  49. data/ext/iodine/fio_tls.h +129 -0
  50. data/ext/iodine/fio_tls_missing.c +649 -0
  51. data/ext/iodine/fio_tls_openssl.c +1056 -0
  52. data/ext/iodine/fio_tmpfile.h +50 -0
  53. data/ext/iodine/fiobj.h +44 -0
  54. data/ext/iodine/fiobj4fio.h +21 -0
  55. data/ext/iodine/fiobj_ary.c +333 -0
  56. data/ext/iodine/fiobj_ary.h +139 -0
  57. data/ext/iodine/fiobj_data.c +1185 -0
  58. data/ext/iodine/fiobj_data.h +167 -0
  59. data/ext/iodine/fiobj_hash.c +409 -0
  60. data/ext/iodine/fiobj_hash.h +176 -0
  61. data/ext/iodine/fiobj_json.c +622 -0
  62. data/ext/iodine/fiobj_json.h +68 -0
  63. data/ext/iodine/fiobj_mem.h +71 -0
  64. data/ext/iodine/fiobj_mustache.c +317 -0
  65. data/ext/iodine/fiobj_mustache.h +62 -0
  66. data/ext/iodine/fiobj_numbers.c +344 -0
  67. data/ext/iodine/fiobj_numbers.h +127 -0
  68. data/ext/iodine/fiobj_str.c +433 -0
  69. data/ext/iodine/fiobj_str.h +172 -0
  70. data/ext/iodine/fiobject.c +620 -0
  71. data/ext/iodine/fiobject.h +654 -0
  72. data/ext/iodine/hpack.h +1923 -0
  73. data/ext/iodine/http.c +2736 -0
  74. data/ext/iodine/http.h +1019 -0
  75. data/ext/iodine/http1.c +825 -0
  76. data/ext/iodine/http1.h +29 -0
  77. data/ext/iodine/http1_parser.h +1835 -0
  78. data/ext/iodine/http_internal.c +1279 -0
  79. data/ext/iodine/http_internal.h +248 -0
  80. data/ext/iodine/http_mime_parser.h +350 -0
  81. data/ext/iodine/iodine.c +1433 -0
  82. data/ext/iodine/iodine.h +64 -0
  83. data/ext/iodine/iodine_caller.c +218 -0
  84. data/ext/iodine/iodine_caller.h +27 -0
  85. data/ext/iodine/iodine_connection.c +941 -0
  86. data/ext/iodine/iodine_connection.h +55 -0
  87. data/ext/iodine/iodine_defer.c +420 -0
  88. data/ext/iodine/iodine_defer.h +6 -0
  89. data/ext/iodine/iodine_fiobj2rb.h +120 -0
  90. data/ext/iodine/iodine_helpers.c +282 -0
  91. data/ext/iodine/iodine_helpers.h +12 -0
  92. data/ext/iodine/iodine_http.c +1280 -0
  93. data/ext/iodine/iodine_http.h +23 -0
  94. data/ext/iodine/iodine_json.c +302 -0
  95. data/ext/iodine/iodine_json.h +6 -0
  96. data/ext/iodine/iodine_mustache.c +567 -0
  97. data/ext/iodine/iodine_mustache.h +6 -0
  98. data/ext/iodine/iodine_pubsub.c +580 -0
  99. data/ext/iodine/iodine_pubsub.h +26 -0
  100. data/ext/iodine/iodine_rack_io.c +273 -0
  101. data/ext/iodine/iodine_rack_io.h +20 -0
  102. data/ext/iodine/iodine_store.c +142 -0
  103. data/ext/iodine/iodine_store.h +20 -0
  104. data/ext/iodine/iodine_tcp.c +346 -0
  105. data/ext/iodine/iodine_tcp.h +13 -0
  106. data/ext/iodine/iodine_tls.c +261 -0
  107. data/ext/iodine/iodine_tls.h +13 -0
  108. data/ext/iodine/mustache_parser.h +1546 -0
  109. data/ext/iodine/redis_engine.c +957 -0
  110. data/ext/iodine/redis_engine.h +79 -0
  111. data/ext/iodine/resp_parser.h +317 -0
  112. data/ext/iodine/scheduler.c +173 -0
  113. data/ext/iodine/scheduler.h +6 -0
  114. data/ext/iodine/websocket_parser.h +506 -0
  115. data/ext/iodine/websockets.c +752 -0
  116. data/ext/iodine/websockets.h +185 -0
  117. data/iodine.gemspec +50 -0
  118. data/lib/iodine/connection.rb +61 -0
  119. data/lib/iodine/json.rb +42 -0
  120. data/lib/iodine/mustache.rb +113 -0
  121. data/lib/iodine/pubsub.rb +55 -0
  122. data/lib/iodine/rack_utils.rb +43 -0
  123. data/lib/iodine/tls.rb +16 -0
  124. data/lib/iodine/version.rb +3 -0
  125. data/lib/iodine.rb +274 -0
  126. data/lib/rack/handler/iodine.rb +33 -0
  127. data/logo.png +0 -0
  128. metadata +284 -0
data/exe/iodine ADDED
@@ -0,0 +1,280 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ IODINE_PARSE_CLI = true
4
+ require 'iodine'
5
+
6
+ # Load Rack if available (assume it will be used).
7
+ #
8
+ # Remember, code costs memory. Duplicating the Rack::Builder module and functions can be avoided.
9
+ begin
10
+ require 'rack'
11
+ module Iodine
12
+ module Base
13
+ module Rack
14
+ Builder = ::Rack::Builder
15
+ end
16
+ end
17
+ end
18
+ rescue LoadError
19
+ puts "WARNING: The gam `rack` wasn't loaded before iodine started loading the rack app, using custom loader."
20
+ module Iodine
21
+ module Base
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
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
210
+
211
+ module Iodine
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
216
+
217
+ def self.try_file filename
218
+ return nil unless File.exist? filename
219
+ result = Iodine::Base::Rack::Builder.parse_file filename
220
+ return result[0] if(result.is_a?(Array))
221
+ return result
222
+ end
223
+
224
+ def self.get_app_opts
225
+ app, opt = nil, Hash.new
226
+ filename = Iodine::DEFAULT_SETTINGS[:filename_]
227
+ if filename
228
+ app = try_file(filename)
229
+ app = try_file("#{filename}.ru") unless app
230
+ unless app
231
+ puts "* Couldn't find #{filename}\n testing for config.ru\n"
232
+ app = try_file "config.ru"
233
+ end
234
+ else
235
+ app = try_file "config.ru";
236
+ end
237
+
238
+ unless app
239
+ puts "WARNING: Ruby application not found#{ filename ? " - missing both #{filename} and config.ru" : " - missing config.ru"}."
240
+ if Iodine::DEFAULT_SETTINGS[:public]
241
+ puts " Running only static file service."
242
+ app = Proc.new { [404, {}, "Not Found!"] }
243
+ else
244
+ puts "\nERROR: Couldn't run Ruby application, check command line arguments."
245
+ ARGV << "-?"
246
+ Iodine::Base::CLI.parse
247
+ exit(0);
248
+ end
249
+ end
250
+ return app, opt
251
+ end
252
+
253
+ def self.perform_warmup(app)
254
+ # load anything marked with `autoload`, since autoload is niether thread safe nor fork friendly.
255
+ Iodine.on_state(:on_start) do
256
+ Module.constants.each do |n|
257
+ begin
258
+ Object.const_get(n)
259
+ rescue StandardError => _e
260
+ end
261
+ end
262
+ Iodine::Base::Rack::Builder.new(app) do |r|
263
+ r.warmup do |a|
264
+ client = ::Rack::MockRequest.new(a)
265
+ client.get('/')
266
+ end
267
+ end
268
+ end
269
+ end
270
+
271
+ def self.call
272
+ app, opt = get_app_opts
273
+ perform_warmup(app) if Iodine::DEFAULT_SETTINGS[:warmup_]
274
+ Iodine::Rack.run(app, opt)
275
+ end
276
+ end
277
+ end
278
+ end
279
+
280
+ Iodine::Base::CLI.call
@@ -0,0 +1,110 @@
1
+ require 'mkmf'
2
+ require 'fileutils'
3
+
4
+ # Test polling
5
+ def iodine_test_polling_support
6
+ iodine_poll_test_kqueue = <<EOS
7
+ \#define _GNU_SOURCE
8
+ \#include <stdlib.h>
9
+ \#include <sys/event.h>
10
+ int main(void) {
11
+ int fd = kqueue();
12
+ }
13
+ EOS
14
+
15
+ iodine_poll_test_epoll = <<EOS
16
+ \#define _GNU_SOURCE
17
+ \#include <stdlib.h>
18
+ \#include <stdio.h>
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"
64
+ else
65
+ puts "* WARNING: No supported polling engine! expecting compilation to fail."
66
+ end
67
+ end
68
+
69
+ iodine_test_polling_support()
70
+
71
+ unless Gem.win_platform?
72
+ # Test for OpenSSL version equal to 1.0.0 or greater.
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
91
+
92
+ dir_config("openssl")
93
+ begin
94
+ require 'openssl'
95
+ rescue LoadError
96
+ else
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
108
+ end
109
+
110
+ create_makefile 'iodine/iodine_ext'