From 9cf54e1b9fc7c082f59a87ceab6dc2a5a2eda8dd Mon Sep 17 00:00:00 2001 From: biondizzle Date: Tue, 17 Feb 2026 15:02:57 -0500 Subject: [PATCH] make this configurable --- TODO.md | 2 +- main.odin | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 125 insertions(+), 10 deletions(-) diff --git a/TODO.md b/TODO.md index 5ddb6ee..4e5d940 100644 --- a/TODO.md +++ b/TODO.md @@ -8,7 +8,7 @@ Goal: "aws cli works reliably for CreateTable/ListTables/PutItem/GetItem/DeleteI ### 1) HTTP + routing hardening - [ ] Audit request parsing boundaries: - - Max body size enforcement (config exists, need to verify enforcement path) + - Max body size enforcement — **DONE** - Missing/invalid headers → correct DynamoDB error types - Content-Type handling (be permissive but consistent) - [x] Ensure **all request-scoped allocations** come from the request arena (no accidental long-lived allocs) diff --git a/main.odin b/main.odin index fdab1d6..e11272c 100644 --- a/main.odin +++ b/main.odin @@ -13,6 +13,13 @@ Config :: struct { port: int, data_dir: string, verbose: bool, + + // HTTP server config + max_body_size: int, + max_headers: int, + read_buffer_size: int, + enable_keep_alive: bool, + max_requests_per_connection: int, } main :: proc() { @@ -36,8 +43,14 @@ main :: proc() { fmt.printfln("Storage engine initialized at %s", config.data_dir) fmt.printfln("Starting DynamoDB-compatible server on %s:%d", config.host, config.port) - // Create HTTP server - server_config := default_server_config() + // Create HTTP server with config values + server_config := Server_Config{ + max_body_size = config.max_body_size, + max_headers = config.max_headers, + read_buffer_size = config.read_buffer_size, + enable_keep_alive = config.enable_keep_alive, + max_requests_per_connection = config.max_requests_per_connection, + } server, server_ok := server_init( context.allocator, @@ -2015,14 +2028,20 @@ make_error_response :: proc(response: ^HTTP_Response, err_type: dynamodb.DynamoD parse_config :: proc() -> Config { config := Config{ - host = "0.0.0.0", - port = 8002, - data_dir = "./data", - verbose = false, + // Defaults + host = "0.0.0.0", + port = 8002, + data_dir = "./data", + verbose = false, + max_body_size = 100 * 1024 * 1024, // 100 MB + max_headers = 100, + read_buffer_size = 8 * 1024, // 8 KB + enable_keep_alive = true, + max_requests_per_connection = 1000, } - // Environment variables - if port_str, env_ok := os.lookup_env("JORMUN_PORT"); env_ok { + // Environment variables (lower priority) + if port_str, ok := os.lookup_env("JORMUN_PORT"); ok { if port, parse_ok := strconv.parse_int(port_str); parse_ok { config.port = port } @@ -2040,11 +2059,107 @@ parse_config :: proc() -> Config { config.verbose = verbose == "1" } - // TODO: Parse command line arguments + if max_body_str, ok := os.lookup_env("JORMUN_MAX_BODY_SIZE"); ok { + if max_body, parse_ok := strconv.parse_int(max_body_str); parse_ok { + config.max_body_size = max_body + } + } + + // Command line arguments (highest priority) + args := os.args[1:] // Skip program name + + for i := 0; i < len(args); i += 1 { + arg := args[i] + + // Helper to get next arg value + get_value :: proc(args: []string, i: ^int) -> (string, bool) { + if i^ + 1 < len(args) { + i^ += 1 + return args[i^], true + } + return "", false + } + + switch arg { + case "--host", "-h": + if value, ok := get_value(args, &i); ok { + config.host = value + } + case "--port", "-p": + if value, ok := get_value(args, &i); ok { + if port, parse_ok := strconv.parse_int(value); parse_ok { + config.port = port + } + } + case "--data-dir", "-d": + if value, ok := get_value(args, &i); ok { + config.data_dir = value + } + case "--verbose", "-v": + config.verbose = true + case "--max-body-size": + if value, ok := get_value(args, &i); ok { + if size, parse_ok := strconv.parse_int(value); parse_ok { + config.max_body_size = size + } + } + case "--max-headers": + if value, ok := get_value(args, &i); ok { + if count, parse_ok := strconv.parse_int(value); parse_ok { + config.max_headers = count + } + } + case "--no-keep-alive": + config.enable_keep_alive = false + case "--help": + print_help() + os.exit(0) + } + } return config } +print_help :: proc() { + help_text := ` +JormunDB - DynamoDB-Compatible Database Server + +USAGE: + jormundb [OPTIONS] + +OPTIONS: + --host, -h Server bind address (default: 0.0.0.0) + --port, -p Server port (default: 8002) + --data-dir, -d Data directory path (default: ./data) + --verbose, -v Enable verbose logging + --max-body-size Maximum request body size in bytes (default: 104857600 = 100MB) + --max-headers Maximum number of headers per request (default: 100) + --no-keep-alive Disable HTTP keep-alive connections + --help Show this help message + +ENVIRONMENT VARIABLES: + JORMUN_HOST Same as --host + JORMUN_PORT Same as --port + JORMUN_DATA_DIR Same as --data-dir + JORMUN_VERBOSE Set to "1" to enable verbose mode + JORMUN_MAX_BODY_SIZE Same as --max-body-size + +EXAMPLES: + # Start with default settings + jormundb + + # Custom port and data directory + jormundb --port 9000 --data-dir /var/lib/jormundb + + # Limit body size to 10MB + jormundb --max-body-size 10485760 + + # Use environment variables + JORMUN_PORT=9000 JORMUN_HOST=127.0.0.1 jormundb +` + fmt.println(help_text) +} + print_banner :: proc(config: Config) { banner := ` ╔═══════════════════════════════════════════════╗