Add an HTTP request task type that calls an external endpoint and routes on its response

Closes #3633764.

A new orchestra_http submodule adds an HTTP request task type: a step calls an external endpoint, writes the status code and the decoded answer to process variables, and its flows route on them, with no code of the author's own.

One commit per step:

  1. The task type.
    • Request: method, URL, headers and a body. Tokens read this instance and branch. In the URL each value is URL-encoded. The body is one of: a JSON template written as it goes out, whose [orchestra:var:NAME] tokens become the variable as JSON (a number stays a number, a list a list, an object an object, a missing variable null) and whose other tokens become JSON strings, refused on save when a token sits inside quotes or the document does not parse; form fields (name|value lines) sent URL-encoded or as multipart, text fields only; or text with its content type.
    • Credentials: none, basic or bearer, the secret held by the Key module as orchestra_client holds its own.
    • Answers: the status code, as a number, to the payload variable, so flows route == 404 or >= 200 / < 300. The body goes to a response variable, decoded when it is JSON, so response.items.0.id reads through VariablePath. The body is capped at 256 KB: a larger one fails the step rather than being cut. A 3xx is an answer; redirects are not followed.
    • Failures: a timeout, a refused connection, a 429 or a 5xx is retryable, so the node's retry policy applies, then the step's When this step fails for good (#3633763). A step that cannot be sent at all fails at once.
    • What a step may call: the servers its author named. A token's value is URL-encoded, so a variable fills a path segment or a query value but cannot add one or change the host; a host a token supplies must be one of the step's allowed hosts, and the form requires the list then. Redirects are not followed. A header value with a line break fails the step. Messages never show the URL's query. http and https are both allowed, an intranet service often being on http; credentials (basic or bearer) over plain http give a warning when the workflow is saved.
    • Timeout: 10 seconds by default, at most 120, settable per node.
    • Tests against a mocked Guzzle client and a stubbed resolver; each guard was confirmed to fail its test when broken (15 mutations).
  2. Docs and example. docs/http.md (in the nav, the architecture table, the roadmap and the examples list), with the synchronous-mode caveat. example_http_lookup ships in orchestra_examples/config/optional, so it installs only beside orchestra_http; the examples test now covers optional workflows and runs this one. French for both.
  3. Lint fixes.
  4. Audit fixes:
    • The size cap is enforced as the body arrives: a sink refuses bytes past 256 KB, which stops the download. It counts only once the status is an answer and only when the step keeps the body: a 502 with a large error page is retried, and a step with no response variable does not mind the size.
    • The form refuses one variable for both the status code and the response.
    • A content type written as a header, in any case, wins over the setting.
    • The plugin's services are writable, as plugin serialization needs on PHP 8.3.
    • The response a stopped download leaves on the client's error is read on Guzzle 7 and 8 alike (the next-major lanes run Guzzle 8, which moved it to ResponseException).
  5. A first version also refused private addresses and pinned DNS, which made the task unusable on an intranet while adding nothing the allowed-hosts rule does not already give: a variable cannot reach a host the author did not name. It is removed. The warning about credentials over http goes through a new TaskTypeWarningsInterface (engine), whose TaskTypeManager::getWarnings() both modelers show beside the node feature warnings, after a CM save and on the page opening a workflow.

On the response, the Webform handler's SubmissionValueSplitter was considered and not shared: the decoded body goes to one variable, and VariablePath already reads response.items.0.id into it in conditions, tokens, Views and deadlines.

Both modelers list the task type from the plugin manager and build its panel from the settings form, so no modeler code was needed.

AI-Generated: Yes (Claude Code was used to help write the change, its tests and this description. I reviewed them, and each new test was confirmed to fail when the code it guards is broken.)

Edited by Frank Mably

Merge request reports

Loading
Loading