itsi-server 0.2.15 → 0.2.16
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 +73 -73
- data/ext/itsi_scheduler/Cargo.toml +1 -1
- data/ext/itsi_server/Cargo.lock +1 -1
- data/ext/itsi_server/Cargo.toml +1 -1
- data/ext/itsi_server/extconf.rb +3 -1
- data/ext/itsi_server/src/lib.rs +1 -0
- data/ext/itsi_server/src/ruby_types/itsi_grpc_call.rs +2 -2
- data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +9 -11
- data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +6 -1
- data/ext/itsi_server/src/server/binds/listener.rs +4 -1
- data/ext/itsi_server/src/server/http_message_types.rs +1 -1
- data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +32 -34
- data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +3 -4
- data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +23 -38
- data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +65 -14
- data/ext/itsi_server/src/server/middleware_stack/middlewares/max_body.rs +1 -1
- data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +1 -1
- data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +21 -8
- data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +1 -5
- data/ext/itsi_server/src/server/middleware_stack/middlewares/static_response.rs +12 -3
- data/ext/itsi_server/src/server/process_worker.rs +2 -1
- data/ext/itsi_server/src/server/serve_strategy/acceptor.rs +96 -0
- data/ext/itsi_server/src/server/serve_strategy/mod.rs +1 -0
- data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +80 -136
- data/ext/itsi_server/src/server/thread_worker.rs +10 -3
- data/ext/itsi_server/src/services/itsi_http_service.rs +26 -21
- data/ext/itsi_server/src/services/mime_types.rs +185 -183
- data/ext/itsi_server/src/services/rate_limiter.rs +16 -34
- data/ext/itsi_server/src/services/static_file_server.rs +7 -13
- data/lib/itsi/server/config/config_helpers.rb +1 -2
- data/lib/itsi/server/config/middleware/etag.md +3 -7
- data/lib/itsi/server/config/middleware/etag.rb +2 -4
- data/lib/itsi/server/config/options/listen_backlog.rb +1 -1
- data/lib/itsi/server/config/options/send_buffer_size.md +15 -0
- data/lib/itsi/server/config/options/send_buffer_size.rb +19 -0
- data/lib/itsi/server/config.rb +24 -25
- data/lib/itsi/server/route_tester.rb +1 -1
- data/lib/itsi/server/version.rb +1 -1
- metadata +4 -1
@@ -1,4 +1,5 @@
|
|
1
1
|
use async_trait::async_trait;
|
2
|
+
use parking_lot::{Mutex, RwLock};
|
2
3
|
use rand::Rng;
|
3
4
|
use redis::aio::ConnectionManager;
|
4
5
|
use redis::{Client, RedisError, Script};
|
@@ -6,9 +7,9 @@ use serde::Deserialize;
|
|
6
7
|
use std::any::Any;
|
7
8
|
use std::collections::{HashMap, HashSet};
|
8
9
|
use std::result::Result;
|
9
|
-
use std::sync::{Arc, LazyLock
|
10
|
+
use std::sync::{Arc, LazyLock};
|
10
11
|
use std::time::{Duration, Instant};
|
11
|
-
use tokio::sync::
|
12
|
+
use tokio::sync::Mutex as AsyncMutex;
|
12
13
|
use tokio::time::timeout;
|
13
14
|
use tracing::warn;
|
14
15
|
use url::Url;
|
@@ -242,10 +243,10 @@ impl InMemoryRateLimiter {
|
|
242
243
|
/// Cleans up expired entries
|
243
244
|
async fn cleanup(&self) {
|
244
245
|
// Try to get the write lock, but fail open if we can't
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
246
|
+
let now = Instant::now();
|
247
|
+
self.entries
|
248
|
+
.write()
|
249
|
+
.retain(|_, entry| entry.expires_at > now);
|
249
250
|
}
|
250
251
|
|
251
252
|
/// Bans an IP address for the specified duration
|
@@ -258,12 +259,7 @@ impl InMemoryRateLimiter {
|
|
258
259
|
let now = Instant::now();
|
259
260
|
let ban_key = format!("ban:ip:{}", ip);
|
260
261
|
|
261
|
-
|
262
|
-
tracing::error!("Failed to acquire write lock: {}", e);
|
263
|
-
RateLimitError::LockError
|
264
|
-
})?;
|
265
|
-
|
266
|
-
entries.insert(
|
262
|
+
self.entries.write().insert(
|
267
263
|
ban_key,
|
268
264
|
RateLimitEntry {
|
269
265
|
count: 1, // Use count=1 to indicate banned
|
@@ -279,12 +275,7 @@ impl InMemoryRateLimiter {
|
|
279
275
|
let now = Instant::now();
|
280
276
|
let ban_key = format!("ban:ip:{}", ip);
|
281
277
|
|
282
|
-
let
|
283
|
-
tracing::error!("Failed to acquire read lock: {}", e);
|
284
|
-
RateLimitError::LockError
|
285
|
-
})?;
|
286
|
-
|
287
|
-
if let Some(entry) = entries.get(&ban_key) {
|
278
|
+
if let Some(entry) = self.entries.read().get(&ban_key) {
|
288
279
|
if entry.expires_at > now {
|
289
280
|
// IP is banned, return a generic reason since we don't store reasons
|
290
281
|
return Ok(Some("IP address banned".to_string()));
|
@@ -310,7 +301,7 @@ impl RateLimiter for InMemoryRateLimiter {
|
|
310
301
|
|
311
302
|
let now = Instant::now();
|
312
303
|
|
313
|
-
let mut entries = self.entries.write()
|
304
|
+
let mut entries = self.entries.write();
|
314
305
|
|
315
306
|
let entry = entries
|
316
307
|
.entry(key.to_string())
|
@@ -436,7 +427,7 @@ impl RateLimiterStore {
|
|
436
427
|
) -> Result<Arc<RedisRateLimiter>, RateLimitError> {
|
437
428
|
// First check if this URL is known to fail
|
438
429
|
{
|
439
|
-
let failed_urls = self.failed_urls.lock()
|
430
|
+
let failed_urls = self.failed_urls.lock();
|
440
431
|
if failed_urls.contains(connection_url) {
|
441
432
|
return Err(RateLimitError::ConnectionTimeout);
|
442
433
|
}
|
@@ -444,10 +435,7 @@ impl RateLimiterStore {
|
|
444
435
|
|
445
436
|
// Then check if we already have a limiter for this URL
|
446
437
|
{
|
447
|
-
let limiters = self
|
448
|
-
.redis_limiters
|
449
|
-
.lock()
|
450
|
-
.unwrap_or_else(|e| e.into_inner());
|
438
|
+
let limiters = self.redis_limiters.lock();
|
451
439
|
if let Some(limiter) = limiters.get(connection_url) {
|
452
440
|
return Ok(limiter.clone());
|
453
441
|
}
|
@@ -455,7 +443,7 @@ impl RateLimiterStore {
|
|
455
443
|
|
456
444
|
// Get a dedicated mutex for this URL or create a new one if it doesn't exist
|
457
445
|
let url_mutex = {
|
458
|
-
let mut locks = CONNECTION_LOCKS.lock()
|
446
|
+
let mut locks = CONNECTION_LOCKS.lock();
|
459
447
|
|
460
448
|
// Get or create the mutex for this URL
|
461
449
|
locks
|
@@ -476,10 +464,7 @@ impl RateLimiterStore {
|
|
476
464
|
|
477
465
|
// Check again if another thread created the limiter while we were waiting
|
478
466
|
{
|
479
|
-
let limiters = self
|
480
|
-
.redis_limiters
|
481
|
-
.lock()
|
482
|
-
.unwrap_or_else(|e| e.into_inner());
|
467
|
+
let limiters = self.redis_limiters.lock();
|
483
468
|
if let Some(limiter) = limiters.get(connection_url) {
|
484
469
|
return Ok(limiter.clone());
|
485
470
|
}
|
@@ -492,10 +477,7 @@ impl RateLimiterStore {
|
|
492
477
|
let limiter = Arc::new(limiter);
|
493
478
|
|
494
479
|
// Store it for future use
|
495
|
-
let mut limiters = self
|
496
|
-
.redis_limiters
|
497
|
-
.lock()
|
498
|
-
.unwrap_or_else(|e| e.into_inner());
|
480
|
+
let mut limiters = self.redis_limiters.lock();
|
499
481
|
limiters.insert(connection_url.to_string(), limiter.clone());
|
500
482
|
|
501
483
|
Ok(limiter)
|
@@ -503,7 +485,7 @@ impl RateLimiterStore {
|
|
503
485
|
Err(e) => {
|
504
486
|
tracing::error!("Failed to initialize Redis rate limiter: {}", e);
|
505
487
|
// Cache the failure
|
506
|
-
let mut failed_urls = self.failed_urls.lock()
|
488
|
+
let mut failed_urls = self.failed_urls.lock();
|
507
489
|
failed_urls.insert(connection_url.to_string());
|
508
490
|
Err(e)
|
509
491
|
}
|
@@ -19,7 +19,7 @@ use http::{
|
|
19
19
|
use http_body_util::{combinators::BoxBody, Full};
|
20
20
|
use itsi_error::Result;
|
21
21
|
use parking_lot::{Mutex, RwLock};
|
22
|
-
use percent_encoding::
|
22
|
+
use percent_encoding::percent_decode_str;
|
23
23
|
use quick_cache::sync::Cache;
|
24
24
|
use serde::Deserialize;
|
25
25
|
use serde_json::json;
|
@@ -175,7 +175,7 @@ impl CacheEntry {
|
|
175
175
|
let mut hasher = Sha256::new();
|
176
176
|
hasher.update(&bytes);
|
177
177
|
let result = hasher.finalize();
|
178
|
-
general_purpose::STANDARD.encode(result)
|
178
|
+
general_purpose::STANDARD.encode(&result[..16])
|
179
179
|
};
|
180
180
|
let headers_ct = get_mime_type(&path);
|
181
181
|
let headers_etag = format!(r#"W/"{etag}""#).parse().unwrap();
|
@@ -279,7 +279,7 @@ impl StaticFileServer {
|
|
279
279
|
supported_encodings: &[HeaderValue],
|
280
280
|
) -> Option<HttpResponse> {
|
281
281
|
let accept: ResponseFormat = request.accept().into();
|
282
|
-
let resolved = self.resolve(path, abs_path, accept
|
282
|
+
let resolved = self.resolve(path, abs_path, accept).await;
|
283
283
|
|
284
284
|
Some(match resolved {
|
285
285
|
Ok(ResolvedAsset {
|
@@ -1188,7 +1188,6 @@ async fn generate_directory_listing(
|
|
1188
1188
|
|
1189
1189
|
// Generate JSON entries for directories.
|
1190
1190
|
for (name, metadata) in dirs {
|
1191
|
-
let encoded = utf8_percent_encode(&name, NON_ALPHANUMERIC).to_string();
|
1192
1191
|
let modified = metadata
|
1193
1192
|
.modified()
|
1194
1193
|
.ok()
|
@@ -1201,7 +1200,7 @@ async fn generate_directory_listing(
|
|
1201
1200
|
|
1202
1201
|
items.push(json!({
|
1203
1202
|
"name": format!("{}/", name),
|
1204
|
-
"path": format!("{}/",
|
1203
|
+
"path": format!("{}/", name),
|
1205
1204
|
"is_dir": true,
|
1206
1205
|
"size": null,
|
1207
1206
|
"modified": modified,
|
@@ -1210,7 +1209,6 @@ async fn generate_directory_listing(
|
|
1210
1209
|
|
1211
1210
|
// Generate JSON entries for files.
|
1212
1211
|
for (name, metadata) in files {
|
1213
|
-
let encoded = utf8_percent_encode(&name, NON_ALPHANUMERIC).to_string();
|
1214
1212
|
let file_size = metadata.len();
|
1215
1213
|
let formatted_size = if file_size < 1024 {
|
1216
1214
|
format!("{} B", file_size)
|
@@ -1234,7 +1232,7 @@ async fn generate_directory_listing(
|
|
1234
1232
|
|
1235
1233
|
items.push(json!({
|
1236
1234
|
"name": name,
|
1237
|
-
"path":
|
1235
|
+
"path": name,
|
1238
1236
|
"is_dir": false,
|
1239
1237
|
"size": formatted_size,
|
1240
1238
|
"modified": modified_str,
|
@@ -1341,11 +1339,9 @@ async fn generate_directory_listing(
|
|
1341
1339
|
|
1342
1340
|
// Generate rows for directories.
|
1343
1341
|
for (name, metadata) in dirs {
|
1344
|
-
let encoded = utf8_percent_encode(&name, NON_ALPHANUMERIC).to_string();
|
1345
|
-
|
1346
1342
|
rows.push_str(&format!(
|
1347
1343
|
r#"<tr><td><a href="{0}/">{1}/</a></td><td class="size">-</td><td class="date">{2}</td></tr>"#,
|
1348
|
-
|
1344
|
+
name,
|
1349
1345
|
name,
|
1350
1346
|
metadata.modified().ok().map(|m| DateTime::<Utc>::from(m).format("%Y-%m-%d %H:%M:%S").to_string())
|
1351
1347
|
.unwrap_or_else(|| "-".to_string())
|
@@ -1355,8 +1351,6 @@ async fn generate_directory_listing(
|
|
1355
1351
|
|
1356
1352
|
// Generate rows for files.
|
1357
1353
|
for (name, metadata) in files {
|
1358
|
-
let encoded = utf8_percent_encode(&name, NON_ALPHANUMERIC).to_string();
|
1359
|
-
|
1360
1354
|
let file_size = metadata.len();
|
1361
1355
|
let formatted_size = if file_size < 1024 {
|
1362
1356
|
format!("{} B", file_size)
|
@@ -1380,7 +1374,7 @@ async fn generate_directory_listing(
|
|
1380
1374
|
|
1381
1375
|
rows.push_str(&format!(
|
1382
1376
|
r#"<tr><td><a href="{0}">{1}</a></td><td class="size">{2}</td><td class="date">{3}</td></tr>"#,
|
1383
|
-
|
1377
|
+
name, name, formatted_size, modified_str
|
1384
1378
|
));
|
1385
1379
|
rows.push('\n');
|
1386
1380
|
}
|
@@ -97,14 +97,13 @@ module Itsi
|
|
97
97
|
if !self.class.ancestors.include?(Middleware) && !location.parent.nil?
|
98
98
|
raise "#{opt_name} must be set at the top level"
|
99
99
|
end
|
100
|
-
|
101
100
|
@location = location
|
102
101
|
@params = case schema
|
103
102
|
when TypedStruct::Validation
|
104
103
|
schema.validate!(params)
|
105
104
|
when Array
|
106
105
|
default, validation = schema
|
107
|
-
params ? validation.validate!(params) : default
|
106
|
+
!params.nil? ? validation.validate!(params) : default
|
108
107
|
when nil
|
109
108
|
nil
|
110
109
|
else
|
@@ -13,8 +13,7 @@ ETags are useful for optimizing client-side caching, conditional GETs, and reduc
|
|
13
13
|
etag \
|
14
14
|
type: "strong",
|
15
15
|
algorithm: "sha256",
|
16
|
-
min_body_size: 0
|
17
|
-
handle_if_none_match: true
|
16
|
+
min_body_size: 0
|
18
17
|
```
|
19
18
|
|
20
19
|
## ETag Applied to a sub-location
|
@@ -23,8 +22,7 @@ location "/assets" do
|
|
23
22
|
etag \
|
24
23
|
type: "weak",
|
25
24
|
algorithm: "md5",
|
26
|
-
min_body_size: 1024
|
27
|
-
handle_if_none_match: true
|
25
|
+
min_body_size: 1024
|
28
26
|
end
|
29
27
|
```
|
30
28
|
|
@@ -40,12 +38,10 @@ end
|
|
40
38
|
|
41
39
|
- **min_body_size**: Minimum response body size (in bytes) required before an ETag is generated. Use this to skip ETags for small or trivial responses.
|
42
40
|
|
43
|
-
- **handle_if_none_match**: When `true`, incoming requests with a matching `If-None-Match` header will receive a `304 Not Modified` response (instead of a full body), if the ETag matches the computed value.
|
44
|
-
|
45
41
|
## How It Works
|
46
42
|
|
47
43
|
### Before the Response
|
48
|
-
If
|
44
|
+
If the request includes an `If-None-Match` header, the value is stored in the request context for comparison later.
|
49
45
|
|
50
46
|
### After the Response
|
51
47
|
|
@@ -7,8 +7,7 @@ module Itsi
|
|
7
7
|
etag \\
|
8
8
|
type: ${1|"strong","weak"|},
|
9
9
|
algorithm: ${2|"sha256","md5"|},
|
10
|
-
min_body_size: ${3|0,1024|}
|
11
|
-
handle_if_none_match: ${4|true,false|}
|
10
|
+
min_body_size: ${3|0,1024|}
|
12
11
|
SNIPPET
|
13
12
|
|
14
13
|
detail "Enables ETag generation for the server."
|
@@ -17,8 +16,7 @@ module Itsi
|
|
17
16
|
{
|
18
17
|
type: (Enum(["strong", "weak"]) & Required()).default("strong"),
|
19
18
|
algorithm: (Enum(["sha256", "md5"]) & Required()).default("sha256"),
|
20
|
-
min_body_size: Range(0...1024 ** 3).default(0)
|
21
|
-
handle_if_none_match: Bool().default(true)
|
19
|
+
min_body_size: Range(0...1024 ** 3).default(0)
|
22
20
|
}
|
23
21
|
end
|
24
22
|
end
|
@@ -4,7 +4,7 @@ module Itsi
|
|
4
4
|
class ListenBacklog < Option
|
5
5
|
|
6
6
|
insert_text <<~SNIPPET
|
7
|
-
listen_backlog ${1|
|
7
|
+
listen_backlog ${1|1024,2048,4096|}
|
8
8
|
SNIPPET
|
9
9
|
|
10
10
|
detail "Specifies the size of the listen backlog for the socket. Larger backlog sizes can improve performance for high-throughput applications by allowing more pending connections to queue, but may increase memory usage. The default value is 1024."
|
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
title: Send Buffer Size
|
3
|
+
url: /options/send_buffer_size
|
4
|
+
---
|
5
|
+
|
6
|
+
Configures the size of the send buffer for the socket. Larger buffer sizes can improve performance for high-throughput applications but may increase memory usage. The default value is 262,144 bytes.
|
7
|
+
|
8
|
+
## Configuration
|
9
|
+
```ruby {filename=Itsi.rb}
|
10
|
+
send_buffer_size 262_144
|
11
|
+
```
|
12
|
+
|
13
|
+
```ruby {filename=Itsi.rb}
|
14
|
+
send_buffer_size 1_048_576
|
15
|
+
```
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Itsi
|
2
|
+
class Server
|
3
|
+
module Config
|
4
|
+
class SendBufferSize < Option
|
5
|
+
|
6
|
+
insert_text <<~SNIPPET
|
7
|
+
send_buffer_size ${1|262_144,1_048_576|}
|
8
|
+
SNIPPET
|
9
|
+
|
10
|
+
detail "Specifies the size of the send buffer for the socket. Larger buffer sizes can improve performance for high-throughput applications but may increase memory usage. The default value is 262,144 bytes."
|
11
|
+
|
12
|
+
schema do
|
13
|
+
(Type(Integer) & Range(1..Float::INFINITY) & Required()).default(262_144)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/itsi/server/config.rb
CHANGED
@@ -41,29 +41,28 @@ module Itsi
|
|
41
41
|
DSL.evaluate(&builder_proc)
|
42
42
|
elsif args[:static]
|
43
43
|
DSL.evaluate do
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
end
|
44
|
+
rate_limit key: "address", store_config: "in_memory", requests: 5, seconds: 10
|
45
|
+
etag type: "strong", algorithm: "md5", min_body_size: 1024 * 1024
|
46
|
+
compress min_size: 1024 * 1024, level: "fastest", algorithms: %w[zstd gzip br deflate],
|
47
|
+
mime_types: %w[all], compress_streams: true
|
48
|
+
log_requests before: { level: "DEBUG", format: "[{request_id}] {method} {path_and_query} - {addr} " },
|
49
|
+
after: { level: "DEBUG",
|
50
|
+
format: "[{request_id}] └─ {status} in {response_time}" }
|
51
|
+
nodelay false
|
52
|
+
static_assets \
|
53
|
+
relative_path: true,
|
54
|
+
allowed_extensions: [],
|
55
|
+
root_dir: ".",
|
56
|
+
not_found_behavior: { error: "not_found" },
|
57
|
+
auto_index: true,
|
58
|
+
try_html_extension: true,
|
59
|
+
max_file_size_in_memory: 1024 * 1024, # 1MB
|
60
|
+
max_files_in_memory: 1000,
|
61
|
+
file_check_interval: 1,
|
62
|
+
serve_hidden_files: false,
|
63
|
+
headers: {
|
64
|
+
"X-Content-Type-Options" => "nosniff"
|
65
|
+
}
|
67
66
|
end
|
68
67
|
elsif File.exist?(config_file_path.to_s)
|
69
68
|
DSL.evaluate(config_file_path)
|
@@ -106,7 +105,6 @@ module Itsi
|
|
106
105
|
Server.write_pid
|
107
106
|
end
|
108
107
|
|
109
|
-
|
110
108
|
srv_config = {
|
111
109
|
workers: args.fetch(:workers) { itsifile_config.fetch(:workers, 1) },
|
112
110
|
worker_memory_limit: args.fetch(:worker_memory_limit) { itsifile_config.fetch(:worker_memory_limit, nil) },
|
@@ -148,7 +146,8 @@ module Itsi
|
|
148
146
|
reuse_port: itsifile_config.fetch(:reuse_port, true),
|
149
147
|
listen_backlog: itsifile_config.fetch(:listen_backlog, 1024),
|
150
148
|
nodelay: itsifile_config.fetch(:nodelay, true),
|
151
|
-
recv_buffer_size: itsifile_config.fetch(:recv_buffer_size, 262_144)
|
149
|
+
recv_buffer_size: itsifile_config.fetch(:recv_buffer_size, 262_144),
|
150
|
+
send_buffer_size: itsifile_config.fetch(:send_buffer_size, 262_144)
|
152
151
|
}.transform_keys(&:to_s)
|
153
152
|
|
154
153
|
[srv_config, errors_to_error_lines(errors)]
|
@@ -23,7 +23,7 @@ module Itsi
|
|
23
23
|
when "cors"
|
24
24
|
"\e[33mcors\e[0m(#{mw_args["allow_origins"].join(" ")}, #{mw_args["allow_methods"].join(" ")})"
|
25
25
|
when "etag"
|
26
|
-
"\e[33metag\e[0m(#{mw_args["type"]}/#{mw_args["algorithm"]}
|
26
|
+
"\e[33metag\e[0m(#{mw_args["type"]}/#{mw_args["algorithm"]})"
|
27
27
|
when "cache_control"
|
28
28
|
"\e[33mcache_control\e[0m(max_age: #{mw_args["max_age"]}, #{mw_args.select do |_, v|
|
29
29
|
v == true
|
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.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wouter Coppieters
|
@@ -233,6 +233,7 @@ files:
|
|
233
233
|
- ext/itsi_server/src/server/process_worker.rs
|
234
234
|
- ext/itsi_server/src/server/redirect_type.rs
|
235
235
|
- ext/itsi_server/src/server/request_job.rs
|
236
|
+
- ext/itsi_server/src/server/serve_strategy/acceptor.rs
|
236
237
|
- ext/itsi_server/src/server/serve_strategy/cluster_mode.rs
|
237
238
|
- ext/itsi_server/src/server/serve_strategy/mod.rs
|
238
239
|
- ext/itsi_server/src/server/serve_strategy/single_mode.rs
|
@@ -501,6 +502,8 @@ files:
|
|
501
502
|
- lib/itsi/server/config/options/ruby_thread_request_backlog_size.rb
|
502
503
|
- lib/itsi/server/config/options/scheduler_threads.md
|
503
504
|
- lib/itsi/server/config/options/scheduler_threads.rb
|
505
|
+
- lib/itsi/server/config/options/send_buffer_size.md
|
506
|
+
- lib/itsi/server/config/options/send_buffer_size.rb
|
504
507
|
- lib/itsi/server/config/options/shutdown_timeout.md
|
505
508
|
- lib/itsi/server/config/options/shutdown_timeout.rb
|
506
509
|
- lib/itsi/server/config/options/stream_body.md
|