iodine 0.7.32 → 0.7.33

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1176ef5b01b31595fe7cccdf6cad949354b5f733ae870a1b56be511d21b0a860
4
- data.tar.gz: 15d65ddf131dbb20b40d4aceb6709abff2634b9a68c00e2a2f54480cbca1f9da
3
+ metadata.gz: 430ea5b683f9561e44a9e1b4f6ab56f1b9c6e58637b3ecaae9f414eed58cc1ba
4
+ data.tar.gz: 17a876d312611ccb0892a9f41b6ae6ca9fa0257b8c78778a7a8edf545f3dc165
5
5
  SHA512:
6
- metadata.gz: 12e5c3799f8f85ec60f1e5a2c596e88d5fc4f8dd1704a82a9159c4d3a6894ac48d5adcf7f36c8ab346ad764573f361090790dc96938cf4804cd4369cb2879ce6
7
- data.tar.gz: 8fedb5972019f22d202d5bad886c01645caadbe864d9b959d2a11f99c70ca038d4d8042b78751c05151da2a516c02f8aeccb05fb8a283d45e533726166cafd88
6
+ metadata.gz: 3db4b691bae4fe0745c36b4d29a960bfd712e7cb3a29cc9f4e2f357a680f3c325b808bff8720a9f3835d8b0c86c725a0542c0d1800e2c38fd77198ee8f13399d
7
+ data.tar.gz: 0404f0211d42ece456f46bfa89b945f3c01d1b87770c612e276743aa3a82519b40e6705854f66a5bceb9d9e3fea27fe86ad9081d3a86271e5cccd07f6c6f0df8
@@ -6,6 +6,14 @@ Please notice that this change log contains changes for upcoming releases as wel
6
6
 
7
7
  ## Changes:
8
8
 
9
+ #### Change log v.0.7.33
10
+
11
+ **Fix**: (`iodine`) exception protection would fail and crash if the exception throws wasn't of type `Exception`. I'm not sure how this would happen, but on some Ruby versions it appeared to have occur, maybe where a custom `raise` would be called with a non-exception type. The issue was fixed by testing for the availability of the `message` and `backtrace` functions. Credit to Jan Biedermann (@janbiedermann) for exposing this issue (#76).
12
+
13
+ **Fix**: (`cli`) the CLI interface no longer requires Rack to be installed. If Rack is installed, it will be used. Otherwise, a copy of the Rack::Builder code will execute, licensed under the MIT license from the Rack source code (Copyright (C) 2007-2019 [Leah Neukirchen](http://leahneukirchen.org/infopage.html)).
14
+
15
+ **Compatibility**: (`facil`) iodine would raise the signal `SIGINT` when shutting down in cluster mode, even if shutdown was initiated using `Iodine.stop`. Although this was designed to ensure worker processes would stop, this approach caused RSpec to stop testing and report an error. A temporary fix was applied and might be upstreamed to the facil.io repo. Credit to Jan Biedermann (@janbiedermann) for exposing this issue (#76).
16
+
9
17
  #### Change log v.0.7.32
10
18
 
11
19
  **Fix**: (`http1`) fixes a race-condition between the `on_ready` and `on_data` events, that could result in the `on_data` event being called twice instead of once (only possible with some clients). On multi-threaded workers, this could result in the CPU spinning while the task lock remains busy. Credit to Néstor Coppi (@Shelvak) for exposing the issue and providing an example application with detailed reports. Issue #75.
@@ -25,6 +25,10 @@ Servers that publish WebSocket and/or EventSource (SSE) support using the `env['
25
25
 
26
26
  This document reserves the Rack `env` Hash keys of `rack.upgrade?` and `rack.upgrade`.
27
27
 
28
+ A conforming server MUST set `env['rack.upgrade?']` to `:websocket` for incoming WebSocket connections and `:sse` for incoming EventSource (SSE) connections.
29
+
30
+ If a connection is not "upgradable", a conforming server SHOULD set `env['rack.upgrade?']` to either `nil` or `false`.
31
+
28
32
  The historical use of `upgrade.websocket?` and `upgrade.websocket` (iodine 0.4.x) will be gradually deprecated.
29
33
 
30
34
  ### The WebSocket / EventSource Callback Object
@@ -82,7 +86,7 @@ The server **MUST** provide the Callback Object with a `client` object, that sup
82
86
 
83
87
  \*Servers that divide large messages into a number of smaller messages (implement message fragmentation) MAY count each fragment separately, as if the fragmentation was performed by the user and `write` was called more than once per message.
84
88
 
85
- * `env` (iodine specific for now) returns the Rack `env` hash related to the originating HTTP request. Some changes to the `env` hash (such as removal of the IO hijacking support) might be required by the implementation.
89
+ * `env` returns the Rack `env` hash related to the originating HTTP request. Some changes to the `env` hash (such as removal of the IO hijacking support) MAY be implemented by the server.
86
90
 
87
91
  WebSocket `ping` / `pong`, timeouts and network considerations should be implemented by the server. It is **RECOMMENDED** (but not required) that the server send `ping`s to prevent connection timeouts and detect network failure.
88
92
 
@@ -1,5 +1,6 @@
1
1
  require 'iodine'
2
2
  require 'json'
3
+ require 'rack'
3
4
 
4
5
  module ShootoutApp
5
6
  # the default HTTP response
data/exe/iodine CHANGED
@@ -1,11 +1,209 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ require 'iodine'
4
+
5
+ # Load Rack if available (assume it will be used).
6
+ #
7
+ # Remember, code costs memory. Duplicating the Rack::Builder module and functions can be avoided.
2
8
  begin
3
9
  require 'rack'
10
+ module Iodine
11
+ module Base
12
+ module Rack
13
+ Builder = ::Rack::Builder
14
+ end
15
+ end
16
+ end
4
17
  rescue LoadError
5
- puts "ERROR: the iodine command-line utility requires the gem: rack."
6
- exit(0)
18
+ module Iodine
19
+ module Base
20
+ module Rack
21
+ # The Rack::Builder code is used when Rack isn't available.
22
+ #
23
+ # The code was copied (with minor adjustments) from the Rack source code and is licensed under the MIT license.
24
+ # Copyright (C) 2007-2019 Leah Neukirchen <http://leahneukirchen.org/infopage.html>
25
+ #
26
+ # ====
27
+ #
28
+ # Rack::Builder implements a small DSL to iteratively construct Rack
29
+ # applications.
30
+ #
31
+ # Example:
32
+ #
33
+ # require 'rack/lobster'
34
+ # app = Rack::Builder.new do
35
+ # use Rack::CommonLogger
36
+ # use Rack::ShowExceptions
37
+ # map "/lobster" do
38
+ # use Rack::Lint
39
+ # run Rack::Lobster.new
40
+ # end
41
+ # end
42
+ #
43
+ # run app
44
+ #
45
+ # Or
46
+ #
47
+ # app = Rack::Builder.app do
48
+ # use Rack::CommonLogger
49
+ # run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] }
50
+ # end
51
+ #
52
+ # run app
53
+ #
54
+ # +use+ adds middleware to the stack, +run+ dispatches to an application.
55
+ # You can use +map+ to construct a Rack::URLMap in a convenient way.
56
+ class Builder
57
+ # https://stackoverflow.com/questions/2223882/whats-the-difference-between-utf-8-and-utf-8-without-bom
58
+ UTF_8_BOM = '\xef\xbb\xbf'
59
+
60
+ def self.parse_file(config, opts = Hash.new)
61
+ if config =~ /\.ru$/
62
+ return self.load_file(config, opts)
63
+ else
64
+ require config
65
+ app = Object.const_get(::File.basename(config, '.rb').split('_').map(&:capitalize).join(''))
66
+ return app, {}
67
+ end
68
+ end
69
+
70
+ def self.load_file(path, options = Hash.new)
71
+ cfgfile = ::File.read(path)
72
+ cfgfile.slice!(/\A#{UTF_8_BOM}/) if cfgfile.encoding == Encoding::UTF_8
73
+
74
+ cfgfile.sub!(/^__END__\n.*\Z/m, '')
75
+ app = new_from_string cfgfile, path
76
+
77
+ return app, options
78
+ end
79
+
80
+ def self.new_from_string(builder_script, file = "(rackup)")
81
+ eval "Iodine::Base::Rack::Builder.new {\n" + builder_script + "\n}.to_app",
82
+ TOPLEVEL_BINDING, file, 0
83
+ end
84
+
85
+ def initialize(default_app = nil, &block)
86
+ @use, @map, @run, @warmup, @freeze_app = [], nil, default_app, nil, false
87
+ instance_eval(&block) if block_given?
88
+ end
89
+
90
+ def self.app(default_app = nil, &block)
91
+ self.new(default_app, &block).to_app
92
+ end
93
+
94
+ # Specifies middleware to use in a stack.
95
+ #
96
+ # class Middleware
97
+ # def initialize(app)
98
+ # @app = app
99
+ # end
100
+ #
101
+ # def call(env)
102
+ # env["rack.some_header"] = "setting an example"
103
+ # @app.call(env)
104
+ # end
105
+ # end
106
+ #
107
+ # use Middleware
108
+ # run lambda { |env| [200, { "Content-Type" => "text/plain" }, ["OK"]] }
109
+ #
110
+ # All requests through to this application will first be processed by the middleware class.
111
+ # The +call+ method in this example sets an additional environment key which then can be
112
+ # referenced in the application if required.
113
+ def use(middleware, *args, &block)
114
+ if @map
115
+ mapping, @map = @map, nil
116
+ @use << proc { |app| generate_map(app, mapping) }
117
+ end
118
+ @use << proc { |app| middleware.new(app, *args, &block) }
119
+ end
120
+
121
+ # Takes an argument that is an object that responds to #call and returns a Rack response.
122
+ # The simplest form of this is a lambda object:
123
+ #
124
+ # run lambda { |env| [200, { "Content-Type" => "text/plain" }, ["OK"]] }
125
+ #
126
+ # However this could also be a class:
127
+ #
128
+ # class Heartbeat
129
+ # def self.call(env)
130
+ # [200, { "Content-Type" => "text/plain" }, ["OK"]]
131
+ # end
132
+ # end
133
+ #
134
+ # run Heartbeat
135
+ def run(app)
136
+ @run = app
137
+ end
138
+
139
+ # Takes a lambda or block that is used to warm-up the application.
140
+ #
141
+ # warmup do |app|
142
+ # client = Rack::MockRequest.new(app)
143
+ # client.get('/')
144
+ # end
145
+ #
146
+ # use SomeMiddleware
147
+ # run MyApp
148
+ def warmup(prc = nil, &block)
149
+ @warmup = prc || block
150
+ end
151
+
152
+ # Creates a route within the application.
153
+ #
154
+ # Rack::Builder.app do
155
+ # map '/' do
156
+ # run Heartbeat
157
+ # end
158
+ # end
159
+ #
160
+ # The +use+ method can also be used here to specify middleware to run under a specific path:
161
+ #
162
+ # Rack::Builder.app do
163
+ # map '/' do
164
+ # use Middleware
165
+ # run Heartbeat
166
+ # end
167
+ # end
168
+ #
169
+ # This example includes a piece of middleware which will run before requests hit +Heartbeat+.
170
+ #
171
+ def map(path, &block)
172
+ @map ||= {}
173
+ @map[path] = block
174
+ end
175
+
176
+ # Freeze the app (set using run) and all middleware instances when building the application
177
+ # in to_app.
178
+ def freeze_app
179
+ @freeze_app = true
180
+ end
181
+
182
+ def to_app
183
+ app = @map ? generate_map(@run, @map) : @run
184
+ fail "missing run or map statement" unless app
185
+ app.freeze if @freeze_app
186
+ app = @use.reverse.inject(app) { |a, e| e[a].tap { |x| x.freeze if @freeze_app } }
187
+ @warmup.call(app) if @warmup
188
+ app
189
+ end
190
+
191
+ def call(env)
192
+ to_app.call(env)
193
+ end
194
+
195
+ private
196
+
197
+ def generate_map(default_app, mapping)
198
+ mapped = default_app ? { '/' => default_app } : {}
199
+ mapping.each { |r, b| mapped[r] = self.class.new(default_app, &b).to_app }
200
+ URLMap.new(mapped)
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
7
206
  end
8
- require 'iodine'
9
207
 
10
208
  module Iodine
11
209
  # The Iodine::Base namespace is reserved for internal use and is NOT part of the public API.
@@ -15,7 +213,7 @@ module Iodine
15
213
 
16
214
  def self.try_file filename
17
215
  return nil unless File.exist? filename
18
- return ::Rack::Builder.parse_file filename
216
+ return Iodine::Base::Rack::Builder.parse_file filename
19
217
  end
20
218
 
21
219
  def self.get_app_opts
@@ -36,7 +234,7 @@ module Iodine
36
234
  puts "WARNING: Ruby application not found#{ filename ? " - missing both #{filename} and config.ru" : " - missing config.ru"}."
37
235
  if Iodine::DEFAULT_SETTINGS[:public]
38
236
  puts " Running only static file service."
39
- opt = ::Rack::Server::Options.new.parse!([])
237
+ opt = Hash.new
40
238
  app = Proc.new { [404, {}, "Not Found!"] }
41
239
  else
42
240
  puts "\nERROR: Couldn't run Ruby application, check command line arguments."
@@ -49,7 +247,7 @@ module Iodine
49
247
  end
50
248
 
51
249
  def self.perform_warmup(app)
52
- # load anything marked with `autoload`, since autoload isn't thread safe nor fork friendly.
250
+ # load anything marked with `autoload`, since autoload is niether thread safe nor fork friendly.
53
251
  Iodine.on_state(:on_start) do
54
252
  Module.constants.each do |n|
55
253
  begin
@@ -57,7 +255,7 @@ module Iodine
57
255
  rescue StandardError => _e
58
256
  end
59
257
  end
60
- ::Rack::Builder.new(app) do |r|
258
+ Iodine::Base::Rack::Builder.new(app) do |r|
61
259
  r.warmup do |a|
62
260
  client = ::Rack::MockRequest.new(a)
63
261
  client.get('/')
@@ -3815,7 +3815,7 @@ static void fio_worker_cleanup(void) {
3815
3815
  fio_state_callback_force(FIO_CALL_ON_FINISH);
3816
3816
  fio_defer_perform();
3817
3817
  if (!fio_data->is_worker) {
3818
- kill(0, SIGINT);
3818
+ fio_cluster_signal_children();
3819
3819
  while (wait(NULL) != -1)
3820
3820
  ;
3821
3821
  }
@@ -6202,7 +6202,7 @@ static void fio_cluster_listen_on_close(intptr_t uuid,
6202
6202
  (int)getpid());
6203
6203
  #endif
6204
6204
  if (fio_data->active)
6205
- kill(0, SIGINT);
6205
+ fio_stop();
6206
6206
  }
6207
6207
  (void)uuid;
6208
6208
  }
@@ -6244,7 +6244,6 @@ static void fio_cluster_client_handler(struct cluster_pr_s *pr) {
6244
6244
  break;
6245
6245
  case FIO_CLUSTER_MSG_SHUTDOWN:
6246
6246
  fio_stop();
6247
- kill(getpid(), SIGINT);
6248
6247
  case FIO_CLUSTER_MSG_ERROR: /* fallthrough */
6249
6248
  case FIO_CLUSTER_MSG_PING: /* fallthrough */
6250
6249
  case FIO_CLUSTER_MSG_ROOT: /* fallthrough */
@@ -6499,7 +6498,7 @@ static void fio_pubsub_on_fork(void) {
6499
6498
  /** Signals children (or self) to shutdown) - NOT signal safe. */
6500
6499
  static void fio_cluster_signal_children(void) {
6501
6500
  if (fio_parent_pid() != getpid()) {
6502
- kill(getpid(), SIGINT);
6501
+ fio_stop();
6503
6502
  return;
6504
6503
  }
6505
6504
  fio_cluster_server_sender(fio_msg_internal_create(0, FIO_CLUSTER_MSG_SHUTDOWN,
@@ -27,7 +27,8 @@ typedef struct {
27
27
  static void *iodine_handle_exception(void *ignr) {
28
28
  (void)ignr;
29
29
  VALUE exc = rb_errinfo();
30
- if (exc != Qnil) {
30
+ if (exc != Qnil && rb_respond_to(exc, rb_intern("message")) &&
31
+ rb_respond_to(exc, rb_intern("backtrace"))) {
31
32
  VALUE msg = rb_funcall2(exc, rb_intern("message"), 0, NULL);
32
33
  VALUE exc_class = rb_class_name(CLASS_OF(exc));
33
34
  VALUE bt = rb_funcall2(exc, rb_intern("backtrace"), 0, NULL);
@@ -46,6 +47,9 @@ static void *iodine_handle_exception(void *ignr) {
46
47
  rb_backtrace();
47
48
  FIO_LOG_ERROR("\n");
48
49
  rb_set_errinfo(Qnil);
50
+ } else if (exc != Qnil) {
51
+ FIO_LOG_ERROR(
52
+ "Iodine caught an unprotected exception - NO MESSAGE / DATA AVAILABLE");
49
53
  }
50
54
  return (void *)Qnil;
51
55
  }
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '0.7.32'.freeze
2
+ VERSION = '0.7.33'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iodine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.32
4
+ version: 0.7.33
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-06-01 00:00:00.000000000 Z
11
+ date: 2019-07-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -202,7 +202,7 @@ licenses:
202
202
  - MIT
203
203
  metadata:
204
204
  allowed_push_host: https://rubygems.org
205
- post_install_message: 'Thank you for installing Iodine 0.7.32.
205
+ post_install_message: 'Thank you for installing Iodine 0.7.33.
206
206
 
207
207
  '
208
208
  rdoc_options: []