diff --git a/engine/artifacts/config-schema.json b/engine/artifacts/config-schema.json index da87de9fb3..d8eb690512 100644 --- a/engine/artifacts/config-schema.json +++ b/engine/artifacts/config-schema.json @@ -415,51 +415,6 @@ ], "format": "uint16", "minimum": 0.0 - }, - "proxy_state_cache_ttl_ms": { - "description": "Proxy state cache TTL in milliseconds.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "route_cache_ttl_ms": { - "description": "Route cache TTL in milliseconds.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "websocket_close_linger_ms": { - "description": "Time to keep TCP connection open after WebSocket close, in milliseconds.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "websocket_max_message_size": { - "description": "Max incoming WebSocket message size in bytes.", - "type": [ - "integer", - "null" - ], - "format": "uint", - "minimum": 0.0 - }, - "websocket_max_outgoing_message_size": { - "description": "Max outgoing WebSocket message size in bytes.", - "type": [ - "integer", - "null" - ], - "format": "uint", - "minimum": 0.0 } }, "additionalProperties": false @@ -572,7 +527,7 @@ "type": "object", "properties": { "actor_allocation_candidate_sample_size": { - "description": "Amount of runners to query from the allocation queue and choose at random when allocating an actor.\n\n**Experimental**", + "description": "Amount of runners to query from the allocation queue and choose at random when allocating an actor.", "type": [ "integer", "null" @@ -581,7 +536,7 @@ "minimum": 0.0 }, "actor_start_threshold": { - "description": "How long to wait after creating and not receiving a starting state before setting actor as lost.\n\nUnit is in milliseconds.\n\n**Experimental**", + "description": "How long to wait after creating and not receiving a starting state before setting actor as lost.\n\nUnit is in milliseconds.", "type": [ "integer", "null" @@ -589,69 +544,15 @@ "format": "int64" }, "actor_stop_threshold": { - "description": "How long to wait after stopping and not receiving a stop state before setting actor as lost.\n\nUnit is in milliseconds.\n\n**Experimental**", + "description": "How long to wait after stopping and not receiving a stop state before setting actor as lost.\n\nUnit is in milliseconds.", "type": [ "integer", "null" ], "format": "int64" }, - "api_max_http_request_body_size": { - "description": "Max HTTP request body size in bytes for API traffic.", - "type": [ - "integer", - "null" - ], - "format": "uint", - "minimum": 0.0 - }, - "api_max_in_flight": { - "description": "Maximum concurrent in-flight requests for API traffic.", - "type": [ - "integer", - "null" - ], - "format": "uint", - "minimum": 0.0 - }, - "api_rate_limit_period_secs": { - "description": "Rate limit for API traffic: period in seconds.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "api_rate_limit_requests": { - "description": "Rate limit for API traffic: number of requests allowed per period.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "api_retry_initial_interval_ms": { - "description": "Initial retry interval for API traffic in milliseconds.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "api_retry_max_attempts": { - "description": "Maximum retry attempts for API traffic.", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, "base_retry_timeout": { - "description": "Time to delay an actor from rescheduling after a rescheduling failure.\n\nUnit is in milliseconds.\n\n**Experimental**", + "description": "Time to delay an actor from rescheduling after a rescheduling failure.\n\nUnit is in milliseconds.", "type": [ "integer", "null" @@ -660,25 +561,7 @@ "minimum": 0.0 }, "default_metadata_poll_interval": { - "description": "Default metadata poll interval for serverless runners when not specified in runner config.\n\nUnit is in milliseconds.\n\n**Experimental**", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "gateway_actor_request_timeout_secs": { - "description": "HTTP request timeout in seconds for actor traffic.\n\nThis is the outer timeout for the entire request lifecycle. Should be slightly longer than `gateway_response_start_timeout_ms` to provide a grace period.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "gateway_api_request_timeout_secs": { - "description": "HTTP request timeout in seconds for API traffic (api-public).", + "description": "Default metadata poll interval for serverless runners when not specified in runner config.\n\nUnit is in milliseconds.", "type": [ "integer", "null" @@ -696,7 +579,7 @@ "minimum": 0.0 }, "gateway_http_max_request_body_size": { - "description": "Max HTTP request body size in bytes for requests to actors.\n\nNote: guard-core also enforces a larger limit (default 256 MiB) as a first line of defense. See `Guard::http_max_request_body_size`.", + "description": "Max HTTP request body size in bytes for requests to actors.", "type": [ "integer", "null" @@ -722,33 +605,6 @@ "format": "uint64", "minimum": 0.0 }, - "gateway_max_in_flight": { - "description": "Maximum concurrent in-flight requests per actor per IP.", - "type": [ - "integer", - "null" - ], - "format": "uint", - "minimum": 0.0 - }, - "gateway_rate_limit_period_secs": { - "description": "Rate limit: period in seconds.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "gateway_rate_limit_requests": { - "description": "Rate limit: number of requests allowed per period.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, "gateway_response_start_timeout_ms": { "description": "Timeout for response to start in milliseconds.", "type": [ @@ -758,24 +614,6 @@ "format": "uint64", "minimum": 0.0 }, - "gateway_retry_initial_interval_ms": { - "description": "Initial retry interval in milliseconds (doubles with each attempt).", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "gateway_retry_max_attempts": { - "description": "Maximum retry attempts for failed requests.", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, "gateway_tunnel_ping_timeout_ms": { "description": "Tunnel ping timeout in milliseconds.", "type": [ @@ -802,44 +640,8 @@ "format": "uint64", "minimum": 0.0 }, - "gateway_ws_connect_timeout_secs": { - "description": "WebSocket connection attempt timeout in seconds.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "gateway_ws_flush_timeout_secs": { - "description": "WebSocket flush timeout in seconds.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "gateway_ws_proxy_timeout_secs": { - "description": "WebSocket proxy task timeout in seconds.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "gateway_ws_send_timeout_secs": { - "description": "WebSocket send message timeout in seconds.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, "hibernating_request_eligible_threshold": { - "description": "How long after last ping before considering a hibernating request disconnected.\n\nUnit is in milliseconds.\n\n**Experimental**", + "description": "How long after last ping before considering a hibernating request disconnected.\n\nUnit is in milliseconds.", "type": [ "integer", "null" @@ -847,7 +649,7 @@ "format": "int64" }, "min_metadata_poll_interval": { - "description": "Minimum metadata poll interval for serverless runners.\n\nThe actual poll interval will be the maximum of this value and the runner config's `metadata_poll_interval` setting. This prevents excessive polling even if the runner config specifies a very short interval.\n\nUnit is in milliseconds.\n\n**Experimental**", + "description": "Minimum metadata poll interval for serverless runners.\n\nThe actual poll interval will be the maximum of this value and the runner config's `metadata_poll_interval` setting. This prevents excessive polling even if the runner config specifies a very short interval.\n\nUnit is in milliseconds.", "type": [ "integer", "null" @@ -865,7 +667,7 @@ "minimum": 0.0 }, "reschedule_backoff_max_exponent": { - "description": "Maximum exponent for the reschedule backoff calculation.\n\nThis controls the maximum backoff duration when rescheduling actors.\n\n**Experimental**", + "description": "Maximum exponent for the reschedule backoff calculation.\n\nThis controls the maximum backoff duration when rescheduling actors.", "type": [ "integer", "null" @@ -874,7 +676,7 @@ "minimum": 0.0 }, "retry_reset_duration": { - "description": "How long an actor goes without retries before it's retry count is reset to 0, effectively resetting its backoff to 0.\n\nUnit is in milliseconds.\n\n**Experimental**", + "description": "How long an actor goes without retries before it's retry count is reset to 0, effectively resetting its backoff to 0.\n\nUnit is in milliseconds.", "type": [ "integer", "null" @@ -882,7 +684,7 @@ "format": "int64" }, "runner_eligible_threshold": { - "description": "How long after last ping before considering a runner ineligible for allocation.\n\nUnit is in milliseconds.\n\n**Experimental**", + "description": "How long after last ping before considering a runner ineligible for allocation.\n\nUnit is in milliseconds.", "type": [ "integer", "null" @@ -917,7 +719,7 @@ "minimum": 0.0 }, "runner_lost_threshold": { - "description": "How long to wait after last ping before forcibly removing a runner from the database and deleting its workflow, evicting all actors.\n\nNote that the runner may still be running and can reconnect.\n\nUnit is in milliseconds.\n\n**Experimental**", + "description": "How long to wait after last ping before forcibly removing a runner from the database and deleting its workflow, evicting all actors.\n\nNote that the runner may still be running and can reconnect.\n\nUnit is in milliseconds.", "type": [ "integer", "null" @@ -925,7 +727,7 @@ "format": "int64" }, "runner_pool_consecutive_successes_to_clear_error": { - "description": "Number of consecutive successes required to clear an active runner pool error.\n\nThis prevents a single success from clearing an error during flapping conditions. Higher values provide more stability but slower recovery from transient errors.\n\n**Experimental**", + "description": "Number of consecutive successes required to clear an active runner pool error.\n\nThis prevents a single success from clearing an error during flapping conditions. Higher values provide more stability but slower recovery from transient errors.", "type": [ "integer", "null" @@ -943,7 +745,7 @@ "minimum": 0.0 }, "serverless_backoff_max_exponent": { - "description": "Maximum exponent for the serverless backoff calculation.\n\nThis controls the maximum backoff duration when serverlessly connecting to runners.\n\n**Experimental**", + "description": "Maximum exponent for the serverless backoff calculation.\n\nThis controls the maximum backoff duration when serverlessly connecting to runners.", "type": [ "integer", "null" @@ -952,7 +754,7 @@ "minimum": 0.0 }, "serverless_base_retry_timeout": { - "description": "Time to delay a serverless runner from attempting a new outbound connection after a connection failure.\n\nUnit is in milliseconds.\n\n**Experimental**", + "description": "Time to delay a serverless runner from attempting a new outbound connection after a connection failure.\n\nUnit is in milliseconds.", "type": [ "integer", "null" @@ -961,7 +763,7 @@ "minimum": 0.0 }, "serverless_drain_grace_period": { - "description": "Drain grace period for serverless runners.\n\nThis time is subtracted from the configured request duration. Once `duration - grace` is reached, the runner is sent stop commands for all of its actors. After the grace period is over (i.e. the full duration is reached) the runner websocket is forcibly closed.\n\nDefault is 10 seconds.\n\nUnit is in milliseconds.\n\n**Experimental**", + "description": "Drain grace period for serverless runners.\n\nThis time is subtracted from the configured request duration. Once `duration - grace` is reached, the runner is sent stop commands for all of its actors. After the grace period is over (i.e. the full duration is reached) the runner websocket is forcibly closed.\n\nDefault is 10 seconds.\n\nUnit is in milliseconds.", "type": [ "integer", "null" @@ -970,7 +772,7 @@ "minimum": 0.0 }, "serverless_retry_reset_duration": { - "description": "How long a serverless runner goes without connection failures before it's retry count is reset to 0, effectively resetting its backoff to 0.\n\nUnit is in milliseconds.\n\n**Experimental**", + "description": "How long a serverless runner goes without connection failures before it's retry count is reset to 0, effectively resetting its backoff to 0.\n\nUnit is in milliseconds.", "type": [ "integer", "null" diff --git a/engine/packages/config/src/config/guard.rs b/engine/packages/config/src/config/guard.rs index 13df499908..8561260905 100644 --- a/engine/packages/config/src/config/guard.rs +++ b/engine/packages/config/src/config/guard.rs @@ -11,16 +11,6 @@ pub struct Guard { pub port: Option, /// Enable & configure HTTPS pub https: Option, - /// Route cache TTL in milliseconds. - pub route_cache_ttl_ms: Option, - /// Proxy state cache TTL in milliseconds. - pub proxy_state_cache_ttl_ms: Option, - /// Time to keep TCP connection open after WebSocket close, in milliseconds. - pub websocket_close_linger_ms: Option, - /// Max incoming WebSocket message size in bytes. - pub websocket_max_message_size: Option, - /// Max outgoing WebSocket message size in bytes. - pub websocket_max_outgoing_message_size: Option, /// Max HTTP request body size in bytes (first line of defense). pub http_max_request_body_size: Option, } @@ -34,29 +24,8 @@ impl Guard { self.port.unwrap_or(crate::defaults::ports::GUARD) } - pub fn route_cache_ttl_ms(&self) -> u64 { - self.route_cache_ttl_ms.unwrap_or(10 * 60 * 1000) // 10 minutes - } - - pub fn proxy_state_cache_ttl_ms(&self) -> u64 { - self.proxy_state_cache_ttl_ms.unwrap_or(60 * 60 * 1000) // 1 hour - } - - pub fn websocket_close_linger_ms(&self) -> u64 { - self.websocket_close_linger_ms.unwrap_or(100) - } - - pub fn websocket_max_message_size(&self) -> usize { - self.websocket_max_message_size.unwrap_or(32 * 1024 * 1024) // 32 MiB - } - - pub fn websocket_max_outgoing_message_size(&self) -> usize { - self.websocket_max_outgoing_message_size - .unwrap_or(32 * 1024 * 1024) // 32 MiB - } - pub fn http_max_request_body_size(&self) -> usize { - self.http_max_request_body_size.unwrap_or(256 * 1024 * 1024) // 256 MiB + self.http_max_request_body_size.unwrap_or(20 * 1024 * 1024) // 20 MiB } } diff --git a/engine/packages/config/src/config/pegboard.rs b/engine/packages/config/src/config/pegboard.rs index 75e9df57c4..df7d2f2eb8 100644 --- a/engine/packages/config/src/config/pegboard.rs +++ b/engine/packages/config/src/config/pegboard.rs @@ -7,39 +7,27 @@ pub struct Pegboard { /// Time to delay an actor from rescheduling after a rescheduling failure. /// /// Unit is in milliseconds. - /// - /// **Experimental** pub base_retry_timeout: Option, /// How long to wait after creating and not receiving a starting state before setting actor as lost. /// /// Unit is in milliseconds. - /// - /// **Experimental** pub actor_start_threshold: Option, /// How long to wait after stopping and not receiving a stop state before setting actor as lost. /// /// Unit is in milliseconds. - /// - /// **Experimental** pub actor_stop_threshold: Option, /// How long an actor goes without retries before it's retry count is reset to 0, effectively resetting its /// backoff to 0. /// /// Unit is in milliseconds. - /// - /// **Experimental** pub retry_reset_duration: Option, /// Maximum exponent for the reschedule backoff calculation. /// /// This controls the maximum backoff duration when rescheduling actors. - /// - /// **Experimental** pub reschedule_backoff_max_exponent: Option, /// How long after last ping before considering a runner ineligible for allocation. /// /// Unit is in milliseconds. - /// - /// **Experimental** pub runner_eligible_threshold: Option, /// How long to wait after last ping before forcibly removing a runner from the database /// and deleting its workflow, evicting all actors. @@ -47,33 +35,23 @@ pub struct Pegboard { /// Note that the runner may still be running and can reconnect. /// /// Unit is in milliseconds. - /// - /// **Experimental** pub runner_lost_threshold: Option, /// How long after last ping before considering a hibernating request disconnected. /// /// Unit is in milliseconds. - /// - /// **Experimental** pub hibernating_request_eligible_threshold: Option, /// Time to delay a serverless runner from attempting a new outbound connection after a connection failure. /// /// Unit is in milliseconds. - /// - /// **Experimental** pub serverless_base_retry_timeout: Option, /// How long a serverless runner goes without connection failures before it's retry count is reset to 0, /// effectively resetting its backoff to 0. /// /// Unit is in milliseconds. - /// - /// **Experimental** pub serverless_retry_reset_duration: Option, /// Maximum exponent for the serverless backoff calculation. /// /// This controls the maximum backoff duration when serverlessly connecting to runners. - /// - /// **Experimental** pub serverless_backoff_max_exponent: Option, /// Global pool desired max. @@ -82,8 +60,6 @@ pub struct Pegboard { /// Default metadata poll interval for serverless runners when not specified in runner config. /// /// Unit is in milliseconds. - /// - /// **Experimental** pub default_metadata_poll_interval: Option, /// Minimum metadata poll interval for serverless runners. @@ -93,20 +69,14 @@ pub struct Pegboard { /// runner config specifies a very short interval. /// /// Unit is in milliseconds. - /// - /// **Experimental** pub min_metadata_poll_interval: Option, /// Number of consecutive successes required to clear an active runner pool error. /// /// This prevents a single success from clearing an error during flapping conditions. /// Higher values provide more stability but slower recovery from transient errors. - /// - /// **Experimental** pub runner_pool_consecutive_successes_to_clear_error: Option, /// Amount of runners to query from the allocation queue and choose at random when allocating an actor. - /// - /// **Experimental** pub actor_allocation_candidate_sample_size: Option, // === Gateway Settings === @@ -125,53 +95,11 @@ pub struct Pegboard { /// Max pending message buffer size for hibernating WebSockets in bytes. pub gateway_hws_max_pending_size: Option, /// Max HTTP request body size in bytes for requests to actors. - /// - /// Note: guard-core also enforces a larger limit (default 256 MiB) as a first line of defense. - /// See `Guard::http_max_request_body_size`. pub gateway_http_max_request_body_size: Option, - /// Rate limit: number of requests allowed per period. - pub gateway_rate_limit_requests: Option, - /// Rate limit: period in seconds. - pub gateway_rate_limit_period_secs: Option, - /// Maximum concurrent in-flight requests per actor per IP. - pub gateway_max_in_flight: Option, - /// HTTP request timeout in seconds for actor traffic. - /// - /// This is the outer timeout for the entire request lifecycle. - /// Should be slightly longer than `gateway_response_start_timeout_ms` to provide a grace period. - pub gateway_actor_request_timeout_secs: Option, - /// HTTP request timeout in seconds for API traffic (api-public). - pub gateway_api_request_timeout_secs: Option, - /// Maximum retry attempts for failed requests. - pub gateway_retry_max_attempts: Option, - /// Initial retry interval in milliseconds (doubles with each attempt). - pub gateway_retry_initial_interval_ms: Option, - /// WebSocket proxy task timeout in seconds. - pub gateway_ws_proxy_timeout_secs: Option, - /// WebSocket connection attempt timeout in seconds. - pub gateway_ws_connect_timeout_secs: Option, - /// WebSocket send message timeout in seconds. - pub gateway_ws_send_timeout_secs: Option, - /// WebSocket flush timeout in seconds. - pub gateway_ws_flush_timeout_secs: Option, - - // === API Settings === - /// Rate limit for API traffic: number of requests allowed per period. - pub api_rate_limit_requests: Option, - /// Rate limit for API traffic: period in seconds. - pub api_rate_limit_period_secs: Option, - /// Maximum concurrent in-flight requests for API traffic. - pub api_max_in_flight: Option, - /// Maximum retry attempts for API traffic. - pub api_retry_max_attempts: Option, - /// Initial retry interval for API traffic in milliseconds. - pub api_retry_initial_interval_ms: Option, - /// Max HTTP request body size in bytes for API traffic. - pub api_max_http_request_body_size: Option, // === Runner Settings === - /// Max HTTP response body size in bytes from actors. - pub runner_http_max_response_body_size: Option, + /// Max response payload size in bytes from actors. + pub runner_max_response_payload_body_size: Option, /// Ping interval for runner updates in milliseconds. pub runner_update_ping_interval_ms: Option, /// GC interval for actor event demuxer in milliseconds. @@ -185,11 +113,7 @@ pub struct Pegboard { /// runner is sent stop commands for all of its actors. After the grace period is over (i.e. the full /// duration is reached) the runner websocket is forcibly closed. /// - /// Default is 10 seconds. - /// /// Unit is in milliseconds. - /// - /// **Experimental** pub serverless_drain_grace_period: Option, } @@ -257,8 +181,6 @@ impl Pegboard { self.actor_allocation_candidate_sample_size.unwrap_or(100) } - // === Gateway Settings === - pub fn gateway_websocket_open_timeout_ms(&self) -> u64 { self.gateway_websocket_open_timeout_ms.unwrap_or(15_000) } @@ -289,87 +211,9 @@ impl Pegboard { .unwrap_or(128 * 1024 * 1024) // 128 MiB } - pub fn gateway_http_max_request_body_size(&self) -> usize { - self.gateway_http_max_request_body_size - .unwrap_or(128 * 1024 * 1024) // 128 MiB - } - - pub fn gateway_rate_limit_requests(&self) -> u64 { - self.gateway_rate_limit_requests.unwrap_or(1200) - } - - pub fn gateway_rate_limit_period_secs(&self) -> u64 { - self.gateway_rate_limit_period_secs.unwrap_or(60) - } - - pub fn gateway_max_in_flight(&self) -> usize { - self.gateway_max_in_flight.unwrap_or(32) - } - - pub fn gateway_actor_request_timeout_secs(&self) -> u64 { - self.gateway_actor_request_timeout_secs.unwrap_or(6 * 60) // 6 minutes - } - - pub fn gateway_api_request_timeout_secs(&self) -> u64 { - self.gateway_api_request_timeout_secs.unwrap_or(60) // 1 minute - } - - pub fn gateway_retry_max_attempts(&self) -> u32 { - self.gateway_retry_max_attempts.unwrap_or(7) - } - - pub fn gateway_retry_initial_interval_ms(&self) -> u64 { - self.gateway_retry_initial_interval_ms.unwrap_or(150) - } - - pub fn gateway_ws_proxy_timeout_secs(&self) -> u64 { - self.gateway_ws_proxy_timeout_secs.unwrap_or(30) - } - - pub fn gateway_ws_connect_timeout_secs(&self) -> u64 { - self.gateway_ws_connect_timeout_secs.unwrap_or(5) - } - - pub fn gateway_ws_send_timeout_secs(&self) -> u64 { - self.gateway_ws_send_timeout_secs.unwrap_or(5) - } - - pub fn gateway_ws_flush_timeout_secs(&self) -> u64 { - self.gateway_ws_flush_timeout_secs.unwrap_or(2) - } - - // === API Settings === - - pub fn api_rate_limit_requests(&self) -> u64 { - self.api_rate_limit_requests.unwrap_or(1200) - } - - pub fn api_rate_limit_period_secs(&self) -> u64 { - self.api_rate_limit_period_secs.unwrap_or(60) - } - - pub fn api_max_in_flight(&self) -> usize { - self.api_max_in_flight.unwrap_or(32) - } - - pub fn api_retry_max_attempts(&self) -> u32 { - self.api_retry_max_attempts.unwrap_or(3) - } - - pub fn api_retry_initial_interval_ms(&self) -> u64 { - self.api_retry_initial_interval_ms.unwrap_or(100) - } - - pub fn api_max_http_request_body_size(&self) -> usize { - self.api_max_http_request_body_size - .unwrap_or(256 * 1024 * 1024) // 256 MiB - } - - // === Runner Settings === - - pub fn runner_http_max_response_body_size(&self) -> usize { - self.runner_http_max_response_body_size - .unwrap_or(128 * 1024 * 1024) // 128 MiB + pub fn runner_max_response_payload_body_size(&self) -> usize { + self.runner_max_response_payload_body_size + .unwrap_or(20 * 1024 * 1024) // 20 MiB } pub fn runner_update_ping_interval_ms(&self) -> u64 { diff --git a/engine/packages/guard-core/src/errors.rs b/engine/packages/guard-core/src/errors.rs index dc774b2a42..ec33b9c613 100644 --- a/engine/packages/guard-core/src/errors.rs +++ b/engine/packages/guard-core/src/errors.rs @@ -127,15 +127,3 @@ pub struct WebSocketServiceTimeout; "WebSocket target changed, retry not possible." )] pub struct WebSocketTargetChanged; - -#[derive(RivetError, Serialize, Deserialize)] -#[error( - "guard", - "request_body_too_large", - "Request body too large.", - "Request body size {size} bytes exceeds maximum allowed {max_size} bytes." -)] -pub struct RequestBodyTooLarge { - pub size: usize, - pub max_size: usize, -} diff --git a/engine/packages/guard-core/src/proxy_service.rs b/engine/packages/guard-core/src/proxy_service.rs index bda3624061..42c4268816 100644 --- a/engine/packages/guard-core/src/proxy_service.rs +++ b/engine/packages/guard-core/src/proxy_service.rs @@ -40,7 +40,6 @@ use crate::{ pub const X_FORWARDED_FOR: HeaderName = HeaderName::from_static("x-forwarded-for"); pub const X_RIVET_ERROR: HeaderName = HeaderName::from_static("x-rivet-error"); -pub const MAX_BODY_SIZE: usize = rivet_util::size::mebibytes(20) as usize; const PROXY_STATE_CACHE_TTL: Duration = Duration::from_secs(60 * 60); // 1 hour const WEBSOCKET_CLOSE_LINGER: Duration = Duration::from_millis(100); // Keep TCP connection open briefly after WebSocket close @@ -724,11 +723,12 @@ impl ProxyService { ResolveRouteOutput::Target(mut target) => { // Read the request body before proceeding with retries let (req_parts, body) = req.into_parts(); - let req_body = Limited::new(body, MAX_BODY_SIZE) - .collect() - .await - .map_err(|err| errors::InvalidRequestBody(err.to_string()).build())? - .to_bytes(); + let req_body = + Limited::new(body, self.state.config.guard().http_max_request_body_size()) + .collect() + .await + .map_err(|err| errors::InvalidRequestBody(err.to_string()).build())? + .to_bytes(); // Use a value-returning loop to handle both errors and successful responses let mut attempts = 0; @@ -800,13 +800,16 @@ impl ProxyService { return Ok(Response::from_parts(parts, streaming_body)); } else { // For non-streaming responses, buffer as before - let body_bytes = Limited::new(body, MAX_BODY_SIZE) - .collect() - .await - .map_err(|err| { - errors::InvalidResponseBody(err.to_string()).build() - })? - .to_bytes(); + let body_bytes = Limited::new( + body, + self.state.config.guard().http_max_request_body_size(), + ) + .collect() + .await + .map_err(|err| { + errors::InvalidResponseBody(err.to_string()).build() + })? + .to_bytes(); let full_body = ResponseBody::Full(Full::new(body_bytes)); return Ok(Response::from_parts(parts, full_body)); @@ -860,11 +863,12 @@ impl ProxyService { ResolveRouteOutput::CustomServe(mut handler) => { // Collect request body let (req_parts, body) = req.into_parts(); - let req_body = Limited::new(body, MAX_BODY_SIZE) - .collect() - .await - .map_err(|err| errors::InvalidRequestBody(err.to_string()).build())? - .to_bytes(); + let req_body = + Limited::new(body, self.state.config.guard().http_max_request_body_size()) + .collect() + .await + .map_err(|err| errors::InvalidRequestBody(err.to_string()).build())? + .to_bytes(); let req_collected = hyper::Request::from_parts(req_parts, Full::::new(req_body)); diff --git a/engine/packages/pegboard-gateway/src/lib.rs b/engine/packages/pegboard-gateway/src/lib.rs index bd267ad50f..a340811ca2 100644 --- a/engine/packages/pegboard-gateway/src/lib.rs +++ b/engine/packages/pegboard-gateway/src/lib.rs @@ -9,7 +9,7 @@ use rivet_error::*; use rivet_guard_core::{ ResponseBody, WebSocketHandle, custom_serve::{CustomServeTrait, HibernationResult}, - errors::{RequestBodyTooLarge, ServiceUnavailable, WebSocketServiceUnavailable}, + errors::{ServiceUnavailable, WebSocketServiceUnavailable}, request_context::{CorsConfig, RequestContext}, utils::is_ws_hibernate, websocket_handle::WebSocketReceiver, @@ -162,20 +162,6 @@ impl PegboardGateway { .context("failed to read body")? .to_bytes(); - // Check request body size limit for requests to actors - let max_request_body_size = self - .ctx - .config() - .pegboard() - .gateway_http_max_request_body_size(); - if body_bytes.len() > max_request_body_size { - return Err(RequestBodyTooLarge { - size: body_bytes.len(), - max_size: max_request_body_size, - } - .build()); - } - let (mut stopped_sub, runner_protocol_version) = tokio::try_join!( ctx.subscribe::(("actor_id", self.actor_id)), get_runner_protocol_version(&ctx, self.runner_id), diff --git a/engine/packages/pegboard-runner/src/ws_to_tunnel_task.rs b/engine/packages/pegboard-runner/src/ws_to_tunnel_task.rs index 90ddea32c5..57cd4ec1ed 100644 --- a/engine/packages/pegboard-runner/src/ws_to_tunnel_task.rs +++ b/engine/packages/pegboard-runner/src/ws_to_tunnel_task.rs @@ -787,7 +787,12 @@ async fn handle_tunnel_message_mk2( let inner_data_len = tunnel_message_inner_data_len_mk2(&msg.message_kind); // Enforce incoming payload size - if inner_data_len > ctx.config().pegboard().runner_http_max_response_body_size() { + if inner_data_len + > ctx + .config() + .pegboard() + .runner_max_response_payload_body_size() + { return Err(errors::WsError::InvalidPacket("payload too large".to_string()).build()); } @@ -835,7 +840,12 @@ async fn handle_tunnel_message_mk1( let inner_data_len = tunnel_message_inner_data_len_mk1(&msg.message_kind); // Enforce incoming payload size - if inner_data_len > ctx.config().pegboard().runner_http_max_response_body_size() { + if inner_data_len + > ctx + .config() + .pegboard() + .runner_max_response_payload_body_size() + { return Err(errors::WsError::InvalidPacket("payload too large".to_string()).build()); } diff --git a/engine/sdks/typescript/runner/src/tunnel.ts b/engine/sdks/typescript/runner/src/tunnel.ts index 0894eaa3e0..8faf11ecf6 100644 --- a/engine/sdks/typescript/runner/src/tunnel.ts +++ b/engine/sdks/typescript/runner/src/tunnel.ts @@ -15,7 +15,7 @@ import { stringifyToClientTunnelMessageKind, stringifyToServerTunnelMessageKind, } from "./stringify"; -import { arraysEqual, idToStr, MAX_BODY_SIZE, stringifyError, unreachable } from "./utils"; +import { arraysEqual, idToStr, MAX_PAYLOAD_SIZE, stringifyError, unreachable } from "./utils"; import { HIBERNATABLE_SYMBOL, WebSocketTunnelAdapter, @@ -855,7 +855,7 @@ export class Tunnel { // Read the body first to get the actual content const body = response.body ? await response.arrayBuffer() : null; - if (body && body.byteLength > MAX_BODY_SIZE) { + if (body && body.byteLength > MAX_PAYLOAD_SIZE) { throw new Error("Response body too large"); } diff --git a/engine/sdks/typescript/runner/src/utils.ts b/engine/sdks/typescript/runner/src/utils.ts index b01d06be85..a6ba6961b4 100644 --- a/engine/sdks/typescript/runner/src/utils.ts +++ b/engine/sdks/typescript/runner/src/utils.ts @@ -1,7 +1,7 @@ import { logger } from "./log"; -// 20MiB. Keep in sync with MAX_BODY_SIZE from engine/packages/guard-core/src/proxy_service.rs -export const MAX_BODY_SIZE = 20 * 1024 * 1024; +// 20MiB. Keep in sync with runner_max_response_payload_body_size from engine/packages/config/src/config/pegboard.rs +export const MAX_PAYLOAD_SIZE = 20 * 1024 * 1024; export function unreachable(x: never): never { throw `Unreachable: ${x}`; diff --git a/engine/sdks/typescript/runner/src/websocket-tunnel-adapter.ts b/engine/sdks/typescript/runner/src/websocket-tunnel-adapter.ts index 75f1f6fbb8..a40f0830d9 100644 --- a/engine/sdks/typescript/runner/src/websocket-tunnel-adapter.ts +++ b/engine/sdks/typescript/runner/src/websocket-tunnel-adapter.ts @@ -1,7 +1,7 @@ import type { Logger } from "pino"; import { VirtualWebSocket, type UniversalWebSocket, type RivetMessageEvent } from "@rivetkit/virtual-websocket"; import type { Tunnel } from "./tunnel"; -import { MAX_BODY_SIZE, wrappingAddU16, wrappingLteU16, wrappingSubU16 } from "./utils"; +import { MAX_PAYLOAD_SIZE, wrappingAddU16, wrappingLteU16, wrappingSubU16 } from "./utils"; export const HIBERNATABLE_SYMBOL = Symbol("hibernatable"); @@ -71,18 +71,18 @@ export class WebSocketTunnelAdapter { if (typeof data === "string") { const encoder = new TextEncoder(); - if (encoder.encode(data).byteLength > MAX_BODY_SIZE) { + if (encoder.encode(data).byteLength > MAX_PAYLOAD_SIZE) { throw new Error("WebSocket message too large"); } messageData = data; } else if (data instanceof ArrayBuffer) { - if (data.byteLength > MAX_BODY_SIZE) throw new Error("WebSocket message too large"); + if (data.byteLength > MAX_PAYLOAD_SIZE) throw new Error("WebSocket message too large"); isBinary = true; messageData = data; } else if (ArrayBuffer.isView(data)) { - if (data.byteLength > MAX_BODY_SIZE) throw new Error("WebSocket message too large"); + if (data.byteLength > MAX_PAYLOAD_SIZE) throw new Error("WebSocket message too large"); isBinary = true; const view = data; diff --git a/website/src/content/docs/actors/limits.mdx b/website/src/content/docs/actors/limits.mdx index 1d3d128974..be654c3fea 100644 --- a/website/src/content/docs/actors/limits.mdx +++ b/website/src/content/docs/actors/limits.mdx @@ -53,8 +53,8 @@ These limits affect actions that do not use `.connect()` and [low-level HTTP req | Name | Soft Limit | Hard Limit | Description | |------|------------|------------|-------------| -| Max request body size | — | 128 MiB | Maximum size of HTTP request bodies. | -| Max response body size | — | 128 MiB | Maximum size of HTTP response bodies. | +| Max request body size | — | 20 MiB | Maximum size of HTTP request bodies. | +| Max response body size | — | 20 MiB | Maximum size of HTTP response bodies. | | Request timeout | — | 5 minutes | Maximum time for a request to complete. | ### Networking