itsi-server 0.2.16 → 0.2.17
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 +6 -1
- 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 +6 -15
- data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +26 -5
- 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 +87 -31
- data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +158 -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 +5 -4
- 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 +6 -2
- data/lib/itsi/server/config/options/include.rb +5 -2
- 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/writev.md +25 -0
- data/lib/itsi/server/config/options/writev.rb +19 -0
- data/lib/itsi/server/config.rb +21 -8
- data/lib/itsi/server/default_config/Itsi.rb +1 -4
- 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
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
|
@@ -55,8 +55,9 @@ module Itsi
|
|
55
55
|
nested_locations: [],
|
56
56
|
middleware_loader: lambda do
|
57
57
|
@options[:nested_locations].each(&:call)
|
58
|
-
@middleware[:app]
|
59
|
-
|
58
|
+
if !(@middleware[:app] || @middleware[:static_assets])
|
59
|
+
@middleware[:app] = { app_proc: DEFAULT_APP[]}
|
60
|
+
end
|
60
61
|
[flatten_routes, Config.errors_to_error_lines(errors)]
|
61
62
|
end
|
62
63
|
}
|
@@ -74,7 +75,7 @@ module Itsi
|
|
74
75
|
define_method(option_name) do |*args, **kwargs, &blk|
|
75
76
|
option.new(self, *args, **kwargs, &blk).build!
|
76
77
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
77
|
-
@errors << [e,
|
78
|
+
@errors << [e, e.backtrace.find{|r| !(r =~ /server\/config/) }]
|
78
79
|
end
|
79
80
|
end
|
80
81
|
|
@@ -85,7 +86,7 @@ module Itsi
|
|
85
86
|
rescue Config::Endpoint::InvalidHandlerException => e
|
86
87
|
@errors << [e, "#{e.backtrace[0]}:in #{e.message}"]
|
87
88
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
88
|
-
@errors << [e,
|
89
|
+
@errors << [e, e.backtrace.find{|r| !(r =~ /server\/config/) }]
|
89
90
|
end
|
90
91
|
end
|
91
92
|
|
@@ -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],
|
@@ -14,11 +14,15 @@ module Itsi
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def build!
|
17
|
+
return if @auto_reloading
|
18
|
+
src = caller.find{|l| !(l =~ /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}.rb" == src
|
22
26
|
if ENV["BUNDLE_BIN_PATH"]
|
23
27
|
watch "#{file}.rb", [%w[bundle exec itsi restart]]
|
24
28
|
else
|
@@ -29,9 +33,9 @@ module Itsi
|
|
29
33
|
@auto_reloading = true
|
30
34
|
|
31
35
|
if ENV["BUNDLE_BIN_PATH"]
|
32
|
-
watch
|
36
|
+
watch src, [%w[bundle exec itsi restart]]
|
33
37
|
else
|
34
|
-
watch
|
38
|
+
watch src, [%w[itsi restart]]
|
35
39
|
end
|
36
40
|
end
|
37
41
|
end
|
@@ -26,8 +26,11 @@ module Itsi
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
|
-
|
29
|
+
filename = File.expand_path("#{included_file}.rb")
|
30
|
+
|
31
|
+
code = IO.read(filename)
|
32
|
+
location.instance_eval(code, filename, 1)
|
33
|
+
|
31
34
|
end
|
32
35
|
|
33
36
|
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
|
@@ -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
|
+
```
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Itsi
|
2
|
+
class Server
|
3
|
+
module Config
|
4
|
+
class Writev < Option
|
5
|
+
|
6
|
+
insert_text <<~SNIPPET
|
7
|
+
writev ${1|true,false|}
|
8
|
+
SNIPPET
|
9
|
+
|
10
|
+
detail "Set whether HTTP/1 connections should try to use vectored writes"
|
11
|
+
|
12
|
+
schema do
|
13
|
+
(Bool() & Required()).default(false)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/itsi/server/config.rb
CHANGED
@@ -44,9 +44,9 @@ module Itsi
|
|
44
44
|
rate_limit key: "address", store_config: "in_memory", requests: 5, seconds: 10
|
45
45
|
etag type: "strong", algorithm: "md5", min_body_size: 1024 * 1024
|
46
46
|
compress min_size: 1024 * 1024, level: "fastest", algorithms: %w[zstd gzip br deflate],
|
47
|
-
|
47
|
+
mime_types: %w[all], compress_streams: true
|
48
48
|
log_requests before: { level: "DEBUG", format: "[{request_id}] {method} {path_and_query} - {addr} " },
|
49
|
-
|
49
|
+
after: { level: "DEBUG",
|
50
50
|
format: "[{request_id}] └─ {status} in {response_time}" }
|
51
51
|
nodelay false
|
52
52
|
static_assets \
|
@@ -65,14 +65,19 @@ module Itsi
|
|
65
65
|
}
|
66
66
|
end
|
67
67
|
elsif File.exist?(config_file_path.to_s)
|
68
|
-
DSL.evaluate
|
68
|
+
DSL.evaluate do
|
69
|
+
include config_file_path.gsub(".rb", "")
|
70
|
+
rackup_file args[:rackup_file], script_name: "/" if args.key?(:rackup_file)
|
71
|
+
end
|
69
72
|
elsif File.exist?("./config.ru")
|
70
73
|
DSL.evaluate do
|
71
74
|
preload true
|
72
|
-
rackup_file args.fetch(:rackup_file, "./config.ru")
|
75
|
+
rackup_file args.fetch(:rackup_file, "./config.ru"), script_name: "/"
|
73
76
|
end
|
74
77
|
else
|
75
|
-
DSL.evaluate
|
78
|
+
DSL.evaluate do
|
79
|
+
rackup_file args[:rackup_file], script_name: "/" if args.key?(:rackup_file)
|
80
|
+
end
|
76
81
|
end
|
77
82
|
|
78
83
|
itsifile_config.transform_keys!(&:to_sym)
|
@@ -106,7 +111,7 @@ module Itsi
|
|
106
111
|
end
|
107
112
|
|
108
113
|
srv_config = {
|
109
|
-
workers: args.fetch(:workers) { itsifile_config.fetch(:workers,
|
114
|
+
workers: args.fetch(:workers) { itsifile_config.fetch(:workers, nil) },
|
110
115
|
worker_memory_limit: args.fetch(:worker_memory_limit) { itsifile_config.fetch(:worker_memory_limit, nil) },
|
111
116
|
silence: args.fetch(:silence) { itsifile_config.fetch(:silence, false) },
|
112
117
|
shutdown_timeout: args.fetch(:shutdown_timeout) { itsifile_config.fetch(:shutdown_timeout, 5) },
|
@@ -127,7 +132,7 @@ module Itsi
|
|
127
132
|
multithreaded_reactor: args.fetch(:multithreaded_reactor) do
|
128
133
|
itsifile_config.fetch(:multithreaded_reactor, nil)
|
129
134
|
end,
|
130
|
-
pin_worker_cores: args.fetch(:pin_worker_cores) { itsifile_config.fetch(:pin_worker_cores,
|
135
|
+
pin_worker_cores: args.fetch(:pin_worker_cores) { itsifile_config.fetch(:pin_worker_cores, false) },
|
131
136
|
scheduler_class: args.fetch(:scheduler_class) { itsifile_config.fetch(:scheduler_class, nil) },
|
132
137
|
oob_gc_responses_threshold: args.fetch(:oob_gc_responses_threshold) do
|
133
138
|
itsifile_config.fetch(:oob_gc_responses_threshold, nil)
|
@@ -139,9 +144,15 @@ module Itsi
|
|
139
144
|
log_format: args.fetch(:log_format) { itsifile_config.fetch(:log_format, nil) },
|
140
145
|
log_target: args.fetch(:log_target) { itsifile_config.fetch(:log_target, nil) },
|
141
146
|
log_target_filters: args.fetch(:log_target_filters) { itsifile_config.fetch(:log_target_filters, nil) },
|
147
|
+
pipeline_flush: itsifile_config.fetch(:pipeline_flush, true),
|
148
|
+
writev: itsifile_config.fetch(:writev, false),
|
149
|
+
max_concurrent_streams: itsifile_config.fetch(:max_concurrent_streams, nil),
|
150
|
+
max_local_error_reset_streams: itsifile_config.fetch(:max_local_error_reset_streams, nil),
|
151
|
+
max_header_list_size: itsifile_config.fetch(:max_header_list_size, 2 * 1024 * 1024),
|
152
|
+
max_send_buf_size: itsifile_config.fetch(:max_send_buf_size, 64 * 1024),
|
142
153
|
binds: args.fetch(:binds) { itsifile_config.fetch(:binds, ["http://0.0.0.0:3000"]) },
|
143
154
|
middleware_loader: middleware_loader,
|
144
|
-
listeners: args.fetch(:listeners
|
155
|
+
listeners: args.fetch(:listeners, nil),
|
145
156
|
reuse_address: itsifile_config.fetch(:reuse_address, true),
|
146
157
|
reuse_port: itsifile_config.fetch(:reuse_port, true),
|
147
158
|
listen_backlog: itsifile_config.fetch(:listen_backlog, 1024),
|
@@ -222,6 +233,8 @@ module Itsi
|
|
222
233
|
|
223
234
|
# Find config file path, if it exists.
|
224
235
|
def self.config_file_path(config_file_path = nil)
|
236
|
+
raise "Config file #{config_file_path} does not exist" if config_file_path && !File.exist?(config_file_path)
|
237
|
+
|
225
238
|
config_file_path ||= \
|
226
239
|
if File.exist?(ITSI_DEFAULT_CONFIG_FILE)
|
227
240
|
ITSI_DEFAULT_CONFIG_FILE
|
@@ -10,10 +10,7 @@ env = ENV.fetch("APP_ENV") { ENV.fetch("RACK_ENV", "development") }
|
|
10
10
|
|
11
11
|
# Number of worker processes to spawn
|
12
12
|
# If more than 1, Itsi will be booted in Cluster mode
|
13
|
-
workers ENV
|
14
|
-
require "etc"
|
15
|
-
env == "development" ? 1 : nil
|
16
|
-
}
|
13
|
+
workers ENV["ITSI_WORKERS"]&.to_i || env == "development" ? 1 : nil
|
17
14
|
|
18
15
|
# Number of threads to spawn per worker process
|
19
16
|
# For pure CPU bound applicationss, you'll get the best results keeping this number low
|
@@ -79,7 +79,7 @@ module Itsi
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def handle_client_streaming(active_call)
|
82
|
-
response = service.send(active_call.method_name, active_call
|
82
|
+
response = service.send(active_call.method_name, active_call)
|
83
83
|
active_call.remote_send(response)
|
84
84
|
end
|
85
85
|
|
@@ -94,7 +94,7 @@ module Itsi
|
|
94
94
|
end
|
95
95
|
|
96
96
|
def handle_bidi_streaming(active_call)
|
97
|
-
result = service.send(active_call.method_name, active_call.
|
97
|
+
result = service.send(active_call.method_name, active_call.each, active_call) do |response|
|
98
98
|
active_call.remote_send(response)
|
99
99
|
end
|
100
100
|
return unless result.is_a?(Enumerator)
|
@@ -12,7 +12,7 @@ module Itsi
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
lambda do |request|
|
15
|
-
Server.respond(request, app.call(request.to_rack_env))
|
15
|
+
Server.respond(request, app.call(env = request.to_rack_env), env)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
@@ -20,14 +20,14 @@ module Itsi
|
|
20
20
|
# Here we build the env, and invoke the Rack app's call method.
|
21
21
|
# We then turn the Rack response into something Itsi server understands.
|
22
22
|
def call(app, request)
|
23
|
-
respond request, app.call(request.to_rack_env)
|
23
|
+
respond request, app.call(env = request.to_rack_env), env
|
24
24
|
end
|
25
25
|
|
26
26
|
# Itsi responses are asynchronous and can be streamed.
|
27
27
|
# Response chunks are sent using response.send_frame
|
28
28
|
# and the response is finished using response.close_write.
|
29
29
|
# If only a single chunk is written, you can use the #send_and_close method.
|
30
|
-
def respond(request, (status, headers, body))
|
30
|
+
def respond(request, (status, headers, body), env)
|
31
31
|
response = request.response
|
32
32
|
|
33
33
|
# Don't try and respond if we've been hijacked.
|
@@ -39,13 +39,16 @@ module Itsi
|
|
39
39
|
|
40
40
|
# 2. Set Headers
|
41
41
|
body_streamer = streaming_body?(body) ? body : headers.delete("rack.hijack")
|
42
|
-
|
43
|
-
|
42
|
+
|
43
|
+
response.reserve_headers(headers.size)
|
44
|
+
|
45
|
+
for key, value in headers
|
46
|
+
case value
|
47
|
+
when String then response[key] = value
|
48
|
+
when Array
|
44
49
|
value.each do |v|
|
45
50
|
response[key] = v
|
46
51
|
end
|
47
|
-
elsif value.is_a?(String)
|
48
|
-
response[key] = value
|
49
52
|
end
|
50
53
|
end
|
51
54
|
|
@@ -53,15 +56,15 @@ module Itsi
|
|
53
56
|
# As soon as we start setting the response
|
54
57
|
# the server will begin to stream it to the client.
|
55
58
|
|
56
|
-
# If we're partially hijacked or returned a streaming body,
|
57
|
-
# stream this response.
|
58
59
|
|
59
60
|
if body_streamer
|
61
|
+
# If we're partially hijacked or returned a streaming body,
|
62
|
+
# stream this response.
|
60
63
|
body_streamer.call(response)
|
61
64
|
|
62
|
-
# If we're enumerable with more than one chunk
|
63
|
-
# also stream, otherwise write in a single chunk
|
64
65
|
elsif body.respond_to?(:each) || body.respond_to?(:to_ary)
|
66
|
+
# If we're enumerable with more than one chunk
|
67
|
+
# also stream, otherwise write in a single chunk
|
65
68
|
unless body.respond_to?(:each)
|
66
69
|
body = body.to_ary
|
67
70
|
raise "Body #to_ary didn't return an array" unless body.is_a?(Array)
|
@@ -78,8 +81,10 @@ module Itsi
|
|
78
81
|
else
|
79
82
|
response.send_and_close(body.to_s)
|
80
83
|
end
|
84
|
+
rescue EOFError
|
85
|
+
response.close
|
81
86
|
ensure
|
82
|
-
|
87
|
+
RackEnvPool.checkin(env)
|
83
88
|
body.close if body.respond_to?(:close)
|
84
89
|
end
|
85
90
|
|
data/lib/itsi/server/version.rb
CHANGED
data/lib/itsi/server.rb
CHANGED
@@ -12,6 +12,7 @@ require_relative "server/rack/handler/itsi"
|
|
12
12
|
require_relative "server/config"
|
13
13
|
require_relative "server/typed_handlers"
|
14
14
|
require_relative "standard_headers"
|
15
|
+
require_relative "rack_env_pool"
|
15
16
|
require_relative "http_request"
|
16
17
|
require_relative "http_response"
|
17
18
|
require_relative "passfile"
|