Files
RustProxy/docs/V0.29.1_IMPLEMENTATION.md
T
2026-10-08 22:22:46 +02:00

5.4 KiB

RustProxy 0.29.1 implementation note

RustProxy 0.29.1 adds explicit Nginx-style internal error routing without parsing Nginx configuration, plus multipart byte ranges and optional PHP PATH_INFO support. SQLite remains schema 9 and Cold Cache remains format 3.

Internal error fallback

A vhost main source and each Custom location can define:

"error_fallback": {
  "target": "redirect-check",
  "statuses": [404]
}

target is the stable id of an enabled Custom location in the same vhost with "internal": true. Internal locations are retained in the compiled route table but never participate in normal public URI matching. The supported intercepted status set is deliberately bounded to 403, 404, 500, 502, 503 and 504; omitting statuses defaults to 404.

The dispatch is internal, not a 3xx client redirect. RustProxy rebuilds the fallback request with the original HTTP method, URI, query string, HTTP version, request headers and the existing transport/request context. The target location then executes through the normal vhost/location path, including ACL/authentication, header rules, cache policy, compression and response processing. Vhost rate limiting is not charged a second time for the same client request.

For requests with a body, RustProxy can replay a body only when its exact size is known and no larger than 8 MiB. Such a request is buffered once so the same bytes can be sent to the fallback target. Unknown-size or larger streaming bodies continue to the original target, but an error fallback cannot replay them; the original error response is retained.

Configuration validation rejects missing/public fallback targets and recursive fallback cycles. A runtime depth limit of 8 internal dispatches remains as a second guard against loops after hot-reload/state inconsistencies.

Configured upstream response statuses are intercepted before normal response finalization and cache publication. RustProxy-generated reverse-proxy failures also participate where applicable: missing backend (500), unavailable/capacity (503), connect failure (503), invalid upstream response (502) and response timeout (504). A configured stale cached response still wins before transport-error fallback.

Example: static files -> application -> redirector

The following vhost fragment expresses the same request flow as an Nginx try_files/error_page chain, without an HTTP redirect between stages:

{
  "source": {
    "type": "web_server",
    "web_service_id": "www-linuxiarz"
  },
  "error_fallback": {
    "target": "to8000",
    "statuses": [404]
  },
  "backends": [
    {
      "id": "app",
      "url": "http://127.0.0.1:8000",
      "enabled": true,
      "allow_private": true,
      "address_allowlist": ["127.0.0.1/32"]
    },
    {
      "id": "redirector",
      "url": "http://127.0.0.1:9000",
      "enabled": true,
      "allow_private": true,
      "address_allowlist": ["127.0.0.1/32"]
    }
  ],
  "routes": [
    {
      "id": "to8000",
      "internal": true,
      "backend_id": "app",
      "source": {"type": "reverse_proxy"},
      "error_fallback": {
        "target": "redirect-check",
        "statuses": [404]
      }
    },
    {
      "id": "redirect-check",
      "internal": true,
      "backend_id": "redirector",
      "source": {"type": "reverse_proxy"},
      "cache": false
    }
  ]
}

The effective flow is:

public request
  -> Web Server
     2xx/3xx -> return normally
     404     -> @to8000 (internal)
                  -> backend app
                     non-404 -> return normally
                     404     -> @redirect-check (internal)
                                  -> redirector backend

Normal public Custom locations can still route /stats, /user-info, /health, RBL files or other paths directly before the main source is used. An internal location's path_prefix is not used for public matching; its id is the fallback name.

Multipart byte ranges

The Web Server now accepts comma-separated byte ranges such as:

Range: bytes=0-99,200-299

One satisfiable range retains the existing single-part 206 response. Multiple satisfiable ranges produce a streaming multipart/byteranges response with per-part Content-Type and Content-Range. At most 16 ranges are accepted. Malformed or entirely unsatisfiable range sets return 416. Multipart responses use the identity file representation rather than .br/.gz sidecars so the multipart MIME framing and Content-Encoding semantics cannot conflict.

PHP PATH_INFO

Web Service PHP configuration gains the default-off field:

"php": {
  "enabled": true,
  "path_info": true
}

When enabled, RustProxy may resolve a request such as /index.php/foo/bar by finding the longest existing configured PHP script prefix under the Web Service document root. FastCGI then receives, conceptually:

SCRIPT_FILENAME=/document/root/index.php
SCRIPT_NAME=/index.php
PATH_INFO=/foo/bar
REQUEST_URI=/index.php/foo/bar

The split uses the same document-root confinement and PHP file checks as normal script resolution. It is disabled by default because it changes URL-to-script resolution semantics.

Compatibility

No SQLite migration is required. Existing vhosts, locations and Web Services deserialize unchanged because internal, error_fallback and PHP path_info use backward-compatible defaults. Existing single-range responses are retained.