itsi 0.2.10 → 0.2.11

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4c0be7500e24ba264247f8ea8a8d2e0e7ce1d69caf449cd194e3ac1c849cffb6
4
- data.tar.gz: 5dd34736783a2f8fe81f3ce3bd7626285ff8f364407248f0bbc5b74ba6c708dc
3
+ metadata.gz: 987c240269c5eeecf8ef25dece0a60076e364199fc04d9616b5b429da7745298
4
+ data.tar.gz: 24993f6fda6930e19550a74c7f2e26b08783e4d633cce3e10fd21a61250e4581
5
5
  SHA512:
6
- metadata.gz: 619d63e46c4286bf336cca1fad51e51dadf9eac57e106a1fd0493b00a838be36bbea16e6a96a5003870752afbba5c11123d9a42e02c43ee2994175f4273811cc
7
- data.tar.gz: 264db7920c1c628ad0c65827dd63d5aeae3ce7c1ccba7a8940ebfc478000b9d728e804cd14c5700bd831fcd7f906bed6db85d4dc298119119eeb65249ec2bb96
6
+ metadata.gz: 0be64ac78c963c680f65da8ea2fb309e726408f5983773576fc60f837c9d4721d89a1525dcf64b1f522a2a1ce842f4356ca586caee30efd886bd966d488a0a00
7
+ data.tar.gz: d770724704cb4f2ab988542572fbf5755c22282a8af7d81d92fe0517d7738dafb66cd7856d83e48b402a7e6412550d58360bdf12180ad14edef0c08c8062b257
data/Cargo.lock CHANGED
@@ -1644,7 +1644,7 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
1644
1644
 
1645
1645
  [[package]]
1646
1646
  name = "itsi-scheduler"
1647
- version = "0.2.10"
1647
+ version = "0.2.11"
1648
1648
  dependencies = [
1649
1649
  "bytes",
1650
1650
  "derive_more",
@@ -1662,7 +1662,7 @@ dependencies = [
1662
1662
 
1663
1663
  [[package]]
1664
1664
  name = "itsi-server"
1665
- version = "0.2.10"
1665
+ version = "0.2.11"
1666
1666
  dependencies = [
1667
1667
  "argon2",
1668
1668
  "async-channel",
data/LICENSE.txt CHANGED
@@ -19,9 +19,178 @@ Please contact: commercial@itsi.fyi
19
19
  --------------------------------------------------------------------------------
20
20
  GNU LESSER GENERAL PUBLIC LICENSE
21
21
  Version 3, 29 June 2007
22
+ --------------------------------------------------------------------------------
23
+ GNU LESSER GENERAL PUBLIC LICENSE
24
+ Version 3, 29 June 2007
25
+
26
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
27
+ Everyone is permitted to copy and distribute verbatim copies
28
+ of this license document, but changing it is not allowed.
29
+
30
+
31
+ This version of the GNU Lesser General Public License incorporates
32
+ the terms and conditions of version 3 of the GNU General Public
33
+ License, supplemented by the additional permissions listed below.
34
+
35
+ 0. Additional Definitions.
36
+
37
+ As used herein, "this License" refers to version 3 of the GNU Lesser
38
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
39
+ General Public License.
40
+
41
+ "The Library" refers to a covered work governed by this License,
42
+ other than an Application or a Combined Work as defined below.
43
+
44
+ An "Application" is any work that makes use of an interface provided
45
+ by the Library, but which is not otherwise based on the Library.
46
+ Defining a subclass of a class defined by the Library is deemed a mode
47
+ of using an interface provided by the Library.
48
+
49
+ A "Combined Work" is a work produced by combining or linking an
50
+ Application with the Library. The particular version of the Library
51
+ with which the Combined Work was made is also called the "Linked
52
+ Version".
53
+
54
+ The "Minimal Corresponding Source" for a Combined Work means the
55
+ Corresponding Source for the Combined Work, excluding any source code
56
+ for portions of the Combined Work that, considered in isolation, are
57
+ based on the Application, and not on the Linked Version.
58
+
59
+ The "Corresponding Application Code" for a Combined Work means the
60
+ object code and/or source code for the Application, including any data
61
+ and utility programs needed for reproducing the Combined Work from the
62
+ Application, but excluding the System Libraries of the Combined Work.
63
+
64
+ 1. Exception to Section 3 of the GNU GPL.
65
+
66
+ You may convey a covered work under sections 3 and 4 of this License
67
+ without being bound by section 3 of the GNU GPL.
68
+
69
+ 2. Conveying Modified Versions.
70
+
71
+ If you modify a copy of the Library, and, in your modifications, a
72
+ facility refers to a function or data to be supplied by an Application
73
+ that uses the facility (other than as an argument passed when the
74
+ facility is invoked), then you may convey a copy of the modified
75
+ version:
76
+
77
+ a) under this License, provided that you make a good faith effort to
78
+ ensure that, in the event an Application does not supply the
79
+ function or data, the facility still operates, and performs
80
+ whatever part of its purpose remains meaningful, or
81
+
82
+ b) under the GNU GPL, with none of the additional permissions of
83
+ this License applicable to that copy.
84
+
85
+ 3. Object Code Incorporating Material from Library Header Files.
86
+
87
+ The object code form of an Application may incorporate material from
88
+ a header file that is part of the Library. You may convey such object
89
+ code under terms of your choice, provided that, if the incorporated
90
+ material is not limited to numerical parameters, data structure
91
+ layouts and accessors, or small macros, inline functions and templates
92
+ (ten or fewer lines in length), you do both of the following:
93
+
94
+ a) Give prominent notice with each copy of the object code that the
95
+ Library is used in it and that the Library and its use are
96
+ covered by this License.
97
+
98
+ b) Accompany the object code with a copy of the GNU GPL and this license
99
+ document.
100
+
101
+ 4. Combined Works.
102
+
103
+ You may convey a Combined Work under terms of your choice that,
104
+ taken together, effectively do not restrict modification of the
105
+ portions of the Library contained in the Combined Work and reverse
106
+ engineering for debugging such modifications, if you also do each of
107
+ the following:
108
+
109
+ a) Give prominent notice with each copy of the Combined Work that
110
+ the Library is used in it and that the Library and its use are
111
+ covered by this License.
112
+
113
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
114
+ document.
115
+
116
+ c) For a Combined Work that displays copyright notices during
117
+ execution, include the copyright notice for the Library among
118
+ these notices, as well as a reference directing the user to the
119
+ copies of the GNU GPL and this license document.
120
+
121
+ d) Do one of the following:
122
+
123
+ 0) Convey the Minimal Corresponding Source under the terms of this
124
+ License, and the Corresponding Application Code in a form
125
+ suitable for, and under terms that permit, the user to
126
+ recombine or relink the Application with a modified version of
127
+ the Linked Version to produce a modified Combined Work, in the
128
+ manner specified by section 6 of the GNU GPL for conveying
129
+ Corresponding Source.
130
+
131
+ 1) Use a suitable shared library mechanism for linking with the
132
+ Library. A suitable mechanism is one that (a) uses at run time
133
+ a copy of the Library already present on the user's computer
134
+ system, and (b) will operate properly with a modified version
135
+ of the Library that is interface-compatible with the Linked
136
+ Version.
137
+
138
+ e) Provide Installation Information, but only if you would otherwise
139
+ be required to provide such information under section 6 of the
140
+ GNU GPL, and only to the extent that such information is
141
+ necessary to install and execute a modified version of the
142
+ Combined Work produced by recombining or relinking the
143
+ Application with a modified version of the Linked Version. (If
144
+ you use option 4d0, the Installation Information must accompany
145
+ the Minimal Corresponding Source and Corresponding Application
146
+ Code. If you use option 4d1, you must provide the Installation
147
+ Information in the manner specified by section 6 of the GNU GPL
148
+ for conveying Corresponding Source.)
149
+
150
+ 5. Combined Libraries.
151
+
152
+ You may place library facilities that are a work based on the
153
+ Library side by side in a single library together with other library
154
+ facilities that are not Applications and are not covered by this
155
+ License, and convey such a combined library under terms of your
156
+ choice, if you do both of the following:
157
+
158
+ a) Accompany the combined library with a copy of the same work based
159
+ on the Library, uncombined with any other library facilities,
160
+ conveyed under the terms of this License.
161
+
162
+ b) Give prominent notice with the combined library that part of it
163
+ is a work based on the Library, and explaining where to find the
164
+ accompanying uncombined form of the same work.
165
+
166
+ 6. Revised Versions of the GNU Lesser General Public License.
167
+
168
+ The Free Software Foundation may publish revised and/or new versions
169
+ of the GNU Lesser General Public License from time to time. Such new
170
+ versions will be similar in spirit to the present version, but may
171
+ differ in detail to address new problems or concerns.
172
+
173
+ Each version is given a distinguishing version number. If the
174
+ Library as you received it specifies that a certain numbered version
175
+ of the GNU Lesser General Public License "or any later version"
176
+ applies to it, you have the option of following the terms and
177
+ conditions either of that published version or of any later version
178
+ published by the Free Software Foundation. If the Library as you
179
+ received it does not specify a version number of the GNU Lesser
180
+ General Public License, you may choose any version of the GNU Lesser
181
+ General Public License ever published by the Free Software Foundation.
182
+
183
+ If the Library as you received it specifies that a proxy can decide
184
+ whether future versions of the GNU Lesser General Public License shall
185
+ apply, that proxy's public statement of acceptance of any version is
186
+ permanent authorization for you to choose that version for the
187
+ Library.
188
+
189
+
22
190
  --------------------------------------------------------------------------------
23
191
  GNU GENERAL PUBLIC LICENSE
24
192
  Version 3, 29 June 2007
193
+ --------------------------------------------------------------------------------
25
194
 
26
195
  Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
27
196
  Everyone is permitted to copy and distribute verbatim copies
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "itsi-scheduler"
3
- version = "0.2.10"
3
+ version = "0.2.11"
4
4
  edition = "2021"
5
5
  authors = ["Wouter Coppieters <wc@pico.net.nz>"]
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "itsi-server"
3
- version = "0.2.10"
3
+ version = "0.2.11"
4
4
  edition = "2021"
5
5
  authors = ["Wouter Coppieters <wc@pico.net.nz>"]
6
6
  license = "MIT"
@@ -86,6 +86,8 @@ fn init(ruby: &Ruby) -> Result<()> {
86
86
  response.define_method("<<", method!(ItsiHttpResponse::send_frame, 1))?;
87
87
  response.define_method("write", method!(ItsiHttpResponse::send_frame, 1))?;
88
88
  response.define_method("read", method!(ItsiHttpResponse::recv_frame, 0))?;
89
+ response.define_method("flush", method!(ItsiHttpResponse::flush, 0))?;
90
+ response.define_method("closed?", method!(ItsiHttpResponse::is_closed, 0))?;
89
91
  response.define_method(
90
92
  "send_and_close",
91
93
  method!(ItsiHttpResponse::send_and_close, 1),
@@ -272,6 +272,14 @@ impl ItsiHttpResponse {
272
272
  // not implemented
273
273
  }
274
274
 
275
+ pub fn flush(&self) {
276
+ // no-op
277
+ }
278
+
279
+ pub fn is_closed(&self) -> bool {
280
+ self.data.response_writer.write().is_none()
281
+ }
282
+
275
283
  pub fn send_and_close(&self, frame: Bytes) -> MagnusResult<()> {
276
284
  let result = self.send_frame_into(ByteFrame::End(frame), &self.data.response_writer);
277
285
  self.data.response_writer.write().take();
@@ -273,9 +273,9 @@ Note that despite the header being named `X-Sendfile`, Itsi does not use the Sen
273
273
  {{% /details %}}
274
274
 
275
275
  {{% details title="Graceful Memory Limits" closed="true" %}}
276
- * Itsi allows you to specify memory limits for Ruby processes. When the limit is reached, Itsi gracefully terminates the process and also invokes a dedicated `after_memory_threshold_reached` callback,
276
+ * Itsi allows you to specify memory limits for Ruby processes. When the limit is reached, Itsi gracefully terminates the process and also invokes a dedicated `after_memory_limit_reached` callback,
277
277
  so that you can log the event for further analysis.
278
- * See <a target="_blank" href="/options/worker_memory_limit">worker_memory_limit</a> and <a target="_blank" href="/options/after_memory_threshold_reached">after_memory_threshold_reached</a>.
278
+ * See <a target="_blank" href="/options/worker_memory_limit">worker_memory_limit</a> and <a target="_blank" href="/options/after_memory_limit_reached">after_memory_limit_reached</a>.
279
279
  {{% /details %}}
280
280
 
281
281
  {{% details title="OOB GC" closed="true" %}}
@@ -213,7 +213,7 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
213
213
 
214
214
  [[package]]
215
215
  name = "itsi-scheduler"
216
- version = "0.2.10"
216
+ version = "0.2.11"
217
217
  dependencies = [
218
218
  "bytes",
219
219
  "derive_more",
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Itsi
4
4
  class Scheduler
5
- VERSION = "0.2.10"
5
+ VERSION = "0.2.11"
6
6
  end
7
7
  end
@@ -4,7 +4,35 @@ require_relative "scheduler/version"
4
4
  require_relative "scheduler/itsi_scheduler"
5
5
 
6
6
  module Itsi
7
+ module ScheduleUtils
8
+ def schedule(&block)
9
+ return to_enum(:schedule) unless block_given?
10
+
11
+ Object.schedule do
12
+ each { |item| Object.schedule { block.call(item) } }
13
+ end
14
+ end
15
+ end
16
+
7
17
  class Scheduler
18
+ def self.enable_schedule_keyword!
19
+ Object.define_method(:schedule) do |&blk|
20
+ result = nil
21
+ return result unless blk
22
+
23
+ if Fiber.scheduler.nil?
24
+ Thread.new do
25
+ Fiber.set_scheduler Itsi::Scheduler.new
26
+ Fiber.schedule { result = blk[] }
27
+ end.join
28
+ else
29
+ Fiber.schedule { result = blk[] }
30
+ end
31
+ result
32
+ end
33
+ Enumerable.include(ScheduleUtils)
34
+ end
35
+
8
36
  class Error < StandardError; end
9
37
 
10
38
  def self.resume_token
@@ -1644,7 +1644,7 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
1644
1644
 
1645
1645
  [[package]]
1646
1646
  name = "itsi-server"
1647
- version = "0.2.10"
1647
+ version = "0.2.11"
1648
1648
  dependencies = [
1649
1649
  "argon2",
1650
1650
  "async-channel",
@@ -11,7 +11,8 @@ module Itsi
11
11
  include ResponseStatusShortcodes
12
12
  attr_accessor :hijacked
13
13
 
14
- EMPTY_IO = StringIO.new("")
14
+ EMPTY_IO = StringIO.new("").tap { |io| io.set_encoding(Encoding::ASCII_8BIT) }
15
+
15
16
  RACK_HEADER_MAP = StandardHeaders::ALL.map do |header|
16
17
  rack_form = if header == "content-type"
17
18
  "CONTENT_TYPE"
@@ -49,9 +50,7 @@ module Itsi
49
50
  "rack.version" => nil,
50
51
  "rack.url_scheme" => "",
51
52
  "rack.input" => "",
52
- "rack.hijack" => "",
53
- "CONTENT_TYPE" => nil,
54
- "CONTENT_LENGTH" => nil
53
+ "rack.hijack" => ""
55
54
  }.freeze
56
55
 
57
56
  def to_rack_env
@@ -75,20 +74,20 @@ module Itsi
75
74
  env["rack.hijack"] = method(:hijack)
76
75
  headers.each do |(k, v)|
77
76
  env[case k
78
- when "content-type" then "CONTENT_TYPE"
79
- when "content-length" then "CONTENT_LENGTH"
80
- when "accept" then "HTTP_ACCEPT"
81
- when "accept-encoding" then "HTTP_ACCEPT_ENCODING"
82
- when "accept-language" then "HTTP_ACCEPT_LANGUAGE"
83
- when "user-agent" then "HTTP_USER_AGENT"
84
- when "referer" then "HTTP_REFERER"
85
- when "origin" then "HTTP_ORIGIN"
86
- when "cookie" then "HTTP_COOKIE"
87
- when "authorization" then "HTTP_AUTHORIZATION"
88
- when "x-forwarded-for" then "HTTP_X_FORWARDED_FOR"
89
- when "x-forwarded-proto" then "HTTP_X_FORWARDED_PROTO"
90
- else RACK_HEADER_MAP[k]
91
- end
77
+ when "content-type" then "CONTENT_TYPE"
78
+ when "content-length" then "CONTENT_LENGTH"
79
+ when "accept" then "HTTP_ACCEPT"
80
+ when "accept-encoding" then "HTTP_ACCEPT_ENCODING"
81
+ when "accept-language" then "HTTP_ACCEPT_LANGUAGE"
82
+ when "user-agent" then "HTTP_USER_AGENT"
83
+ when "referer" then "HTTP_REFERER"
84
+ when "origin" then "HTTP_ORIGIN"
85
+ when "cookie" then "HTTP_COOKIE"
86
+ when "authorization" then "HTTP_AUTHORIZATION"
87
+ when "x-forwarded-for" then "HTTP_X_FORWARDED_FOR"
88
+ when "x-forwarded-proto" then "HTTP_X_FORWARDED_PROTO"
89
+ else RACK_HEADER_MAP[k]
90
+ end
92
91
  ] = v
93
92
  end
94
93
  env
@@ -154,8 +153,8 @@ module Itsi
154
153
  def build_input_io
155
154
  case body_parts
156
155
  when nil then EMPTY_IO
157
- when String then StringIO.new(body_parts)
158
- when Array then File.open(body_parts.first, "rb")
156
+ when String then StringIO.new(body_parts).tap { |io| io.set_encoding(Encoding::ASCII_8BIT) }
157
+ when Array then File.open(body_parts.first, "rb").tap { |io| io.set_encoding(Encoding::ASCII_8BIT) }
159
158
  else body_parts
160
159
  end
161
160
  end
@@ -5,9 +5,10 @@ prev: deny_list/
5
5
  next: controller/
6
6
  ---
7
7
 
8
+ > If you're after running a rack app using a fully-featured framework, e.g. a Ruby on Rails or Sinatra, take a look at the [Rackup File](/middleware/rackup_file) middleware instead.
9
+
8
10
  The **endpoint** middleware allows you to define an ultra light-weight, inline, Ruby endpoint.
9
11
 
10
- > If you're after running a rack app using a fully-featured framework, e.g. a Ruby on Rails or Sinatra, take a look at the [Rackup File](/middleware/rackup_file) middleware instead.
11
12
  This feature can be useful for quickly prototyping, building small pieces of isolated functionality, or minimal endpoints where high throughput is essential.
12
13
 
13
14
  `endpoint` has several variants, that further restrict the endpoint to respond to specific HTTP methods:
@@ -9,11 +9,11 @@ Itsi can automatically generate TLS certificates for you, both in development an
9
9
  To automatically generate a TLS certificate in development, just bind using the `https` scheme.
10
10
  E.g.
11
11
  ```ruby {filename=Itsi.rb}
12
- bind "https://localhost"
12
+ bind "https://0.0.0.0"
13
13
 
14
14
  or
15
15
 
16
- bind "https://localhost:8443"
16
+ bind "https://0.0.0.0:8443"
17
17
  ```
18
18
  Itsi will generate a local development CA for you if it does not yet exist, then use this to
19
19
  sign a just-in-time certificate for your server.
@@ -3,7 +3,7 @@ title: Scheduler Threads
3
3
  url: /options/scheduler_threads
4
4
  ---
5
5
 
6
- You can explicitly spawn a pool of non-blocking scheduler threads and divide work across traditional blocking and non-blocking threads, using [location](/middleware/location) blocks.
6
+ You can explicitly spawn a pool of non-blocking scheduler threads and divide work across traditional/blocking and non-blocking threads, using [location](/middleware/location) blocks.
7
7
 
8
8
  This allows you to safely dip your toes into using non-blocking threads for specific I/O heavy operations without having to port an entire application to non-blocking I/O.
9
9
 
@@ -31,8 +31,8 @@ fiber_scheduler nil
31
31
 
32
32
  # If you bind to https, without specifying a certificate, Itsi will use a self-signed certificate.
33
33
  # The self-signed certificate will use a CA generated for your host and stored inside `ITSI_LOCAL_CA_DIR` (Defaults to ~/.itsi)
34
- # bind "https://localhost:3000"
35
- # bind "https://localhost:3000?domains=dev.itsi.fyi"
34
+ # bind "https://0.0.0.0:3000"
35
+ # bind "https://0.0.0.0:3000?domains=dev.itsi.fyi"
36
36
  #
37
37
  # If you want to use let's encrypt to generate you a real certificate you and pass cert=acme and an acme_email address to generate one.
38
38
  # bind "https://itsi.fyi?cert=acme&acme_email=admin@itsi.fyi"
@@ -8,10 +8,11 @@ module Rack
8
8
  port = options.fetch(:Port, 3001)
9
9
  ::Itsi::Server.start(
10
10
  {
11
- app: app,
12
11
  binds: ["http://#{host}:#{port}"]
13
12
  }
14
- )
13
+ ) do
14
+ run app
15
+ end
15
16
  end
16
17
  end
17
18
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Itsi
4
4
  class Server
5
- VERSION = "0.2.10"
5
+ VERSION = "0.2.11"
6
6
  end
7
7
  end
@@ -3,6 +3,7 @@
3
3
  ENV["ITSI_LOG"] = "off"
4
4
 
5
5
  require "minitest/reporters"
6
+ require "rackup"
6
7
  require "itsi/server"
7
8
  require "itsi/scheduler"
8
9
  require "socket"
@@ -24,7 +25,9 @@ def free_bind(protocol = "http", unix_socket: false)
24
25
  end
25
26
  end
26
27
 
27
- def server(app: nil, protocol: "http", bind: free_bind(protocol), itsi_rb: nil, cleanup: true, timeout: 5, &blk)
28
+ def server(app: nil, app_with_lint: nil, protocol: "http", bind: free_bind(protocol), itsi_rb: nil, cleanup: true,
29
+ timeout: 5, &blk)
30
+ app ||= Rack::Lint.new(app_with_lint) if app_with_lint
28
31
  itsi_rb ||= lambda do
29
32
  # Inline Itsi.rb
30
33
  bind bind
@@ -6,25 +6,25 @@ class TestRackServer < Minitest::Test
6
6
  end
7
7
 
8
8
  def test_hello_world
9
- server(app: lambda do |env|
10
- [200, { "Content-Type" => "text/plain" }, ["Hello, World!"]]
9
+ server(app_with_lint: lambda do |env|
10
+ [200, { "content-type" => "text/plain" }, ["Hello, World!"]]
11
11
  end) do
12
12
  assert_equal "Hello, World!", get("/")
13
13
  end
14
14
  end
15
15
 
16
16
  def test_post
17
- server(app: lambda do |env|
17
+ server(app_with_lint: lambda do |env|
18
18
  assert_equal env["REQUEST_METHOD"], "POST"
19
19
  assert_equal "data", env["rack.input"].read
20
- [200, { "Content-Type" => "text/plain" }, ["Hello, World!"]]
20
+ [200, { "content-type" => "text/plain" }, ["Hello, World!"]]
21
21
  end) do
22
22
  assert_equal "Hello, World!", post("/", "data").body
23
23
  end
24
24
  end
25
25
 
26
26
  def test_full_hijack
27
- server(app: lambda do |env|
27
+ server(app_with_lint: lambda do |env|
28
28
  io = env["rack.hijack"].call
29
29
  io.write("HTTP/1.1 200 Ok\r\n")
30
30
  io.write("Content-Type: text/plain\r\n")
@@ -36,14 +36,15 @@ class TestRackServer < Minitest::Test
36
36
  io.write("World!\r\n")
37
37
  io.write("0\r\n\r\n")
38
38
  io.close
39
+ [200, {}, []]
39
40
  end) do
40
41
  assert_equal "Hello, World!", get("/")
41
42
  end
42
43
  end
43
44
 
44
45
  def test_streaming_body
45
- server(app: lambda do |env|
46
- [200, { "Content-Type" => "text/plain" }, lambda { |stream|
46
+ server(app_with_lint: lambda do |env|
47
+ [200, { "content-type" => "text/plain" }, lambda { |stream|
47
48
  stream.write("Hello")
48
49
  stream.write(", World!")
49
50
  stream.close
@@ -54,8 +55,8 @@ class TestRackServer < Minitest::Test
54
55
  end
55
56
 
56
57
  def test_partial_hijack
57
- server(app: lambda do |env|
58
- [200, { "Content-Type" => "text/plain", "rack.hijack" => lambda { |stream|
58
+ server(app_with_lint: lambda do |env|
59
+ [200, { "content-type" => "text/plain", "rack.hijack" => lambda { |stream|
59
60
  stream.write("Hello")
60
61
  stream.write(", World!")
61
62
  stream.close
@@ -66,8 +67,8 @@ class TestRackServer < Minitest::Test
66
67
  end
67
68
 
68
69
  def test_enumerable_body
69
- server(app: lambda do |env|
70
- [200, { "Content-Type" => "application/json" },
70
+ server(app_with_lint: lambda do |env|
71
+ [200, { "content-type" => "application/json" },
71
72
  %W[one\n two\n three\n]]
72
73
  end) do
73
74
  assert_equal "one\ntwo\nthree\n", get("/")
@@ -82,7 +83,7 @@ class TestRackServer < Minitest::Test
82
83
  fiber_scheduler "Itsi::Scheduler"
83
84
  run(lambda do |env|
84
85
  sleep 0.25
85
- [200, { "Content-Type" => "text/plain" }, "Response: #{env["PATH_INFO"][1..-1]}"]
86
+ [200, { "content-type" => "text/plain" }, "Response: #{env["PATH_INFO"][1..-1]}"]
86
87
  end)
87
88
  end
88
89
  ) do
@@ -100,17 +101,17 @@ class TestRackServer < Minitest::Test
100
101
  end
101
102
 
102
103
  def test_query_params
103
- server(app: lambda do |env|
104
- [200, { "Content-Type" => "text/plain" }, [env["QUERY_STRING"]]]
104
+ server(app_with_lint: lambda do |env|
105
+ [200, { "content-type" => "text/plain" }, [env["QUERY_STRING"]]]
105
106
  end) do
106
107
  assert_equal "foo=bar&baz=qux", get("/?foo=bar&baz=qux")
107
108
  end
108
109
  end
109
110
 
110
111
  def test_put_request
111
- server(app: lambda do |env|
112
+ server(app_with_lint: lambda do |env|
112
113
  body = env["rack.input"].read
113
- [200, { "Content-Type" => "text/plain" }, [body]]
114
+ [200, { "content-type" => "text/plain" }, [body]]
114
115
  end) do |uri|
115
116
  req = Net::HTTP::Put.new(uri)
116
117
  req.body = "put data"
@@ -120,9 +121,9 @@ class TestRackServer < Minitest::Test
120
121
  end
121
122
 
122
123
  def test_custom_headers
123
- server(app: lambda do |env|
124
+ server(app_with_lint: lambda do |env|
124
125
  header = env["HTTP_X_CUSTOM"] || ""
125
- [200, { "Content-Type" => "text/plain" }, [header]]
126
+ [200, { "content-type" => "text/plain" }, [header]]
126
127
  end) do |uri|
127
128
  req = Net::HTTP::Get.new(uri)
128
129
  req["X-Custom"] = "custom-value"
@@ -134,7 +135,7 @@ class TestRackServer < Minitest::Test
134
135
  def test_error_response
135
136
  response = nil
136
137
  capture_subprocess_io do
137
- server(app: lambda do |env|
138
+ server(app_with_lint: lambda do |env|
138
139
  raise "Intentional error for testing"
139
140
  end) do
140
141
  response = get_resp("/")
@@ -144,8 +145,8 @@ class TestRackServer < Minitest::Test
144
145
  end
145
146
 
146
147
  def test_redirect
147
- server(app: lambda do |env|
148
- [302, { "Location" => "http://example.com" }, []]
148
+ server(app_with_lint: lambda do |env|
149
+ [302, { "location" => "http://example.com" }, []]
149
150
  end) do
150
151
  response = get_resp("/")
151
152
  assert_equal "302", response.code
@@ -154,11 +155,11 @@ class TestRackServer < Minitest::Test
154
155
  end
155
156
 
156
157
  def test_not_found
157
- server(app: lambda do |env|
158
+ server(app_with_lint: lambda do |env|
158
159
  if env["PATH_INFO"] == "/"
159
- [200, { "Content-Type" => "text/plain" }, ["Home"]]
160
+ [200, { "content-type" => "text/plain" }, ["Home"]]
160
161
  else
161
- [404, { "Content-Type" => "text/plain" }, ["Not Found"]]
162
+ [404, { "content-type" => "text/plain" }, ["Not Found"]]
162
163
  end
163
164
  end) do
164
165
  response = get_resp("/nonexistent")
@@ -168,8 +169,8 @@ class TestRackServer < Minitest::Test
168
169
  end
169
170
 
170
171
  def test_head_request
171
- server(app: lambda do |env|
172
- [200, { "Content-Type" => "text/plain", "Content-Length" => "13" }, ["Hello, World!"]]
172
+ server(app_with_lint: lambda do |env|
173
+ [200, { "content-type" => "text/plain", "content-length" => "13" }, []]
173
174
  end) do
174
175
  response = head("/")
175
176
  assert_equal "200", response.code
@@ -179,8 +180,8 @@ class TestRackServer < Minitest::Test
179
180
  end
180
181
 
181
182
  def test_options_request
182
- server(app: lambda do |env|
183
- [200, { "Allow" => "GET,POST,OPTIONS", "Content-Type" => "text/plain" }, ["Options Response"]]
183
+ server(app_with_lint: lambda do |env|
184
+ [200, { "allow" => "GET,POST,OPTIONS", "content-type" => "text/plain" }, ["Options Response"]]
184
185
  end) do
185
186
  response = options("/")
186
187
  assert_equal "200", response.code
@@ -190,8 +191,8 @@ class TestRackServer < Minitest::Test
190
191
  end
191
192
 
192
193
  def test_cookie_handling
193
- server(app: lambda do |env|
194
- [200, { "Content-Type" => "text/plain", "Set-Cookie" => "session=abc123; Path=/" }, ["Cookie Test"]]
194
+ server(app_with_lint: lambda do |env|
195
+ [200, { "content-type" => "text/plain", "set-cookie" => "session=abc123; Path=/" }, ["Cookie Test"]]
195
196
  end) do
196
197
  response = get_resp("/")
197
198
  assert_equal "200", response.code
@@ -201,8 +202,8 @@ class TestRackServer < Minitest::Test
201
202
  end
202
203
 
203
204
  def test_multiple_headers
204
- server(app: lambda do |env|
205
- [200, { "Content-Type" => "text/plain", "X-Example" => "one, two, three" }, ["Multiple Headers"]]
205
+ server(app_with_lint: lambda do |env|
206
+ [200, { "content-type" => "text/plain", "x-example" => "one, two, three" }, ["Multiple Headers"]]
206
207
  end) do
207
208
  response = get_resp("/")
208
209
  assert_equal "200", response.code
@@ -213,8 +214,8 @@ class TestRackServer < Minitest::Test
213
214
 
214
215
  def test_large_body
215
216
  large_text = "A" * 10_000
216
- server(app: lambda do |env|
217
- [200, { "Content-Type" => "text/plain", "Content-Length" => large_text.bytesize.to_s }, [large_text]]
217
+ server(app_with_lint: lambda do |env|
218
+ [200, { "content-type" => "text/plain", "content-length" => large_text.bytesize.to_s }, [large_text]]
218
219
  end) do
219
220
  response = get_resp("/")
220
221
  assert_equal "200", response.code
@@ -223,8 +224,8 @@ class TestRackServer < Minitest::Test
223
224
  end
224
225
 
225
226
  def test_custom_status_code
226
- server(app: lambda do |env|
227
- [201, { "Content-Type" => "text/plain" }, ["Created"]]
227
+ server(app_with_lint: lambda do |env|
228
+ [201, { "content-type" => "text/plain" }, ["Created"]]
228
229
  end) do
229
230
  response = get_resp("/")
230
231
  assert_equal "201", response.code
@@ -233,8 +234,8 @@ class TestRackServer < Minitest::Test
233
234
  end
234
235
 
235
236
  def test_empty_body
236
- server(app: lambda do |env|
237
- [204, { "Content-Type" => "text/plain" }, []]
237
+ server(app_with_lint: lambda do |env|
238
+ [204, {}, []]
238
239
  end) do
239
240
  response = get_resp("/")
240
241
  assert_equal "204", response.code
@@ -244,8 +245,8 @@ class TestRackServer < Minitest::Test
244
245
 
245
246
  def test_utf8_response
246
247
  utf8_text = "こんにちは世界"
247
- server(app: lambda do |env|
248
- [200, { "Content-Type" => "text/plain; charset=utf-8" }, [utf8_text]]
248
+ server(app_with_lint: lambda do |env|
249
+ [200, { "content-type" => "text/plain; charset=utf-8" }, [utf8_text]]
249
250
  end) do
250
251
  response = get_resp("/")
251
252
  assert_equal "200", response.code
@@ -254,9 +255,9 @@ class TestRackServer < Minitest::Test
254
255
  end
255
256
 
256
257
  def test_custom_request_header
257
- server(app: lambda do |env|
258
+ server(app_with_lint: lambda do |env|
258
259
  header_value = env["HTTP_X_MY_HEADER"] || ""
259
- [200, { "Content-Type" => "text/plain" }, [header_value]]
260
+ [200, { "content-type" => "text/plain" }, [header_value]]
260
261
  end) do |uri|
261
262
  req = Net::HTTP::Get.new(uri)
262
263
  req["X-My-Header"] = "test-header"
@@ -266,16 +267,16 @@ class TestRackServer < Minitest::Test
266
267
  end
267
268
 
268
269
  def test_url_encoded_query_params
269
- server(app: lambda do |env|
270
- [200, { "Content-Type" => "text/plain" }, [env["QUERY_STRING"]]]
270
+ server(app_with_lint: lambda do |env|
271
+ [200, { "content-type" => "text/plain" }, [env["QUERY_STRING"]]]
271
272
  end) do
272
273
  assert_equal "param=%C3%A9", get("/?param=%C3%A9")
273
274
  end
274
275
  end
275
276
 
276
- def test_https
277
- server(app: lambda do |env|
278
- [200, { "Content-Type" => "text/plain" }, ["Hello, HTTPS!"]]
277
+ def test_rackup_handler
278
+ server(app_with_lint: lambda do |env|
279
+ [200, { "content-type" => "text/plain" }, ["Hello, HTTPS!"]]
279
280
  end, protocol: "https") do |uri|
280
281
  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true,
281
282
  verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http|
@@ -285,4 +286,24 @@ class TestRackServer < Minitest::Test
285
286
  assert_equal "Hello, HTTPS!", response.body
286
287
  end
287
288
  end
289
+
290
+ # Used by `rails -s` and other tools using the rack-up interface.
291
+ def test_rackup_handler
292
+ host, port = free_bind.split(%r{:/?/?}).last(2)
293
+ app = ->(_) { [200, { "content-type" => "text/plain" }, ["Hello, Rackup!"]] }
294
+
295
+ Thread.new do
296
+ Rack::Handler::Itsi.run(
297
+ app,
298
+ {
299
+ host: host,
300
+ Port: port
301
+ }
302
+ )
303
+ end
304
+
305
+ sleep 0.25
306
+ assert_equal Net::HTTP.get(URI("http://#{host}:#{port}")), "Hello, Rackup!"
307
+ Process.kill(:SIGINT, Process.pid)
308
+ end
288
309
  end
data/lib/itsi/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Itsi
2
- VERSION = '0.2.10'
2
+ VERSION = '0.2.11'
3
3
  end
data/tasks.txt CHANGED
@@ -1,6 +1,3 @@
1
- - Fix crash during reload on Linux
2
- - Fix orphaned processes on linux (Maybe need to kill watcher?)
3
-
4
1
  Tasks:
5
2
  - Add way to opt non Itsi.rb files into LSP
6
3
  - Better parallelization a lá Iodine
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: itsi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.10
4
+ version: 0.2.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wouter Coppieters
@@ -15,28 +15,28 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: 0.2.10
18
+ version: 0.2.11
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: 0.2.10
25
+ version: 0.2.11
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: itsi-server
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: 0.2.10
32
+ version: 0.2.11
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 0.2.10
39
+ version: 0.2.11
40
40
  description: Wrapper Gem for both the Itsi server and the Itsi Fiber scheduler
41
41
  email:
42
42
  - wc@pico.net.nz
@@ -672,7 +672,6 @@ files:
672
672
  - gems/scheduler/.rubocop.yml
673
673
  - gems/scheduler/Cargo.lock
674
674
  - gems/scheduler/Cargo.toml
675
- - gems/scheduler/README.md
676
675
  - gems/scheduler/Rakefile
677
676
  - gems/scheduler/bin/console
678
677
  - gems/scheduler/bin/setup
@@ -695,7 +694,6 @@ files:
695
694
  - gems/server/.rubocop.yml
696
695
  - gems/server/Cargo.lock
697
696
  - gems/server/Cargo.toml
698
- - gems/server/README.md
699
697
  - gems/server/Rakefile
700
698
  - gems/server/bin/console
701
699
  - gems/server/bin/setup
@@ -1,76 +0,0 @@
1
- ---
2
- title: Itsi Scheduler
3
- type: docs
4
- weight: 4
5
- sidebar:
6
- exclude: true
7
- ---
8
- <img src="itsi-scheduler-100.png" width="80px" style="display: block; margin-left: auto; margin-right: auto;">
9
-
10
- `Itsi Scheduler` is an implementation of a Ruby [Fiber Scheduler](https://docs.ruby-lang.org/en/3.2/Fiber/Scheduler.html).
11
-
12
- When combined with Itsi Server, you can write endpoints that look just like regular synchronous Ruby code. Behind the scenes, the scheduler will transparently pause and resume fibers to prevent threads from blocking, greatly increasing throughput for I/O-heavy workloads
13
-
14
- If you're purely after a lightweight, yet efficient Ruby scheduler,
15
- you can use Itsi Scheduler as a standalone scheduler for any Ruby application.
16
-
17
- Just use `Fiber.set_scheduler` to set an instance `Itsi::Scheduler` as a scheduler to opt in to this IO weaving behavior
18
- *automatically* for all blocking IO.
19
-
20
- ### Primer on Fiber Schedulers
21
-
22
- Fiber schedulers are a way to automatically manage the execution of non-blocking fibers in Ruby. A scheduler is responsible for the automatic pausing and resumption of Fibers based
23
- on whether or not they are awaiting IO operations.
24
- Ruby's Fiber scheduler implementation automatically invokes the current Fiber scheduler (if it exists) for each blocking operation, allowing it to seamlessly drive the execution of huge numbers of simultaneous non-blocking fibers
25
- while ensuring the main thread is never blocked on IO.
26
-
27
- This behind the scenes magic allows Ruby to provide async IO (just like we find in languages with `async/await` like `Rust`, `C#`, `JavaScript`) *but* with the added beauty
28
- that synchronous and asynchronous code is identical! (I.e. Ruby's functions are [colorless](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/))
29
-
30
- ## Getting Started
31
- To install and use Itsi Scheduler follow the instructions below:
32
-
33
-
34
- ### 1 - Install Itsi Scheduler
35
-
36
- **Prerequisites**
37
-
38
- You'll need at least `build-essential` and `libclang-dev` installed to build Itsi on Linux.
39
- E.g.
40
- ```bash
41
- apt-get install build-essential libclang-dev
42
- ```
43
-
44
- Then use `gem` to install the Itsi package. This will in turn install both the
45
- `itsi-server` gem, and the `itsi-scheduler` gem.
46
-
47
-
48
- ```bash
49
- gem install itsi-scheduler
50
- ```
51
-
52
-
53
- ### 2 - Use Itsi Scheduler
54
-
55
- Great! You now have Itsi Scheduler installed.
56
- Now you can run code like this:
57
-
58
- ```ruby
59
- require 'itsi/scheduler'
60
- require 'socket'
61
- results = Thread.new do
62
- Fiber.set_scheduler Itsi::Scheduler.new
63
- results = []
64
- Fiber.schedule do
65
- results << Addrinfo.getaddrinfo("www.ruby-lang.org", 80, nil, :STREAM)
66
- end
67
- Fiber.schedule do
68
- results << Addrinfo.getaddrinfo("www.google.com", 80, nil, :STREAM)
69
- end
70
- results
71
- end.value
72
-
73
- puts results.map(&:inspect)
74
- ```
75
-
76
- to run many blocking operations simultaneously all while occupying only a single Ruby thread!
@@ -1,49 +0,0 @@
1
- ---
2
- type: docs
3
- sidebar:
4
- exclude: true
5
- ---
6
-
7
- # ItsiServer
8
-
9
- TODO: Delete this and the text below, and describe your gem
10
-
11
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/itsi_server`. To experiment with that code, run `bin/console` for an interactive prompt.
12
-
13
- ## Installation
14
-
15
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
16
-
17
- Install the gem and add to the application's Gemfile by executing:
18
-
19
- ```bash
20
- bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
21
- ```
22
-
23
- If bundler is not being used to manage dependencies, install the gem by executing:
24
-
25
- ```bash
26
- gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
27
- ```
28
-
29
- ## Usage
30
-
31
- TODO: Write usage instructions here
32
-
33
- ## Development
34
-
35
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
36
-
37
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
38
-
39
- ## Contributing
40
-
41
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/itsi_server. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/itsi_server/blob/master/CODE_OF_CONDUCT.md).
42
-
43
- ## License
44
-
45
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
46
-
47
- ## Code of Conduct
48
-
49
- Everyone interacting in the ItsiServer project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/itsi_server/blob/master/CODE_OF_CONDUCT.md).