128 lines
5.4 KiB
Markdown
128 lines
5.4 KiB
Markdown
# 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:
|
|
|
|
```json
|
|
"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:
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```text
|
|
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:
|
|
|
|
```http
|
|
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:
|
|
|
|
```json
|
|
"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:
|
|
|
|
```text
|
|
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.
|