Request Timeouts and Limits

This guide covers configuring request timeouts, body size limits, and other server settings in Armature.

Table of Contents

Overview

Armature provides configurable request timeouts and size limits to protect your application from:

  • Slow clients - Connections that take too long to send data
  • Large payloads - Requests that exceed memory limits
  • Slow handlers - Request handlers that take too long to respond
  • Resource exhaustion - Too many headers or oversized headers

Features

  • โœ… Configurable request timeout for entire request lifecycle
  • โœ… Separate body read timeout for large uploads
  • โœ… Maximum body size limits with early rejection
  • โœ… Maximum header size and count limits
  • โœ… Keep-alive configuration
  • โœ… Preset configurations for common use cases
  • โœ… JSON error responses with detailed messages

Basic Usage

Default Configuration

By default, Armature uses sensible defaults:

use armature_framework::prelude::*;

#[tokio::main]
async fn main() {
    // Uses default ServerConfig:
    // - 30 second request timeout
    // - 60 second body timeout
    // - 1MB max body size
    // - 8KB max header size
    let app = Application::create::<AppModule>().await;
    app.listen(3000).await.unwrap();
}

Custom Configuration

use armature_framework::prelude::*;
use std::time::Duration;

#[tokio::main]
async fn main() {
    let config = ServerConfig::new()
        .request_timeout(Duration::from_secs(60))
        .body_timeout(Duration::from_secs(120))
        .max_body_size(10 * 1024 * 1024)  // 10MB
        .max_header_size(16 * 1024);      // 16KB

    let app = Application::create_with_config::<AppModule>(config).await;
    app.listen(3000).await.unwrap();
}

Configuration Options

ServerConfig Fields

Option Default Description
request_timeout 30 seconds Maximum time for entire request (headers + body + handler)
body_timeout 60 seconds Maximum time to read the request body
max_body_size 1MB Maximum request body size in bytes
max_header_size 8KB Maximum total header size in bytes
max_headers 100 Maximum number of headers
keep_alive true Enable HTTP keep-alive connections
keep_alive_timeout 5 seconds Keep-alive timeout duration

Builder Methods

use armature_framework::prelude::*;
use std::time::Duration;

let config = ServerConfig::new()
    // Request timing
    .request_timeout(Duration::from_secs(30))
    .body_timeout(Duration::from_secs(60))

    // Size limits
    .max_body_size(1_048_576)     // 1MB
    .max_header_size(8_192)       // 8KB
    .max_headers(100)

    // Connection settings
    .keep_alive(true)
    .keep_alive_timeout(Duration::from_secs(5));

Preset Configurations

Armature provides preset configurations for common use cases:

API Server (Default)

Optimized for typical API workloads:

let config = ServerConfig::api();
// - Request timeout: 30s
// - Body timeout: 30s
// - Max body: 1MB

File Upload Server

Optimized for handling large file uploads:

let config = ServerConfig::file_upload();
// - Request timeout: 5 minutes
// - Body timeout: 10 minutes
// - Max body: 100MB

No Timeout

For internal services with other timeout mechanisms:

let config = ServerConfig::no_timeout();
// โš ๏ธ Use with caution! Only for trusted internal services.

Timeout Behavior

Request Timeout

The request timeout covers the entire request lifecycle after headers are received:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                  Request Timeout                     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  Read Body  โ”‚  Execute Handler  โ”‚  Send Response    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

If the timeout expires, a 408 Request Timeout response is sent.

Body Timeout

The body timeout specifically covers reading the request body:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚    Body Timeout     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚     Read Body       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

This is separate from the request timeout to allow longer times for large uploads while keeping handler execution fast.

Error Responses

Armature returns JSON error responses when limits are exceeded:

408 Request Timeout

{
  "error": "Request Timeout",
  "message": "Request timed out after 30 seconds",
  "status": 408
}

413 Payload Too Large

{
  "error": "Payload Too Large",
  "message": "Request body too large: 5242880 bytes (max: 1048576 bytes)",
  "status": 413
}

431 Request Header Fields Too Large

{
  "error": "Request Header Fields Too Large",
  "message": "Too many headers: 150 (max: 100)",
  "status": 431
}

Best Practices

1. Set Appropriate Timeouts

Consider your application's needs:

// API endpoint - fast responses expected
let api_config = ServerConfig::new()
    .request_timeout(Duration::from_secs(10));

// Long-running report generation
let report_config = ServerConfig::new()
    .request_timeout(Duration::from_secs(300));

2. Size Limits Based on Content

// JSON API - small payloads
let json_api = ServerConfig::new()
    .max_body_size(100 * 1024);  // 100KB

// Image upload API
let image_api = ServerConfig::new()
    .max_body_size(5 * 1024 * 1024);  // 5MB

// Video upload API
let video_api = ServerConfig::new()
    .max_body_size(500 * 1024 * 1024);  // 500MB

3. Consider Client Network Conditions

// Mobile-friendly settings
let mobile_config = ServerConfig::new()
    .body_timeout(Duration::from_secs(120))  // Slower uploads
    .request_timeout(Duration::from_secs(60));

4. Defense in Depth

Combine with other protections:

use armature_framework::prelude::*;
use armature_ratelimit::*;

// Server-level limits
let config = ServerConfig::new()
    .max_body_size(1024 * 1024)
    .request_timeout(Duration::from_secs(30));

// Plus rate limiting
let rate_limit = RateLimiter::new(100, Duration::from_secs(60));

Examples

Complete API Server

use armature_framework::prelude::*;
use std::time::Duration;

#[module]
struct AppModule;

#[controller("/api")]
struct ApiController;

#[controller]
impl ApiController {
    #[post("/data")]
    async fn create_data(&self, #[body] data: CreateRequest) -> HttpResponse {
        // Handler implementation
        HttpResponse::created()
    }
}

#[tokio::main]
async fn main() {
    // Initialize logging
    let _guard = Application::init_logging();

    // Configure server
    let config = ServerConfig::new()
        .request_timeout(Duration::from_secs(30))
        .body_timeout(Duration::from_secs(30))
        .max_body_size(1024 * 1024)  // 1MB
        .max_headers(50)
        .keep_alive(true)
        .keep_alive_timeout(Duration::from_secs(10));

    // Create and run application
    let app = Application::create_with_config::<AppModule>(config).await;

    println!("Server configuration:");
    println!("  Request timeout: {:?}", app.config().request_timeout);
    println!("  Max body size: {} bytes", app.config().max_body_size);

    app.listen(3000).await.unwrap();
}

File Upload Server

use armature_framework::prelude::*;
use std::time::Duration;

#[module]
struct UploadModule;

#[controller("/upload")]
struct UploadController;

#[controller]
impl UploadController {
    #[post("/file")]
    async fn upload_file(&self, request: HttpRequest) -> HttpResponse {
        let fields = request.multipart()?;
        // Process uploaded file
        HttpResponse::ok()
    }
}

#[tokio::main]
async fn main() {
    let config = ServerConfig::file_upload()
        .max_body_size(200 * 1024 * 1024);  // 200MB limit

    let app = Application::create_with_config::<UploadModule>(config).await;
    app.listen(3000).await.unwrap();
}

Mixed Configuration with Middleware

For applications needing different limits for different routes, use middleware:

use armature_framework::prelude::*;

// Custom middleware to check body size per-route
async fn check_body_size(
    request: HttpRequest,
    max_size: usize,
) -> Result<HttpRequest, Error> {
    if request.body.len() > max_size {
        return Err(Error::PayloadTooLarge(format!(
            "Body exceeds limit of {} bytes",
            max_size
        )));
    }
    Ok(request)
}

Common Pitfalls

  • โŒ Setting timeouts too low for legitimate use cases

  • โŒ Allowing unlimited body sizes on public endpoints

  • โŒ Using no_timeout() on public-facing services

  • โŒ Not considering slow network conditions

  • โœ… Testing timeout behavior in development

  • โœ… Setting limits based on actual requirements

  • โœ… Monitoring timeout and size limit errors

  • โœ… Providing clear error messages to clients

Summary

Configuration Use Case
ServerConfig::api() Standard REST APIs
ServerConfig::file_upload() File upload services
Custom config Specific requirements

Key Points:

  1. Always set timeouts - Protect against slow clients and stuck handlers
  2. Size limits are critical - Prevent memory exhaustion attacks
  3. Use presets - Start with api() or file_upload() for common cases
  4. Monitor errors - Track 408 and 413 responses to tune settings
  5. Test thoroughly - Verify timeout behavior before production