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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +3 -1
  3. data/exe/itsi +8 -5
  4. data/ext/itsi_acme/Cargo.toml +1 -1
  5. data/ext/itsi_scheduler/Cargo.toml +1 -1
  6. data/ext/itsi_server/Cargo.toml +3 -1
  7. data/ext/itsi_server/src/lib.rs +6 -1
  8. data/ext/itsi_server/src/ruby_types/itsi_body_proxy/mod.rs +2 -0
  9. data/ext/itsi_server/src/ruby_types/itsi_grpc_call.rs +4 -4
  10. data/ext/itsi_server/src/ruby_types/itsi_grpc_response_stream/mod.rs +14 -13
  11. data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +64 -33
  12. data/ext/itsi_server/src/ruby_types/itsi_http_response.rs +151 -152
  13. data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +422 -110
  14. data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +62 -15
  15. data/ext/itsi_server/src/ruby_types/itsi_server.rs +1 -1
  16. data/ext/itsi_server/src/server/binds/listener.rs +45 -7
  17. data/ext/itsi_server/src/server/frame_stream.rs +142 -0
  18. data/ext/itsi_server/src/server/http_message_types.rs +142 -9
  19. data/ext/itsi_server/src/server/io_stream.rs +28 -5
  20. data/ext/itsi_server/src/server/lifecycle_event.rs +1 -1
  21. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +2 -3
  22. data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +8 -10
  23. data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +2 -3
  24. data/ext/itsi_server/src/server/middleware_stack/middlewares/csp.rs +3 -3
  25. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +54 -56
  26. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +5 -7
  27. data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +5 -5
  28. data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +7 -10
  29. data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +2 -3
  30. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +1 -2
  31. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_response.rs +4 -6
  32. data/ext/itsi_server/src/server/mod.rs +1 -0
  33. data/ext/itsi_server/src/server/process_worker.rs +3 -4
  34. data/ext/itsi_server/src/server/serve_strategy/acceptor.rs +16 -12
  35. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +83 -31
  36. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +166 -142
  37. data/ext/itsi_server/src/server/signal.rs +37 -9
  38. data/ext/itsi_server/src/server/thread_worker.rs +84 -69
  39. data/ext/itsi_server/src/services/itsi_http_service.rs +43 -43
  40. data/ext/itsi_server/src/services/static_file_server.rs +28 -47
  41. data/lib/itsi/http_request.rb +31 -39
  42. data/lib/itsi/http_response.rb +5 -0
  43. data/lib/itsi/rack_env_pool.rb +59 -0
  44. data/lib/itsi/server/config/dsl.rb +6 -4
  45. data/lib/itsi/server/config/middleware/compression.md +3 -3
  46. data/lib/itsi/server/config/middleware/endpoint/controller.md +1 -1
  47. data/lib/itsi/server/config/middleware/proxy.md +2 -2
  48. data/lib/itsi/server/config/middleware/proxy.rb +1 -1
  49. data/lib/itsi/server/config/middleware/rackup_file.rb +2 -2
  50. data/lib/itsi/server/config/options/auto_reload_config.rb +11 -6
  51. data/lib/itsi/server/config/options/include.md +1 -0
  52. data/lib/itsi/server/config/options/include.rb +13 -8
  53. data/lib/itsi/server/config/options/pipeline_flush.md +16 -0
  54. data/lib/itsi/server/config/options/pipeline_flush.rb +19 -0
  55. data/lib/itsi/server/config/options/reuse_port.rb +2 -4
  56. data/lib/itsi/server/config/options/writev.md +25 -0
  57. data/lib/itsi/server/config/options/writev.rb +19 -0
  58. data/lib/itsi/server/config.rb +22 -9
  59. data/lib/itsi/server/default_config/Itsi.rb +9 -8
  60. data/lib/itsi/server/grpc/grpc_call.rb +2 -0
  61. data/lib/itsi/server/grpc/grpc_interface.rb +2 -2
  62. data/lib/itsi/server/rack/handler/itsi.rb +3 -1
  63. data/lib/itsi/server/rack_interface.rb +17 -12
  64. data/lib/itsi/server/scheduler_interface.rb +2 -0
  65. data/lib/itsi/server/version.rb +1 -1
  66. data/lib/itsi/server.rb +1 -0
  67. data/lib/ruby_lsp/itsi/addon.rb +12 -13
  68. 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(BoxBody::new(Full::new(Bytes::new())))
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(BoxBody::new(Full::new(Bytes::new())))
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(BoxBody::new(Full::new(Bytes::new())))
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
- .map_ok(Frame::data)
692
- .map_err(move |e| {
693
- warn!("Error streaming file {}: {}", path_clone.display(), e);
694
- unreachable!("We handle IO errors above")
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<BoxBody<Bytes, Infallible>> {
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
- .map_ok(Frame::data)
711
- .map_err(move |e| {
712
- warn!("Error streaming file {}: {}", path_clone.display(), e);
713
- unreachable!("We handle IO errors above")
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(BoxBody::new(Full::new(Bytes::new())))
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(BoxBody::new(Full::new(Bytes::new()))).unwrap();
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(BoxBody::new(Full::new(Bytes::new())))
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(BoxBody::new(Full::new(Bytes::new()))).unwrap();
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
- BoxBody::new(Full::new(range_bytes)),
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>) -> BoxBody<Bytes, Infallible> {
991
- BoxBody::new(Full::new(bytes.as_ref().clone()))
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() -> http::Response<BoxBody<Bytes, Infallible>> {
976
+ fn build_not_modified_response() -> HttpResponse {
996
977
  Response::builder()
997
978
  .status(StatusCode::NOT_MODIFIED)
998
- .body(BoxBody::new(Full::new(Bytes::new())))
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: BoxBody<Bytes, Infallible>,
1013
- ) -> http::Response<BoxBody<Bytes, Infallible>> {
993
+ body: HttpBody,
994
+ ) -> HttpResponse {
1014
995
  let mut response = Response::new(body);
1015
996
 
1016
997
  *response.status_mut() = status;
@@ -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 = if header == "content-type"
18
- "CONTENT_TYPE"
19
- elsif header == "content-length"
20
- "CONTENT_LENGTH"
21
- else
22
- "HTTP_#{header.upcase.gsub(/-/, "_")}"
23
- end
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.tap do |hm|
26
- hm.default_proc = proc { |_, key| "HTTP_#{key.upcase.gsub(/-/, "_")}" }
27
- end
26
+ end.to_h
28
27
 
29
- RACK_ENV_TEMPLATE = {
30
- "SERVER_SOFTWARE" => "Itsi",
31
- "rack.errors" => $stderr,
32
- "rack.multithread" => true,
33
- "rack.multiprocess" => true,
34
- "rack.run_once" => false,
35
- "rack.hijack?" => true,
36
- "rack.multipart.buffer_size" => 16_384,
37
- "SCRIPT_NAME" => "",
38
- "REQUEST_METHOD" => "",
39
- "PATH_INFO" => "",
40
- "REQUEST_PATH" => "",
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 = RACK_ENV_TEMPLATE.dup
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"] = [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
- headers.each do |(k, v)|
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"
@@ -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, caller[1]]
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, caller[1]]
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.md) middleware for more information.
50
+ Go to the [static_assets](/middleware/static_assets) middleware for more information.
@@ -181,6 +181,6 @@ module UserController
181
181
  end
182
182
  end
183
183
 
184
- controller User
184
+ controller UserController
185
185
  post "/", :create
186
186
  ```
@@ -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" => { rewrite: "{addr}" } },
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" => { rewrite: "{addr}" } }
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" => { rewrite: "{addr}" },|} },
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 = Itsi::Server::RackInterface.for(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
- auto_reload_config! # Auto-reload the server configuration each time it changes.
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}.rb", [%w[bundle exec itsi restart]]
28
+ watch "#{file}", [%w[bundle exec itsi restart]]
24
29
  else
25
- watch "#{file}.rb", [%w[itsi restart]]
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 "Itsi.rb", [%w[bundle exec itsi restart]]
37
+ watch src, [%w[bundle exec itsi restart]]
33
38
  else
34
- watch "Itsi.rb", [%w[itsi restart]]
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
- included_file = @params
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}.rb", [%w[bundle exec itsi restart]]
28
+ watch "#{included_file}", [%w[bundle exec itsi restart]]
23
29
  else
24
- watch "#{included_file}.rb", [%w[itsi restart]]
30
+ watch "#{included_file}", [%w[itsi restart]]
25
31
  end
26
32
  end
27
33
  end
28
34
 
29
- code = IO.read("#{included_file}.rb")
30
- location.instance_eval(code, "#{included_file}.rb", 1)
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
- reuse_port ${1|true,false|}
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(false)
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
+ ```