itsi-server 0.2.11 → 0.2.12
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/Cargo.lock +1 -1
- data/ext/itsi_error/Cargo.toml +1 -1
- data/ext/itsi_rb_helpers/Cargo.toml +1 -1
- data/ext/itsi_scheduler/Cargo.toml +1 -1
- data/ext/itsi_server/Cargo.toml +1 -1
- data/ext/itsi_server/src/default_responses/mod.rs +3 -0
- data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +15 -10
- data/ext/itsi_server/src/ruby_types/itsi_http_response.rs +2 -2
- data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +6 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +13 -7
- data/ext/itsi_server/src/server/thread_worker.rs +4 -1
- data/lib/itsi/server/config/config_helpers.rb +5 -2
- data/lib/itsi/server/config/middleware/endpoint/delete.rb +3 -2
- data/lib/itsi/server/config/middleware/endpoint/endpoint.rb +14 -3
- data/lib/itsi/server/config/middleware/endpoint/get.rb +3 -2
- data/lib/itsi/server/config/middleware/endpoint/patch.rb +3 -2
- data/lib/itsi/server/config/middleware/endpoint/post.rb +3 -2
- data/lib/itsi/server/config/middleware/endpoint/put.rb +3 -2
- data/lib/itsi/server/config/middleware/rackup_file.md +18 -0
- data/lib/itsi/server/config/middleware/rackup_file.rb +2 -0
- data/lib/itsi/server/config/middleware/run.md +20 -1
- data/lib/itsi/server/config/middleware/run.rb +2 -0
- data/lib/itsi/server/config/options/ruby_thread_request_backlog_size.md +18 -0
- data/lib/itsi/server/config/options/ruby_thread_request_backlog_size.rb +19 -0
- data/lib/itsi/server/config/options/scheduler_threads.md +7 -0
- data/lib/itsi/server/config.rb +3 -0
- data/lib/itsi/server/rack_interface.rb +5 -6
- data/lib/itsi/server/version.rb +1 -1
- metadata +3 -2
- data/sig/itsi_server.rbs +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a7ed672076127c57c4691c730e0a1545edaa1659573652b91e4a5db2bab3f02
|
4
|
+
data.tar.gz: 438a6d6e9693ae19080604e642709aa0381cd3486e4f562b175300549cf417f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c203ef2a97ed945256f138561359f7c4a32f79417b3caaded682f12a56ff2737b2becfd5e9994e133c547a2e455974e2b1255749ff61ada8e5c8f20aaa95721
|
7
|
+
data.tar.gz: 87908e166b10ef36fd3404d5f80cfca03baa5ac12e974947d3e8d9e65b93d73919c0159c0d6d77d62ff33e1eb100dc37ab3bf96fa455e7eec370bc582986c9e7
|
data/Cargo.lock
CHANGED
data/ext/itsi_error/Cargo.toml
CHANGED
data/ext/itsi_server/Cargo.toml
CHANGED
@@ -9,3 +9,6 @@ pub static NOT_FOUND_RESPONSE: LazyLock<ErrorResponse> = LazyLock::new(ErrorResp
|
|
9
9
|
|
10
10
|
pub static INTERNAL_SERVER_ERROR_RESPONSE: LazyLock<ErrorResponse> =
|
11
11
|
LazyLock::new(ErrorResponse::internal_server_error);
|
12
|
+
|
13
|
+
pub static SERVICE_UNAVAILABLE_RESPONSE: LazyLock<ErrorResponse> =
|
14
|
+
LazyLock::new(ErrorResponse::service_unavailable);
|
@@ -22,6 +22,7 @@ use super::{
|
|
22
22
|
itsi_http_response::ItsiHttpResponse,
|
23
23
|
};
|
24
24
|
use crate::{
|
25
|
+
default_responses::{INTERNAL_SERVER_ERROR_RESPONSE, SERVICE_UNAVAILABLE_RESPONSE},
|
25
26
|
server::{
|
26
27
|
byte_frame::ByteFrame,
|
27
28
|
http_message_types::{HttpRequest, HttpResponse},
|
@@ -186,16 +187,20 @@ impl ItsiHttpRequest {
|
|
186
187
|
} else {
|
187
188
|
&context.sender
|
188
189
|
};
|
189
|
-
match sender
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
190
|
+
match sender.try_send(RequestJob::ProcessHttpRequest(request, app)) {
|
191
|
+
Err(err) => match err {
|
192
|
+
async_channel::TrySendError::Full(_) => {
|
193
|
+
Ok(SERVICE_UNAVAILABLE_RESPONSE
|
194
|
+
.to_http_response(context.accept.clone())
|
195
|
+
.await)
|
196
|
+
}
|
197
|
+
async_channel::TrySendError::Closed(err) => {
|
198
|
+
error!("Error occurred: {:?}", err);
|
199
|
+
Ok(INTERNAL_SERVER_ERROR_RESPONSE
|
200
|
+
.to_http_response(context.accept.clone())
|
201
|
+
.await)
|
202
|
+
}
|
203
|
+
},
|
199
204
|
_ => match receiver.recv().await {
|
200
205
|
Some(first_frame) => Ok(response
|
201
206
|
.build(first_frame, receiver, shutdown_channel)
|
@@ -347,7 +347,7 @@ impl ItsiHttpResponse {
|
|
347
347
|
})?;
|
348
348
|
let header_value = unsafe { HeaderValue::from_maybe_shared_unchecked(value) };
|
349
349
|
if let Some(ref mut resp) = *self.data.response.write() {
|
350
|
-
resp.headers_mut().
|
350
|
+
resp.headers_mut().append(header_name, header_value);
|
351
351
|
}
|
352
352
|
Ok(())
|
353
353
|
}
|
@@ -364,7 +364,7 @@ impl ItsiHttpResponse {
|
|
364
364
|
})?;
|
365
365
|
for value in values {
|
366
366
|
let header_value = unsafe { HeaderValue::from_maybe_shared_unchecked(value) };
|
367
|
-
headers_mut.
|
367
|
+
headers_mut.append(&header_name, header_value);
|
368
368
|
}
|
369
369
|
}
|
370
370
|
}
|
@@ -69,6 +69,7 @@ pub struct ServerParams {
|
|
69
69
|
pub pin_worker_cores: bool,
|
70
70
|
pub scheduler_class: Option<String>,
|
71
71
|
pub oob_gc_responses_threshold: Option<u64>,
|
72
|
+
pub ruby_thread_request_backlog_size: Option<usize>,
|
72
73
|
pub middleware_loader: HeapValue<Proc>,
|
73
74
|
pub middleware: OnceLock<MiddlewareSet>,
|
74
75
|
pub binds: Vec<Bind>,
|
@@ -222,6 +223,10 @@ impl ServerParams {
|
|
222
223
|
let scheduler_class: Option<String> = rb_param_hash.fetch("scheduler_class")?;
|
223
224
|
let oob_gc_responses_threshold: Option<u64> =
|
224
225
|
rb_param_hash.fetch("oob_gc_responses_threshold")?;
|
226
|
+
|
227
|
+
let ruby_thread_request_backlog_size: Option<usize> =
|
228
|
+
rb_param_hash.fetch("ruby_thread_request_backlog_size")?;
|
229
|
+
|
225
230
|
let middleware_loader: Proc = rb_param_hash.fetch("middleware_loader")?;
|
226
231
|
let log_level: Option<String> = rb_param_hash.fetch("log_level")?;
|
227
232
|
let log_target: Option<String> = rb_param_hash.fetch("log_target")?;
|
@@ -310,6 +315,7 @@ impl ServerParams {
|
|
310
315
|
scheduler_threads,
|
311
316
|
streamable_body,
|
312
317
|
scheduler_class,
|
318
|
+
ruby_thread_request_backlog_size,
|
313
319
|
oob_gc_responses_threshold,
|
314
320
|
binds,
|
315
321
|
itsi_server_token_preference,
|
@@ -18,6 +18,7 @@ use std::sync::Arc;
|
|
18
18
|
pub struct RubyApp {
|
19
19
|
app: Arc<HeapValue<Proc>>,
|
20
20
|
request_type: RequestType,
|
21
|
+
script_name: Option<String>,
|
21
22
|
sendfile: bool,
|
22
23
|
nonblocking: bool,
|
23
24
|
base_path: Regex,
|
@@ -53,6 +54,9 @@ impl RubyApp {
|
|
53
54
|
let base_path_src = params
|
54
55
|
.funcall::<_, _, String>(Symbol::new("[]"), ("base_path",))
|
55
56
|
.unwrap_or("".to_owned());
|
57
|
+
let script_name = params
|
58
|
+
.funcall::<_, _, Option<String>>(Symbol::new("[]"), ("script_name",))
|
59
|
+
.unwrap_or(None);
|
56
60
|
let base_path = Regex::new(&base_path_src).unwrap();
|
57
61
|
|
58
62
|
let request_type: RequestType = params
|
@@ -65,6 +69,7 @@ impl RubyApp {
|
|
65
69
|
app: Arc::new(app.into()),
|
66
70
|
sendfile,
|
67
71
|
nonblocking,
|
72
|
+
script_name,
|
68
73
|
request_type,
|
69
74
|
base_path,
|
70
75
|
}))
|
@@ -82,13 +87,14 @@ impl MiddlewareLayer for RubyApp {
|
|
82
87
|
match self.request_type {
|
83
88
|
RequestType::Http => {
|
84
89
|
let uri = req.uri().path();
|
85
|
-
let script_name = self
|
86
|
-
.base_path
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
90
|
+
let script_name = self.script_name.clone().unwrap_or_else(|| {
|
91
|
+
self.base_path
|
92
|
+
.captures(uri)
|
93
|
+
.and_then(|caps| caps.name("base_path"))
|
94
|
+
.map(|m| m.as_str())
|
95
|
+
.unwrap_or("/")
|
96
|
+
.to_owned()
|
97
|
+
});
|
92
98
|
ItsiHttpRequest::process_request(
|
93
99
|
self.app.clone(),
|
94
100
|
req,
|
@@ -65,9 +65,12 @@ type ThreadWorkerBuildResult = Result<(
|
|
65
65
|
pub fn build_thread_workers(params: Arc<ServerParams>, pid: Pid) -> ThreadWorkerBuildResult {
|
66
66
|
let blocking_thread_count = params.threads;
|
67
67
|
let nonblocking_thread_count = params.scheduler_threads;
|
68
|
+
let ruby_thread_request_backlog_size: usize = params
|
69
|
+
.ruby_thread_request_backlog_size
|
70
|
+
.unwrap_or_else(|| (blocking_thread_count as u16 * 30) as usize);
|
68
71
|
|
69
72
|
let (blocking_sender, blocking_receiver) =
|
70
|
-
async_channel::bounded(
|
73
|
+
async_channel::bounded(ruby_thread_request_backlog_size);
|
71
74
|
let blocking_receiver_ref = Arc::new(blocking_receiver);
|
72
75
|
let blocking_sender_ref = blocking_sender;
|
73
76
|
let scheduler_class = load_scheduler_class(params.scheduler_class.clone())?;
|
@@ -11,14 +11,17 @@ module Itsi
|
|
11
11
|
].flatten
|
12
12
|
|
13
13
|
listing.each do |file|
|
14
|
-
current = klass.subclasses
|
14
|
+
current = klass.subclasses.dup
|
15
15
|
require file
|
16
16
|
following = klass.subclasses
|
17
17
|
new_class = (following - current).first
|
18
18
|
|
19
19
|
documentation_file = "#{file[/(.*)\.rb/, 1]}.md"
|
20
20
|
documentation_file = "#{file[%r{(.*)/[^/]+\.rb}, 1]}/_index.md" unless File.exist?(documentation_file)
|
21
|
-
|
21
|
+
unless File.exist?(documentation_file) && new_class
|
22
|
+
new_class&.documentation "Documentation not found"
|
23
|
+
next
|
24
|
+
end
|
22
25
|
|
23
26
|
new_class.documentation IO.read(documentation_file)
|
24
27
|
.gsub(/^---.*?\n.*?-+/m, "") # Strip frontmatter
|
@@ -26,12 +26,13 @@ module Itsi
|
|
26
26
|
paths: Array(Or(Type(String), Type(Regexp))),
|
27
27
|
handler: Type(Proc) & Required(),
|
28
28
|
http_methods: Array(Type(String)),
|
29
|
+
script_name: Type(String).default(nil),
|
29
30
|
nonblocking: Bool()
|
30
31
|
}
|
31
32
|
end
|
32
33
|
|
33
|
-
def initialize(location, path="", handler=nil, http_methods: [], nonblocking: false, &handler_proc)
|
34
|
-
location.endpoint(path, handler, http_methods: ["DELETE"], nonblocking: nonblocking, &handler_proc)
|
34
|
+
def initialize(location, path="", handler=nil, http_methods: [], nonblocking: false, script_name: nil, &handler_proc) # rubocop:disable Metrics/ParameterLists,Lint/MissingSuper
|
35
|
+
location.endpoint(path, handler, http_methods: ["DELETE"], nonblocking: nonblocking, script_name: script_name, &handler_proc)
|
35
36
|
end
|
36
37
|
|
37
38
|
def build!
|
@@ -28,17 +28,24 @@ module Itsi
|
|
28
28
|
paths: Array(Or(Type(String), Type(Regexp))),
|
29
29
|
handler: Type(Proc) & Required(),
|
30
30
|
http_methods: Array(Type(String)),
|
31
|
+
script_name: Type(String).default(nil),
|
31
32
|
nonblocking: Bool()
|
32
33
|
}
|
33
34
|
end
|
34
35
|
|
35
|
-
def initialize(location, path="", handler=nil, http_methods: [], nonblocking: false, &handler_proc)
|
36
|
+
def initialize(location, path="", handler=nil, http_methods: [], nonblocking: false, script_name: nil, &handler_proc)
|
36
37
|
raise "Can not combine a controller method and inline handler" if handler && handler_proc
|
37
38
|
handler_proc = location.controller.method(handler).to_proc if handler.is_a?(Symbol) || handler.is_a?(String)
|
38
39
|
|
39
40
|
super(
|
40
41
|
location,
|
41
|
-
{
|
42
|
+
{
|
43
|
+
paths: Array(path),
|
44
|
+
handler: handler_proc,
|
45
|
+
http_methods: http_methods,
|
46
|
+
nonblocking: nonblocking,
|
47
|
+
script_name: script_name
|
48
|
+
}
|
42
49
|
)
|
43
50
|
|
44
51
|
num_required, keywords = Itsi::Server::TypedHandlers::SourceParser.extract_expr_from_source_location(handler_proc)
|
@@ -75,7 +82,11 @@ module Itsi
|
|
75
82
|
|
76
83
|
def build!
|
77
84
|
params = @params
|
78
|
-
app = {
|
85
|
+
app = {
|
86
|
+
preloader: -> { params[:handler] },
|
87
|
+
nonblocking: @params[:nonblocking],
|
88
|
+
script_name: @params[:script_name]
|
89
|
+
}
|
79
90
|
|
80
91
|
if @params[:paths] == [""] && @params[:http_methods].empty?
|
81
92
|
location.middleware[:app] = app
|
@@ -26,12 +26,13 @@ module Itsi
|
|
26
26
|
paths: Array(Or(Type(String), Type(Regexp))),
|
27
27
|
handler: Type(Proc) & Required(),
|
28
28
|
http_methods: Array(Type(String)),
|
29
|
+
script_name: Type(String).default(nil),
|
29
30
|
nonblocking: Bool()
|
30
31
|
}
|
31
32
|
end
|
32
33
|
|
33
|
-
def initialize(location, path="", handler=nil, http_methods: [], nonblocking: false, &handler_proc)
|
34
|
-
location.endpoint(path, handler, http_methods: ["GET", "HEAD"], nonblocking: nonblocking, &handler_proc)
|
34
|
+
def initialize(location, path="", handler=nil, http_methods: [], nonblocking: false, script_name: nil, &handler_proc) # rubocop:disable Lint/MissingSuper,Metrics/ParameterLists
|
35
|
+
location.endpoint(path, handler, http_methods: ["GET", "HEAD"], nonblocking: nonblocking, script_name: script_name, &handler_proc)
|
35
36
|
end
|
36
37
|
|
37
38
|
def build!
|
@@ -26,12 +26,13 @@ module Itsi
|
|
26
26
|
paths: Array(Or(Type(String), Type(Regexp))),
|
27
27
|
handler: Type(Proc) & Required(),
|
28
28
|
http_methods: Array(Type(String)),
|
29
|
+
script_name: Type(String).default(nil),
|
29
30
|
nonblocking: Bool()
|
30
31
|
}
|
31
32
|
end
|
32
33
|
|
33
|
-
def initialize(location, path="", handler=nil, http_methods: [], nonblocking: false, &handler_proc)
|
34
|
-
location.endpoint(path, handler, http_methods: ["PATCH"], nonblocking: nonblocking, &handler_proc)
|
34
|
+
def initialize(location, path="", handler=nil, http_methods: [], nonblocking: false, script_name: nil, &handler_proc) # rubocop:disable Lint/MissingSuper
|
35
|
+
location.endpoint(path, handler, http_methods: ["PATCH"], nonblocking: nonblocking, script_name: script_name, &handler_proc)
|
35
36
|
end
|
36
37
|
|
37
38
|
def build!
|
@@ -26,12 +26,13 @@ module Itsi
|
|
26
26
|
paths: Array(Or(Type(String), Type(Regexp))),
|
27
27
|
handler: Type(Proc) & Required(),
|
28
28
|
http_methods: Array(Type(String)),
|
29
|
+
script_name: Type(String).default(nil),
|
29
30
|
nonblocking: Bool()
|
30
31
|
}
|
31
32
|
end
|
32
33
|
|
33
|
-
def initialize(location, path="", handler=nil, http_methods: [], nonblocking: false, &handler_proc)
|
34
|
-
location.endpoint(path, handler, http_methods: ["POST"], nonblocking: nonblocking, &handler_proc)
|
34
|
+
def initialize(location, path="", handler=nil, http_methods: [], nonblocking: false, script_name: nil, &handler_proc) # rubocop:disable Lint/MissingSuper
|
35
|
+
location.endpoint(path, handler, http_methods: ["POST"], nonblocking: nonblocking, script_name: script_name, &handler_proc)
|
35
36
|
end
|
36
37
|
|
37
38
|
def build!
|
@@ -26,12 +26,13 @@ module Itsi
|
|
26
26
|
paths: Array(Or(Type(String), Type(Regexp))),
|
27
27
|
handler: Type(Proc) & Required(),
|
28
28
|
http_methods: Array(Type(String)),
|
29
|
+
script_name: Type(String).default(nil),
|
29
30
|
nonblocking: Bool()
|
30
31
|
}
|
31
32
|
end
|
32
33
|
|
33
|
-
def initialize(location, path="", handler=nil, http_methods: [], nonblocking: false, &handler_proc)
|
34
|
-
location.endpoint(path, handler, http_methods: ["PUT"], nonblocking: nonblocking, &handler_proc)
|
34
|
+
def initialize(location, path="", handler=nil, http_methods: [], nonblocking: false, script_name: nil, &handler_proc) # rubocop:disable Lint/MissingSuper,Metrics/ParameterLists
|
35
|
+
location.endpoint(path, handler, http_methods: ["PUT"], nonblocking: nonblocking, script_name: script_name, &handler_proc)
|
35
36
|
end
|
36
37
|
|
37
38
|
def build!
|
@@ -44,6 +44,24 @@ $ curl http://0.0.0.0:3000/root/child_path
|
|
44
44
|
:/root/child_path
|
45
45
|
```
|
46
46
|
|
47
|
+
### `SCRIPT_NAME` and `PATH_INFO` Rack ENV variables.
|
48
|
+
Rack applications mounted at a subpath will, by default, receive a `SCRIPT_NAME` value that includes the subpath at which the app is mounted, and a `PATH_INFO` value that is relative to the subpath at which the rack app is mounted.
|
49
|
+
If you wish to use location blocks only to control the middleware that applies to a rack app, but still have it behave as if it were mounted elsewhere (e.g. at the root), you can explicitly set the `script_name` option.
|
50
|
+
E.g.
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
location "/subpath/*" do
|
54
|
+
rackup_file "config.ru", script_name: '/'
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
```bash
|
59
|
+
# Our script-name override kicks in here, even though the app is mounted under `/subpath`
|
60
|
+
$ curl http://0.0.0.0:3000/subpath/child_path
|
61
|
+
/:/subpath/child_path
|
62
|
+
```
|
63
|
+
|
64
|
+
|
47
65
|
### Options
|
48
66
|
* `nonblocking` (default false). Determines whether requests sent to this Rack application should be run on non-blocking threads. Only applies if running in hybrid (non-blocking and blocking thread pool) mode. Otherwise this is a no-op and will run in whatever mode is set globally.
|
49
67
|
* `sendfile` (default true). Determines whether Itsi should respect the `X-Sendfile` header set by the Rack application and use the `sendfile` function to efficiently send files. (Despite the name, this does not use the OS-level `sendfile` system call). Note. Itsi enforces the restriction that the referenced file must be within a child directory of the application root.
|
@@ -15,6 +15,7 @@ module Itsi
|
|
15
15
|
schema do
|
16
16
|
{
|
17
17
|
nonblocking: Bool().default(false),
|
18
|
+
script_name: Type(String).default(nil),
|
18
19
|
sendfile: Bool().default(true)
|
19
20
|
}
|
20
21
|
end
|
@@ -31,6 +32,7 @@ module Itsi
|
|
31
32
|
preloader: -> { @app },
|
32
33
|
sendfile: @params[:sendfile],
|
33
34
|
nonblocking: @params[:nonblocking],
|
35
|
+
script_name: @params[:script_name],
|
34
36
|
base_path: "^(?<base_path>#{location.paths_from_parent.gsub(/\.\*\)$/, ")")}).*$"
|
35
37
|
}
|
36
38
|
location.middleware[:app] = app_args
|
@@ -39,7 +39,6 @@ run(Rack::Builder.app do
|
|
39
39
|
use Rack::CommonLogger
|
40
40
|
run ->(env) { [200, { 'content-type' => 'text/plain' }, [[env['SCRIPT_NAME'], env['PATH_INFO']].join(":") ] ] }
|
41
41
|
end)
|
42
|
-
|
43
42
|
```
|
44
43
|
|
45
44
|
```bash
|
@@ -50,6 +49,26 @@ $ curl http://0.0.0.0:3000/root/child_path
|
|
50
49
|
:/root/child_path
|
51
50
|
```
|
52
51
|
|
52
|
+
### `SCRIPT_NAME` and `PATH_INFO` Rack ENV variables.
|
53
|
+
Rack applications mounted at a subpath will, by default, receive a `SCRIPT_NAME` value that includes the subpath at which the app is mounted, and a `PATH_INFO` value that is relative to the subpath at which the rack app is mounted.
|
54
|
+
If you wish to use location blocks only to control the middleware that applies to a rack app, but still have it behave as if it were mounted elsewhere (e.g. at the root), you can explicitly set the `script_name` option.
|
55
|
+
E.g.
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
location "/subpath/*" do
|
59
|
+
run(Rack::Builder.app do
|
60
|
+
use Rack::CommonLogger
|
61
|
+
run ->(env) { [200, { 'content-type' => 'text/plain' }, [[env['SCRIPT_NAME'], env['PATH_INFO']].join(":") ] ] }
|
62
|
+
end, script_name: '/')
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
```bash
|
67
|
+
# Our script-name override kicks in here, even though the app is mounted under `/subpath`
|
68
|
+
$ curl http://0.0.0.0:3000/subpath/child_path
|
69
|
+
/:/subpath/child_path
|
70
|
+
```
|
71
|
+
|
53
72
|
### Options
|
54
73
|
* `nonblocking` (default false). Determines whether requests sent to this Rack application should be run on non-blocking threads. Only applies if running in hybrid (non-blocking and blocking thread pool) mode. Otherwise this is a no-op and will run in whatever mode is set globally.
|
55
74
|
* `sendfile` (default true). Determines whether Itsi should respect the `X-Sendfile` header set by the Rack application and use the `sendfile` function to efficiently send files. (Despite the name, this does not use the OS-level `sendfile` system call). Note. Itsi enforces the restriction that the referenced file must be within a child directory of the application root.
|
@@ -15,6 +15,7 @@ module Itsi
|
|
15
15
|
schema do
|
16
16
|
{
|
17
17
|
nonblocking: Bool().default(false),
|
18
|
+
script_name: Type(String).default(nil),
|
18
19
|
sendfile: Bool().default(true)
|
19
20
|
}
|
20
21
|
end
|
@@ -30,6 +31,7 @@ module Itsi
|
|
30
31
|
preloader: -> { Itsi::Server::RackInterface.for(@app) },
|
31
32
|
sendfile: @params[:sendfile],
|
32
33
|
nonblocking: @params[:nonblocking],
|
34
|
+
script_name: @params[:script_name],
|
33
35
|
base_path: "^(?<base_path>#{location.paths_from_parent.gsub(/\.\*\)$/, ')')}).*$"
|
34
36
|
}
|
35
37
|
location.middleware[:app] = app_args
|
@@ -0,0 +1,18 @@
|
|
1
|
+
---
|
2
|
+
title: Ruby Thread Request Backlog Size
|
3
|
+
url: /options/ruby_thread_request_backlog_size
|
4
|
+
---
|
5
|
+
|
6
|
+
Configures the size of the backlog queue for incoming requests in the Ruby thread pool.
|
7
|
+
Up to this many requests can be queued at once before the server rejects further requests to Ruby workers (note this does not block other requests to proxied hosts or for static assets).
|
8
|
+
|
9
|
+
The default value is `30 x number of threads`.
|
10
|
+
|
11
|
+
## Configuration
|
12
|
+
```ruby {filename=Itsi.rb}
|
13
|
+
ruby_thread_request_backlog_size 20
|
14
|
+
```
|
15
|
+
|
16
|
+
```ruby {filename=Itsi.rb}
|
17
|
+
ruby_thread_request_backlog_size 100
|
18
|
+
```
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Itsi
|
2
|
+
class Server
|
3
|
+
module Config
|
4
|
+
class RubyThreadRequestBacklogSize < Option
|
5
|
+
|
6
|
+
insert_text <<~SNIPPET
|
7
|
+
ruby_thread_request_backlog_size ${1|10,25,50,100|}
|
8
|
+
SNIPPET
|
9
|
+
|
10
|
+
detail "The maximum number of requests that can be queued for processing by the Ruby thread."
|
11
|
+
|
12
|
+
schema do
|
13
|
+
(Type(Integer)).default(nil)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -25,10 +25,17 @@ fiber_scheduler true
|
|
25
25
|
# We mount the same app *twice*.
|
26
26
|
# For a specific route prefix, all requests will be sent to non blocking threads.
|
27
27
|
# All others fall through to the default mount
|
28
|
+
|
28
29
|
location "/heavy_io/*" do
|
30
|
+
# You can optionally use the `script_name: ""` option here to set the base path for the mounted app (useful if a nested app
|
31
|
+
# should still serve requests as if it was mounted at the root).
|
32
|
+
# Otherwise it will infer the script-name based on the parent location block.
|
29
33
|
rackup_file "./config.ru", nonblocking: true
|
30
34
|
end
|
31
35
|
|
32
36
|
rackup_file "./config.ru"
|
33
37
|
|
34
38
|
```
|
39
|
+
## Examples.
|
40
|
+
|
41
|
+
See [https://github.com/wouterken/itsi/tree/main/examples/hybrid_scheduler_mode](hybrid_scheduler_mode) example in the Git repository.
|
data/lib/itsi/server/config.rb
CHANGED
@@ -134,6 +134,9 @@ module Itsi
|
|
134
134
|
oob_gc_responses_threshold: args.fetch(:oob_gc_responses_threshold) do
|
135
135
|
itsifile_config.fetch(:oob_gc_responses_threshold, nil)
|
136
136
|
end,
|
137
|
+
ruby_thread_request_backlog_size: args.fetch(:ruby_thread_request_backlog_size) do
|
138
|
+
itsifile_config.fetch(:ruby_thread_request_backlog_size, nil)
|
139
|
+
end,
|
137
140
|
log_level: args.fetch(:log_level) { itsifile_config.fetch(:log_level, nil) },
|
138
141
|
log_format: args.fetch(:log_format) { itsifile_config.fetch(:log_format, nil) },
|
139
142
|
log_target: args.fetch(:log_target) { itsifile_config.fetch(:log_target, nil) },
|
@@ -40,13 +40,12 @@ module Itsi
|
|
40
40
|
# 2. Set Headers
|
41
41
|
body_streamer = streaming_body?(body) ? body : headers.delete("rack.hijack")
|
42
42
|
headers.each do |key, value|
|
43
|
-
|
43
|
+
if value.is_a?(Array)
|
44
|
+
value.each do |v|
|
45
|
+
response[key] = v
|
46
|
+
end
|
47
|
+
elsif value.is_a?(String)
|
44
48
|
response[key] = value
|
45
|
-
next
|
46
|
-
end
|
47
|
-
|
48
|
-
value.each do |v|
|
49
|
-
response[key] = v
|
50
49
|
end
|
51
50
|
end
|
52
51
|
|
data/lib/itsi/server/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: itsi-server
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wouter Coppieters
|
@@ -497,6 +497,8 @@ files:
|
|
497
497
|
- lib/itsi/server/config/options/reuse_address.rb
|
498
498
|
- lib/itsi/server/config/options/reuse_port.md
|
499
499
|
- lib/itsi/server/config/options/reuse_port.rb
|
500
|
+
- lib/itsi/server/config/options/ruby_thread_request_backlog_size.md
|
501
|
+
- lib/itsi/server/config/options/ruby_thread_request_backlog_size.rb
|
500
502
|
- lib/itsi/server/config/options/scheduler_threads.md
|
501
503
|
- lib/itsi/server/config/options/scheduler_threads.rb
|
502
504
|
- lib/itsi/server/config/options/shutdown_timeout.md
|
@@ -532,7 +534,6 @@ files:
|
|
532
534
|
- lib/itsi/standard_headers.rb
|
533
535
|
- lib/ruby_lsp/itsi/addon.rb
|
534
536
|
- lib/shell_completions/completions.rb
|
535
|
-
- sig/itsi_server.rbs
|
536
537
|
homepage: https://itsi.fyi
|
537
538
|
licenses:
|
538
539
|
- MIT
|
data/sig/itsi_server.rbs
DELETED