Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ regex = "1.13.0"
log = "0.4.33"
rustyline = "18.0.1"
tokio = { version = "1.52.3", features = ["full"] }
reqwest = { version = "0.13.4", features = ["json"] }
reqwest = { version = "0.13.4", features = ["json", "stream"] }
encoding_rs = "0.8.35"
# sqlx 0.9 split the old `runtime-tokio-rustls` feature into a separate runtime
# and TLS backend; `tls-rustls` aliases the ring-backed rustls stack we used before.
sqlx = { version = "0.9.0", features = ["runtime-tokio", "tls-rustls", "sqlite", "mysql", "postgres", "chrono"] }
Expand Down
9 changes: 9 additions & 0 deletions Docs/04-advanced-features/interoperability.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ Non-2xx statuses are not errors — check `resp.ok` or `resp.status`
yourself. Network failures (DNS, connection refused) still raise errors you
can `try`/`catch`.

Outbound responses are streamed and decoded into a bounded buffer. The
`web_server_max_response_size` setting (64 MiB by default) limits the response
body for `read content` and `read response`, both as received and after text
decoding. The limit includes chunked responses with no declared length. Outside
a `main loop`, the connection and body read share the script's remaining
`timeout_seconds`; inside a lifetime-exempt `main loop`, each request gets a
fresh timeout of that duration. Cooperative cancellation also interrupts a
request that is waiting on the remote peer.
Comment on lines +95 to +102

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Non-trivial behavior change ships without the required Dev Diary entry

This change adds new outbound HTTP streaming/bounding behavior and documents it in the guides (Docs/04-advanced-features/interoperability.md:95-102), but it does not include a Dev Diary entry, which the repository rules require for any non-trivial feature or behavior change.
Impact: The change set violates the repository's mandatory documentation policy, so a required record of this behavior change is missing.

Repo rule requiring a Dev Diary entry

CLAUDE.md and AGENTS.md state under Documentation Development that a non-trivial feature or behavior change must ship "A Dev Diary entry in Dev diary/" in the same change. This PR alters outbound open url semantics (streaming, response ceiling on received and decoded bytes, per-request timeouts, cooperative cancellation) — clearly non-trivial — but the diff adds no file under Dev diary/.

Prompt for agents
The repository rules (CLAUDE.md / AGENTS.md, Documentation Development section) require a Dev Diary entry in the Dev diary/ directory for any non-trivial feature or behavior change. This PR changes outbound HTTP (open url) behavior substantially: streaming responses, enforcing web_server_max_response_size on received and decoded bytes including chunked bodies, applying execution-budget timeouts to connect/headers/body, and cooperative cancellation. Add a dated Dev Diary markdown entry (matching the existing naming convention like 2026-07-16-bounded-outbound-http.md) describing the motivation, behavior change, and validation.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


**Note:** inside an `open url` statement the words `method`, `headers`, and
`body` introduce clauses, so use different variable names there (e.g.
`request_headers`, `payload`).
Expand Down
15 changes: 12 additions & 3 deletions Docs/reference/configuration-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ All keys currently loaded from config files, with defaults.
| `web_server_tls_cert_file` | path | *(none)* | Default PEM cert for bare `listen … secured` |
| `web_server_tls_key_file` | path | *(none)* | Default PEM key for bare `listen … secured` |
| `web_server_max_body_size` | integer ≥ 1 | `1048576` (1 MiB) | Max HTTP request body size (bytes); enforced while streaming (chunked-safe) |
| `web_server_max_response_size` | integer ≥ 1 | `67108864` (64 MiB) | Max HTTP response body size (bytes) |
| `web_server_max_response_size` | integer ≥ 1 | `67108864` (64 MiB) | Max handler or outbound HTTP response body size (bytes) |
| `web_server_request_queue_bound` | integer ≥ 1 | `256` | Max queued HTTP requests before shedding with 503 |
| `web_server_response_timeout_seconds` | integer ≥ 0 | `300` | Seconds to await a handler before shedding with 504; `0` disables |
| `web_socket_queue_bound` | integer ≥ 1 | `1024` | Max queued frames/events per WebSocket channel before shedding |
Expand Down Expand Up @@ -248,7 +248,11 @@ budget.

#### `timeout_seconds`

Maximum execution time for a WFL script in seconds. The script terminates if it exceeds this limit.
Maximum execution time for a WFL script in seconds. Outside a `main loop`, an
outbound `open url` request (connection, headers, and response body) consumes
the run's remaining time. A `main loop` remains exempt from the lifetime limit,
but each outbound request inside it gets this duration as a fresh finite timeout
so a stalled remote peer cannot wedge the server indefinitely.

- **Type:** Integer (minimum: 1)
- **Default:** `60`
Expand Down Expand Up @@ -524,7 +528,12 @@ Because request handlers run one at a time (see [Web Servers → Limitations](..

#### `web_server_max_response_size`

Maximum HTTP response body a handler may `respond with`, in bytes. A larger response is refused (the handler gets a runtime error) rather than streaming an unbounded payload to the client.
Maximum HTTP response body size, in bytes, for both directions: content a
handler may `respond with`, and content an outbound `open url` statement may
read. A larger handler response is refused; a larger outbound response is
stopped when either its received bytes or decoded UTF-8 text reaches this
limit. This applies even when the remote server uses chunked transfer encoding
or omits `Content-Length`.

- **Type:** Integer (bytes, at least 1)
- **Default:** `67108864` (64 MiB)
Expand Down
16 changes: 16 additions & 0 deletions fuzz/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ pub struct WflConfig {
/// server sheds new requests with a 503 instead of growing memory without
/// bound. Default 256; must be at least 1.
pub web_server_request_queue_bound: usize,
/// Maximum HTTP response body size in bytes. A handler that tries to send a
/// larger body is refused with a 500 rather than streaming an unbounded
/// payload. Feeds `ExecutionBudget`. Default 64 MiB.
/// Maximum HTTP response body size in bytes, for both handler responses and
/// bodies read by outbound `open url` statements. A larger body is refused
/// rather than buffered/streamed without bound. Feeds `ExecutionBudget`.
/// Default 64 MiB.
pub web_server_max_response_size: usize,
/// Maximum seconds the transport waits for a handler to answer an accepted
/// HTTP request before shedding it with 504 and releasing its in-flight
Expand Down
3 changes: 2 additions & 1 deletion src/exec/budget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ pub struct BudgetLimits {
/// Maximum accepted HTTP request body size in bytes. Mapped from `.wflcfg`
/// `web_server_max_body_size`.
pub max_request_body_bytes: usize,
/// Maximum HTTP response body size in bytes. Mapped from `.wflcfg`
/// Maximum HTTP response body size in bytes, for both handler responses and
/// bodies read by outbound `open url` statements. Mapped from `.wflcfg`
/// `web_server_max_response_size`.
pub max_response_bytes: usize,
/// Maximum accepted-but-unhandled HTTP requests held in the transport
Expand Down
Loading
Loading