itsi-server 0.2.16 → 0.2.18
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 +3 -1
- data/exe/itsi +8 -5
- data/ext/itsi_acme/Cargo.toml +1 -1
- data/ext/itsi_scheduler/Cargo.toml +1 -1
- data/ext/itsi_server/Cargo.toml +3 -1
- data/ext/itsi_server/src/lib.rs +6 -1
- data/ext/itsi_server/src/ruby_types/itsi_body_proxy/mod.rs +2 -0
- data/ext/itsi_server/src/ruby_types/itsi_grpc_call.rs +4 -4
- data/ext/itsi_server/src/ruby_types/itsi_grpc_response_stream/mod.rs +14 -13
- data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +64 -33
- data/ext/itsi_server/src/ruby_types/itsi_http_response.rs +151 -152
- data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +422 -110
- data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +62 -15
- data/ext/itsi_server/src/ruby_types/itsi_server.rs +1 -1
- data/ext/itsi_server/src/server/binds/listener.rs +45 -7
- data/ext/itsi_server/src/server/frame_stream.rs +142 -0
- data/ext/itsi_server/src/server/http_message_types.rs +142 -9
- data/ext/itsi_server/src/server/io_stream.rs +28 -5
- data/ext/itsi_server/src/server/lifecycle_event.rs +1 -1
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +2 -3
- data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +8 -10
- data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +2 -3
- data/ext/itsi_server/src/server/middleware_stack/middlewares/csp.rs +3 -3
- data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +54 -56
- data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +5 -7
- data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +5 -5
- data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +7 -10
- data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +2 -3
- data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +1 -2
- data/ext/itsi_server/src/server/middleware_stack/middlewares/static_response.rs +4 -6
- data/ext/itsi_server/src/server/mod.rs +1 -0
- data/ext/itsi_server/src/server/process_worker.rs +3 -4
- data/ext/itsi_server/src/server/serve_strategy/acceptor.rs +16 -12
- data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +83 -31
- data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +166 -142
- data/ext/itsi_server/src/server/signal.rs +37 -9
- data/ext/itsi_server/src/server/thread_worker.rs +84 -69
- data/ext/itsi_server/src/services/itsi_http_service.rs +43 -43
- data/ext/itsi_server/src/services/static_file_server.rs +28 -47
- data/lib/itsi/http_request.rb +31 -39
- data/lib/itsi/http_response.rb +5 -0
- data/lib/itsi/rack_env_pool.rb +59 -0
- data/lib/itsi/server/config/dsl.rb +6 -4
- data/lib/itsi/server/config/middleware/compression.md +3 -3
- data/lib/itsi/server/config/middleware/endpoint/controller.md +1 -1
- data/lib/itsi/server/config/middleware/proxy.md +2 -2
- data/lib/itsi/server/config/middleware/proxy.rb +1 -1
- data/lib/itsi/server/config/middleware/rackup_file.rb +2 -2
- data/lib/itsi/server/config/options/auto_reload_config.rb +11 -6
- data/lib/itsi/server/config/options/include.md +1 -0
- data/lib/itsi/server/config/options/include.rb +13 -8
- data/lib/itsi/server/config/options/pipeline_flush.md +16 -0
- data/lib/itsi/server/config/options/pipeline_flush.rb +19 -0
- data/lib/itsi/server/config/options/reuse_port.rb +2 -4
- data/lib/itsi/server/config/options/writev.md +25 -0
- data/lib/itsi/server/config/options/writev.rb +19 -0
- data/lib/itsi/server/config.rb +22 -9
- data/lib/itsi/server/default_config/Itsi.rb +9 -8
- data/lib/itsi/server/grpc/grpc_call.rb +2 -0
- data/lib/itsi/server/grpc/grpc_interface.rb +2 -2
- data/lib/itsi/server/rack/handler/itsi.rb +3 -1
- data/lib/itsi/server/rack_interface.rb +17 -12
- data/lib/itsi/server/scheduler_interface.rb +2 -0
- data/lib/itsi/server/version.rb +1 -1
- data/lib/itsi/server.rb +1 -0
- data/lib/ruby_lsp/itsi/addon.rb +12 -13
- metadata +7 -1
@@ -2,7 +2,7 @@ use crate::{
|
|
2
2
|
default_responses::NOT_FOUND_RESPONSE,
|
3
3
|
prelude::*,
|
4
4
|
server::{
|
5
|
-
http_message_types::{HttpRequest, HttpResponse, RequestExt, ResponseFormat},
|
5
|
+
http_message_types::{HttpBody, HttpRequest, HttpResponse, RequestExt, ResponseFormat},
|
6
6
|
middleware_stack::ErrorResponse,
|
7
7
|
redirect_type::RedirectType,
|
8
8
|
},
|
@@ -16,7 +16,6 @@ use http::{
|
|
16
16
|
},
|
17
17
|
HeaderName, HeaderValue, Response, StatusCode,
|
18
18
|
};
|
19
|
-
use http_body_util::{combinators::BoxBody, Full};
|
20
19
|
use itsi_error::Result;
|
21
20
|
use parking_lot::{Mutex, RwLock};
|
22
21
|
use percent_encoding::percent_decode_str;
|
@@ -28,7 +27,6 @@ use std::{
|
|
28
27
|
borrow::Cow,
|
29
28
|
cmp::Ordering,
|
30
29
|
collections::HashMap,
|
31
|
-
convert::Infallible,
|
32
30
|
fs::Metadata,
|
33
31
|
ops::Deref,
|
34
32
|
path::{Path, PathBuf},
|
@@ -324,7 +322,7 @@ impl StaticFileServer {
|
|
324
322
|
}) => Response::builder()
|
325
323
|
.status(StatusCode::MOVED_PERMANENTLY)
|
326
324
|
.header(header::LOCATION, redirect_to)
|
327
|
-
.body(
|
325
|
+
.body(HttpBody::empty())
|
328
326
|
.unwrap(),
|
329
327
|
Err(not_found_behavior) => match not_found_behavior {
|
330
328
|
NotFoundBehavior::Error(error_response) => {
|
@@ -340,7 +338,7 @@ impl StaticFileServer {
|
|
340
338
|
NotFoundBehavior::Redirect(redirect) => Response::builder()
|
341
339
|
.status(redirect.r#type.status_code())
|
342
340
|
.header(header::LOCATION, redirect.to)
|
343
|
-
.body(
|
341
|
+
.body(HttpBody::empty())
|
344
342
|
.unwrap(),
|
345
343
|
},
|
346
344
|
})
|
@@ -407,7 +405,7 @@ impl StaticFileServer {
|
|
407
405
|
|
408
406
|
Response::builder()
|
409
407
|
.status(StatusCode::NOT_FOUND)
|
410
|
-
.body(
|
408
|
+
.body(HttpBody::empty())
|
411
409
|
.unwrap()
|
412
410
|
}
|
413
411
|
|
@@ -648,15 +646,8 @@ impl StaticFileServer {
|
|
648
646
|
Err(nf)
|
649
647
|
}
|
650
648
|
|
651
|
-
async fn stream_file_range(
|
652
|
-
&self,
|
653
|
-
path: PathBuf,
|
654
|
-
start: u64,
|
655
|
-
end: u64,
|
656
|
-
) -> Option<BoxBody<Bytes, Infallible>> {
|
649
|
+
async fn stream_file_range(&self, path: PathBuf, start: u64, end: u64) -> Option<HttpBody> {
|
657
650
|
use futures::TryStreamExt;
|
658
|
-
use http_body_util::StreamBody;
|
659
|
-
use hyper::body::Frame;
|
660
651
|
use tokio::io::AsyncSeekExt;
|
661
652
|
use tokio_util::io::ReaderStream;
|
662
653
|
|
@@ -687,32 +678,25 @@ impl StaticFileServer {
|
|
687
678
|
let range_length = end - start + 1;
|
688
679
|
let limited_reader = tokio::io::AsyncReadExt::take(file, range_length);
|
689
680
|
let path_clone = path.clone();
|
690
|
-
let stream = ReaderStream::with_capacity(limited_reader, 64 * 1024)
|
691
|
-
.
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
});
|
696
|
-
|
697
|
-
Some(BoxBody::new(StreamBody::new(stream)))
|
681
|
+
let stream = ReaderStream::with_capacity(limited_reader, 64 * 1024).map_err(move |e| {
|
682
|
+
warn!("Error streaming file {}: {}", path_clone.display(), e);
|
683
|
+
unreachable!("We handle IO errors above")
|
684
|
+
});
|
685
|
+
Some(HttpBody::stream(stream))
|
698
686
|
}
|
699
687
|
|
700
|
-
async fn stream_file(&self, path: PathBuf) -> Option<
|
688
|
+
async fn stream_file(&self, path: PathBuf) -> Option<HttpBody> {
|
701
689
|
use futures::TryStreamExt;
|
702
|
-
use http_body_util::StreamBody;
|
703
|
-
use hyper::body::Frame;
|
704
690
|
use tokio_util::io::ReaderStream;
|
705
691
|
|
706
692
|
match File::open(&path).await {
|
707
693
|
Ok(file) => {
|
708
694
|
let path_clone = path.clone();
|
709
|
-
let stream = ReaderStream::with_capacity(file, 64 * 1024)
|
710
|
-
.
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
});
|
715
|
-
Some(BoxBody::new(StreamBody::new(stream)))
|
695
|
+
let stream = ReaderStream::with_capacity(file, 64 * 1024).map_err(move |e| {
|
696
|
+
warn!("Error streaming file {}: {}", path_clone.display(), e);
|
697
|
+
unreachable!("We handle IO errors above")
|
698
|
+
});
|
699
|
+
Some(HttpBody::stream(stream))
|
716
700
|
}
|
717
701
|
Err(e) => {
|
718
702
|
warn!(
|
@@ -749,7 +733,7 @@ impl StaticFileServer {
|
|
749
733
|
return Response::builder()
|
750
734
|
.status(StatusCode::RANGE_NOT_SATISFIABLE)
|
751
735
|
.header("Content-Range", format!("bytes */{}", content_length))
|
752
|
-
.body(
|
736
|
+
.body(HttpBody::empty())
|
753
737
|
.unwrap();
|
754
738
|
}
|
755
739
|
|
@@ -795,7 +779,7 @@ impl StaticFileServer {
|
|
795
779
|
builder = builder.header("Content-Range", range);
|
796
780
|
}
|
797
781
|
|
798
|
-
return builder.body(
|
782
|
+
return builder.body(HttpBody::empty()).unwrap();
|
799
783
|
}
|
800
784
|
|
801
785
|
// For GET requests, prepare the actual content
|
@@ -829,10 +813,7 @@ impl StaticFileServer {
|
|
829
813
|
}
|
830
814
|
}
|
831
815
|
|
832
|
-
fn serve_cached_content(
|
833
|
-
&self,
|
834
|
-
serve_cache_args: ServeCacheArgs,
|
835
|
-
) -> http::Response<BoxBody<Bytes, Infallible>> {
|
816
|
+
fn serve_cached_content(&self, serve_cache_args: ServeCacheArgs) -> HttpResponse {
|
836
817
|
let ServeCacheArgs(
|
837
818
|
cache_entry,
|
838
819
|
start,
|
@@ -855,7 +836,7 @@ impl StaticFileServer {
|
|
855
836
|
return Response::builder()
|
856
837
|
.status(StatusCode::RANGE_NOT_SATISFIABLE)
|
857
838
|
.header("Content-Range", format!("bytes */{}", content_length))
|
858
|
-
.body(
|
839
|
+
.body(HttpBody::empty())
|
859
840
|
.unwrap();
|
860
841
|
}
|
861
842
|
|
@@ -904,7 +885,7 @@ impl StaticFileServer {
|
|
904
885
|
builder = builder.header("Content-Range", range);
|
905
886
|
}
|
906
887
|
|
907
|
-
return builder.body(
|
888
|
+
return builder.body(HttpBody::empty()).unwrap();
|
908
889
|
}
|
909
890
|
|
910
891
|
if is_range_request {
|
@@ -920,7 +901,7 @@ impl StaticFileServer {
|
|
920
901
|
cache_entry.last_modified_http_date.clone(),
|
921
902
|
content_range,
|
922
903
|
&self.headers,
|
923
|
-
|
904
|
+
HttpBody::full(range_bytes),
|
924
905
|
)
|
925
906
|
} else {
|
926
907
|
// Return the full content
|
@@ -987,15 +968,15 @@ fn format_http_date_header(time: SystemTime) -> HeaderValue {
|
|
987
968
|
.unwrap()
|
988
969
|
}
|
989
970
|
|
990
|
-
fn build_ok_body(bytes: Arc<Bytes>) ->
|
991
|
-
|
971
|
+
fn build_ok_body(bytes: Arc<Bytes>) -> HttpBody {
|
972
|
+
HttpBody::full(bytes.as_ref().clone())
|
992
973
|
}
|
993
974
|
|
994
975
|
// Helper function to handle not modified responses
|
995
|
-
fn build_not_modified_response() ->
|
976
|
+
fn build_not_modified_response() -> HttpResponse {
|
996
977
|
Response::builder()
|
997
978
|
.status(StatusCode::NOT_MODIFIED)
|
998
|
-
.body(
|
979
|
+
.body(HttpBody::empty())
|
999
980
|
.unwrap()
|
1000
981
|
}
|
1001
982
|
|
@@ -1009,8 +990,8 @@ fn build_file_response(
|
|
1009
990
|
last_modified_http_date: HeaderValue,
|
1010
991
|
range_header: Option<String>,
|
1011
992
|
headers: &Option<HashMap<String, String>>,
|
1012
|
-
body:
|
1013
|
-
) ->
|
993
|
+
body: HttpBody,
|
994
|
+
) -> HttpResponse {
|
1014
995
|
let mut response = Response::new(body);
|
1015
996
|
|
1016
997
|
*response.status_mut() = status;
|
data/lib/itsi/http_request.rb
CHANGED
@@ -14,50 +14,35 @@ module Itsi
|
|
14
14
|
EMPTY_IO = StringIO.new("").tap { |io| io.set_encoding(Encoding::ASCII_8BIT) }
|
15
15
|
|
16
16
|
RACK_HEADER_MAP = StandardHeaders::ALL.map do |header|
|
17
|
-
rack_form =
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
17
|
+
rack_form = \
|
18
|
+
if header == "content-type"
|
19
|
+
"CONTENT_TYPE"
|
20
|
+
elsif header == "content-length"
|
21
|
+
"CONTENT_LENGTH"
|
22
|
+
else
|
23
|
+
"HTTP_#{header.upcase.gsub(/-/, "_")}"
|
24
|
+
end
|
24
25
|
[header, rack_form]
|
25
|
-
end.to_h
|
26
|
-
hm.default_proc = proc { |_, key| "HTTP_#{key.upcase.gsub(/-/, "_")}" }
|
27
|
-
end
|
26
|
+
end.to_h
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
"QUERY_STRING" => "",
|
42
|
-
"REMOTE_ADDR" => "",
|
43
|
-
"SERVER_PORT" => "",
|
44
|
-
"SERVER_NAME" => "",
|
45
|
-
"SERVER_PROTOCOL" => "",
|
46
|
-
"HTTP_HOST" => "",
|
47
|
-
"HTTP_VERSION" => "",
|
48
|
-
"itsi.request" => "",
|
49
|
-
"itsi.response" => "",
|
50
|
-
"rack.version" => nil,
|
51
|
-
"rack.url_scheme" => "",
|
52
|
-
"rack.input" => "",
|
53
|
-
"rack.hijack" => ""
|
54
|
-
}.freeze
|
28
|
+
RACK_HEADER_MAP.default_proc = proc { |_, key| "HTTP_#{key.upcase.gsub(/-/, "_")}" }
|
29
|
+
|
30
|
+
HTTP_09 = "HTTP/0.9"
|
31
|
+
HTTP_09_ARR = ["HTTP/0.9"].freeze
|
32
|
+
HTTP_10 = "HTTP/1.0"
|
33
|
+
HTTP_10_ARR = ["HTTP/1.0"].freeze
|
34
|
+
HTTP_11 = "HTTP/1.1"
|
35
|
+
HTTP_11_ARR = ["HTTP/1.1"].freeze
|
36
|
+
HTTP_20 = "HTTP/2.0"
|
37
|
+
HTTP_20_ARR = ["HTTP/2.0"].freeze
|
38
|
+
HTTP_30 = "HTTP/3.0"
|
39
|
+
HTTP_30_ARR = ["HTTP/3.0"].freeze
|
55
40
|
|
56
41
|
def to_rack_env
|
57
42
|
path = self.path
|
58
43
|
host = self.host
|
59
44
|
version = self.version
|
60
|
-
env =
|
45
|
+
env = RackEnvPool.checkout
|
61
46
|
env["SCRIPT_NAME"] = script_name
|
62
47
|
env["REQUEST_METHOD"] = request_method
|
63
48
|
env["REQUEST_PATH"] = env["PATH_INFO"] = path
|
@@ -68,11 +53,18 @@ module Itsi
|
|
68
53
|
env["HTTP_VERSION"] = env["SERVER_PROTOCOL"] = version
|
69
54
|
env["itsi.request"] = self
|
70
55
|
env["itsi.response"] = response
|
71
|
-
env["rack.version"] =
|
56
|
+
env["rack.version"] = \
|
57
|
+
case version
|
58
|
+
when HTTP_09 then HTTP_09_ARR
|
59
|
+
when HTTP_10 then HTTP_10_ARR
|
60
|
+
when HTTP_11 then HTTP_11_ARR
|
61
|
+
when HTTP_20 then HTTP_20_ARR
|
62
|
+
when HTTP_30 then HTTP_30_ARR
|
63
|
+
end
|
72
64
|
env["rack.url_scheme"] = scheme
|
73
65
|
env["rack.input"] = build_input_io
|
74
66
|
env["rack.hijack"] = method(:hijack)
|
75
|
-
|
67
|
+
each_header do |k, v|
|
76
68
|
env[case k
|
77
69
|
when "content-type" then "CONTENT_TYPE"
|
78
70
|
when "content-length" then "CONTENT_LENGTH"
|
data/lib/itsi/http_response.rb
CHANGED
@@ -16,6 +16,7 @@ module Itsi
|
|
16
16
|
body = body.to_s unless body.is_a?(String)
|
17
17
|
|
18
18
|
if headers
|
19
|
+
reserve_headers(headers.size)
|
19
20
|
headers.each do |key, value|
|
20
21
|
if value.is_a?(Array)
|
21
22
|
value.each { |v| add_header(key, v) }
|
@@ -40,5 +41,9 @@ module Itsi
|
|
40
41
|
close
|
41
42
|
end
|
42
43
|
end
|
44
|
+
|
45
|
+
def flush
|
46
|
+
# No-op. Our Rust server performs stream coalescing and automatically flushes on a tight interval.
|
47
|
+
end
|
43
48
|
end
|
44
49
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Itsi
|
4
|
+
module RackEnvPool
|
5
|
+
|
6
|
+
RACK_ENV_TEMPLATE = {
|
7
|
+
"SERVER_SOFTWARE" => "Itsi",
|
8
|
+
"rack.errors" => $stderr,
|
9
|
+
"rack.multithread" => true,
|
10
|
+
"rack.multiprocess" => true,
|
11
|
+
"rack.run_once" => false,
|
12
|
+
"rack.hijack?" => true,
|
13
|
+
"rack.multipart.buffer_size" => 16_384,
|
14
|
+
"SCRIPT_NAME" => "",
|
15
|
+
"REQUEST_METHOD" => "",
|
16
|
+
"PATH_INFO" => "",
|
17
|
+
"REQUEST_PATH" => "",
|
18
|
+
"QUERY_STRING" => "",
|
19
|
+
"REMOTE_ADDR" => "",
|
20
|
+
"SERVER_PORT" => "",
|
21
|
+
"SERVER_NAME" => "",
|
22
|
+
"SERVER_PROTOCOL" => "",
|
23
|
+
"HTTP_HOST" => "",
|
24
|
+
"HTTP_VERSION" => "",
|
25
|
+
"itsi.request" => "",
|
26
|
+
"itsi.response" => "",
|
27
|
+
"rack.version" => nil,
|
28
|
+
"rack.url_scheme" => "",
|
29
|
+
"rack.input" => "",
|
30
|
+
"rack.hijack" => ""
|
31
|
+
}.freeze
|
32
|
+
|
33
|
+
POOL = []
|
34
|
+
|
35
|
+
def self.checkout # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
|
36
|
+
POOL.pop&.tap do |recycled|
|
37
|
+
recycled.keys.each do |key|
|
38
|
+
case key
|
39
|
+
when "SERVER_SOFTWARE" then recycled[key] = "Itsi"
|
40
|
+
when "rack.errors" then recycled[key] = $stderr
|
41
|
+
when "rack.multithread", "rack.multiprocess", "rack.hijack?" then recycled[key] = true
|
42
|
+
when "rack.run_once" then recycled[key] = false
|
43
|
+
when "rack.multipart.buffer_size" then recycled[key] = 16_384
|
44
|
+
when "SCRIPT_NAME", "REQUEST_METHOD", "PATH_INFO", "REQUEST_PATH", "QUERY_STRING", "REMOTE_ADDR",
|
45
|
+
"SERVER_PORT", "SERVER_NAME", "SERVER_PROTOCOL", "HTTP_HOST", "HTTP_VERSION", "itsi.request",
|
46
|
+
"itsi.response", "rack.version", "rack.url_scheme", "rack.input", "rack.hijack"
|
47
|
+
nil
|
48
|
+
else recycled.delete(key)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end || RACK_ENV_TEMPLATE.dup
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.checkin(env)
|
55
|
+
POOL << env
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -1,3 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
1
4
|
module Itsi
|
2
5
|
class Server
|
3
6
|
module Config
|
@@ -55,8 +58,7 @@ module Itsi
|
|
55
58
|
nested_locations: [],
|
56
59
|
middleware_loader: lambda do
|
57
60
|
@options[:nested_locations].each(&:call)
|
58
|
-
@middleware[:app]
|
59
|
-
@middleware[:app][:app_proc] = @middleware[:app]&.[](:preloader)&.call || DEFAULT_APP[]
|
61
|
+
@middleware[:app] = { app_proc: DEFAULT_APP[] } unless @middleware[:app] || @middleware[:static_assets]
|
60
62
|
[flatten_routes, Config.errors_to_error_lines(errors)]
|
61
63
|
end
|
62
64
|
}
|
@@ -74,7 +76,7 @@ module Itsi
|
|
74
76
|
define_method(option_name) do |*args, **kwargs, &blk|
|
75
77
|
option.new(self, *args, **kwargs, &blk).build!
|
76
78
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
77
|
-
@errors << [e,
|
79
|
+
@errors << [e, e.backtrace.find { |r| !(r =~ %r{server/config}) }]
|
78
80
|
end
|
79
81
|
end
|
80
82
|
|
@@ -85,7 +87,7 @@ module Itsi
|
|
85
87
|
rescue Config::Endpoint::InvalidHandlerException => e
|
86
88
|
@errors << [e, "#{e.backtrace[0]}:in #{e.message}"]
|
87
89
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
88
|
-
@errors << [e,
|
90
|
+
@errors << [e, e.backtrace.find { |r| !(r =~ %r{server/config}) }]
|
89
91
|
end
|
90
92
|
end
|
91
93
|
|
@@ -13,7 +13,7 @@ You can enable several different compression algorithms, and choose to selective
|
|
13
13
|
min_size: 1024 # 1KiB,
|
14
14
|
algorithms: %w[zstd gzip deflate br],
|
15
15
|
compress_streams: true,
|
16
|
-
mime_types: %[all],
|
16
|
+
mime_types: %w[all],
|
17
17
|
level: "fastest"
|
18
18
|
```
|
19
19
|
|
@@ -24,7 +24,7 @@ You can enable several different compression algorithms, and choose to selective
|
|
24
24
|
compress \
|
25
25
|
min_size: 1024 # 1KiB,
|
26
26
|
algorithms: %w[zstd gzip deflate br],
|
27
|
-
mime_types: %[image],
|
27
|
+
mime_types: %w[image],
|
28
28
|
level: "fastest"
|
29
29
|
|
30
30
|
static_assets: \
|
@@ -47,4 +47,4 @@ You can enable several different compression algorithms, and choose to selective
|
|
47
47
|
# Pre-compressed `static_assets`
|
48
48
|
Itsi also supports serving pre-compressed static assets directly from the file-system.
|
49
49
|
This is configured inside the `static_assets` middleware.
|
50
|
-
Go to the [static_assets](/middleware/static_assets
|
50
|
+
Go to the [static_assets](/middleware/static_assets) middleware for more information.
|
@@ -12,7 +12,7 @@ proxy \
|
|
12
12
|
to: "http://backend.example.com/api{path}{query}",
|
13
13
|
backends: ["127.0.0.1:3001", "127.0.0.1:3002"],
|
14
14
|
backend_priority: "round_robin",
|
15
|
-
headers: { "X-Forwarded-For" =>
|
15
|
+
headers: { "X-Forwarded-For" => "{addr}" },
|
16
16
|
verify_ssl: false,
|
17
17
|
timeout: 30,
|
18
18
|
tls_sni: true,
|
@@ -54,7 +54,7 @@ proxy \
|
|
54
54
|
3. **Header Overrides**
|
55
55
|
The `headers` option lets you specify extra or overriding headers. Each header value may be a literal or a string rewrite. For example, overriding `"X-Forwarded-For"` to carry the client’s IP is done by:
|
56
56
|
```ruby
|
57
|
-
{ "X-Forwarded-For" =>
|
57
|
+
{ "X-Forwarded-For" => "{addr}" }
|
58
58
|
```
|
59
59
|
|
60
60
|
4. **Request Forwarding and Error Handling**
|
@@ -7,7 +7,7 @@ module Itsi
|
|
7
7
|
to: "${1:http://backend.example.com{path_and_query}",
|
8
8
|
backends: [${2:"127.0.0.1:3001", "127.0.0.1:3002"}],
|
9
9
|
backend_priority: ${3|"round_robin","ordered","random"|},
|
10
|
-
headers: { ${4| "X-Forwarded-For" =>
|
10
|
+
headers: { ${4| "X-Forwarded-For" => "{addr}"|} },
|
11
11
|
verify_ssl: ${5|true,false|},
|
12
12
|
timeout: ${6|30,60|},
|
13
13
|
tls_sni: ${7|true,false|},
|
@@ -24,12 +24,12 @@ module Itsi
|
|
24
24
|
super(location, params)
|
25
25
|
raise "Rackup file must be a string" unless app.is_a?(String)
|
26
26
|
|
27
|
-
@app =
|
27
|
+
@app = app
|
28
28
|
end
|
29
29
|
|
30
30
|
def build!
|
31
31
|
app_args = {
|
32
|
-
preloader: -> { @app },
|
32
|
+
preloader: -> { Itsi::Server::RackInterface.for(@app) },
|
33
33
|
sendfile: @params[:sendfile],
|
34
34
|
nonblocking: @params[:nonblocking],
|
35
35
|
script_name: @params[:script_name],
|
@@ -2,9 +2,8 @@ module Itsi
|
|
2
2
|
class Server
|
3
3
|
module Config
|
4
4
|
class AutoReloadConfig < Option
|
5
|
-
|
6
5
|
insert_text <<~SNIPPET
|
7
|
-
|
6
|
+
auto_reload_config! # Auto-reload the server configuration each time it changes.
|
8
7
|
SNIPPET
|
9
8
|
|
10
9
|
detail "Auto-reload the server configuration each time it changes."
|
@@ -14,24 +13,30 @@ module Itsi
|
|
14
13
|
end
|
15
14
|
|
16
15
|
def build!
|
16
|
+
return if @auto_reloading
|
17
|
+
|
18
|
+
src = caller.find { |l| !(l =~ %r{lib/itsi/server/config}) }.split(":").first
|
19
|
+
|
17
20
|
location.instance_eval do
|
18
21
|
return if @auto_reloading
|
19
22
|
|
20
23
|
if @included
|
21
24
|
@included.each do |file|
|
25
|
+
next if "#{file}" == src
|
26
|
+
|
22
27
|
if ENV["BUNDLE_BIN_PATH"]
|
23
|
-
watch "#{file}
|
28
|
+
watch "#{file}", [%w[bundle exec itsi restart]]
|
24
29
|
else
|
25
|
-
watch "#{file}
|
30
|
+
watch "#{file}", [%w[itsi restart]]
|
26
31
|
end
|
27
32
|
end
|
28
33
|
end
|
29
34
|
@auto_reloading = true
|
30
35
|
|
31
36
|
if ENV["BUNDLE_BIN_PATH"]
|
32
|
-
watch
|
37
|
+
watch src, [%w[bundle exec itsi restart]]
|
33
38
|
else
|
34
|
-
watch
|
39
|
+
watch src, [%w[itsi restart]]
|
35
40
|
end
|
36
41
|
end
|
37
42
|
end
|
@@ -7,6 +7,7 @@ Use the `include` option to load additional files to be evaluated within the cur
|
|
7
7
|
You can use this option to split a large configuration file into multiple smaller files.
|
8
8
|
|
9
9
|
Files required using `include` are also subject to auto-reloading, when using the [auto_reload_config](/options/auto_reload_config) option.
|
10
|
+
The path of the included file is evaluated relative to the current configuration file.
|
10
11
|
|
11
12
|
## Examples
|
12
13
|
```ruby {filename="Itsi.rb"}
|
@@ -2,34 +2,39 @@ module Itsi
|
|
2
2
|
class Server
|
3
3
|
module Config
|
4
4
|
class Include < Option
|
5
|
-
|
6
5
|
insert_text "include \"${1|other_file|}\" # Include another file to be loaded within the current configuration"
|
7
6
|
|
8
7
|
detail "Include another file to be loaded within the current configuration"
|
9
8
|
|
10
9
|
schema do
|
11
|
-
Type(String)
|
10
|
+
Type(String) & Required()
|
12
11
|
end
|
13
12
|
|
14
13
|
def build!
|
15
|
-
|
14
|
+
caller_location = caller_locations(2, 1).first.path
|
15
|
+
included_file = \
|
16
|
+
if caller_location =~ %r{lib/itsi/server}
|
17
|
+
File.expand_path("#{@params}.rb")
|
18
|
+
else
|
19
|
+
File.expand_path("#{@params}.rb", File.dirname(caller_location))
|
20
|
+
end
|
21
|
+
|
16
22
|
location.instance_eval do
|
17
23
|
@included ||= []
|
18
24
|
@included << included_file
|
19
25
|
|
20
26
|
if @auto_reloading
|
21
27
|
if ENV["BUNDLE_BIN_PATH"]
|
22
|
-
watch "#{included_file}
|
28
|
+
watch "#{included_file}", [%w[bundle exec itsi restart]]
|
23
29
|
else
|
24
|
-
watch "#{included_file}
|
30
|
+
watch "#{included_file}", [%w[itsi restart]]
|
25
31
|
end
|
26
32
|
end
|
27
33
|
end
|
28
34
|
|
29
|
-
code = IO.read(
|
30
|
-
location.instance_eval(code,
|
35
|
+
code = IO.read(included_file)
|
36
|
+
location.instance_eval(code, included_file, 1)
|
31
37
|
end
|
32
|
-
|
33
38
|
end
|
34
39
|
end
|
35
40
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
---
|
2
|
+
title: Pipeline Flush
|
3
|
+
url: /options/pipeline_flush
|
4
|
+
---
|
5
|
+
|
6
|
+
Aggregates flushes to better support pipelined responses. (HTTP1 only)
|
7
|
+
The default value is `false`.
|
8
|
+
|
9
|
+
## Configuration
|
10
|
+
```ruby {filename=Itsi.rb}
|
11
|
+
pipeline_flush true
|
12
|
+
```
|
13
|
+
|
14
|
+
```ruby {filename=Itsi.rb}
|
15
|
+
pipeline_flush false
|
16
|
+
```
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Itsi
|
2
|
+
class Server
|
3
|
+
module Config
|
4
|
+
class PipelineFlush < Option
|
5
|
+
|
6
|
+
insert_text <<~SNIPPET
|
7
|
+
pipeline_flush ${1|true,false|}
|
8
|
+
SNIPPET
|
9
|
+
|
10
|
+
detail "Aggregates flushes to better support pipelined responses. (HTTP1 only)."
|
11
|
+
|
12
|
+
schema do
|
13
|
+
(Bool() & Required()).default(false)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -2,17 +2,15 @@ module Itsi
|
|
2
2
|
class Server
|
3
3
|
module Config
|
4
4
|
class ReusePort < Option
|
5
|
-
|
6
5
|
insert_text <<~SNIPPET
|
7
|
-
|
6
|
+
reuse_port ${1|true,false|}
|
8
7
|
SNIPPET
|
9
8
|
|
10
9
|
detail "Configures whether the server should set the reuse_port option on the underlying socket."
|
11
10
|
|
12
11
|
schema do
|
13
|
-
(Bool() & Required()).default(
|
12
|
+
(Bool() & Required()).default(true)
|
14
13
|
end
|
15
|
-
|
16
14
|
end
|
17
15
|
end
|
18
16
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
---
|
2
|
+
title: Write Vectored
|
3
|
+
url: /options/writev
|
4
|
+
---
|
5
|
+
|
6
|
+
Set whether HTTP/1 connections should try to use vectored writes,
|
7
|
+
or always flatten into a single buffer.
|
8
|
+
|
9
|
+
Note that setting this to false may mean more copies of body data,
|
10
|
+
but may also improve performance when an IO transport doesn't
|
11
|
+
support vectored writes well, such as most TLS implementations.
|
12
|
+
|
13
|
+
Setting this to true will force hyper to use queued strategy
|
14
|
+
which may eliminate unnecessary cloning on some TLS backends
|
15
|
+
|
16
|
+
Default is `nil` in which case hyper will try to guess which mode to use
|
17
|
+
|
18
|
+
## Configuration
|
19
|
+
```ruby {filename=Itsi.rb}
|
20
|
+
writev true
|
21
|
+
```
|
22
|
+
|
23
|
+
```ruby {filename=Itsi.rb}
|
24
|
+
writev false
|
25
|
+
```
|