Skip to content

Oz/add http request action docs #1286

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
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
14 changes: 14 additions & 0 deletions docs/traffic-policy/actions/http-request.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import ActionVariables from "/traffic-policy/actions/http-request/variables.mdx";
import ActionOverview from "/traffic-policy/actions/http-request/index.mdx";
import ActionVariablesDescription from "/traffic-policy/common/action-variables-description.mdx";
import ActionConfig from "/traffic-policy/actions/http-request/config.mdx";

# HTTP Request

<ActionOverview />
<ActionConfig />

## Action Result Variables

<ActionVariablesDescription />
<ActionVariables />
11 changes: 11 additions & 0 deletions docs/traffic-policy/actions/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,17 @@ actions:
- traffic-control
- connection-modification

- type: http-request
name: Http Request
description: Sends a http request to a thrid-party API adn returns the response.
phases:
- on_http_request
- on_http_response
- on_tcp_connect
categories:
- traffic-control
- connection-modification

- type: jwt-validation
name: JWT Validation
description: Validate JSON Web Tokens (JWTs) on your incoming requests.
Expand Down
2 changes: 1 addition & 1 deletion src/components/ConfigTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type ConfigEnumProps = PropsWithChildren & {

export const Config = ({ children }: PropsWithChildren) => {
return (
<ul className="m-0 flex flex-shrink-0 list-none flex-col divide-y divide-gray-200 self-start border-b border-t border-gray-200 p-0 dark:divide-gray-800 dark:border-gray-800 [&_li+li]:mt-0 [&_p]:w-2/3">
<ul className="m-0 flex flex-shrink-0 list-none flex-col divide-y divide-gray-200 self-start border-b border-t border-gray-200 p-0 dark:divide-gray-800 dark:border-gray-800 [&_li+li]:mt-0">
{children}
</ul>
);
Expand Down
91 changes: 91 additions & 0 deletions traffic-policy/actions/http-request/behavior.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
## Behavior

The `http-request` action issues an outbound HTTP request during either the `on_http_request` or `on_http_response` phase of policy execution. It can include dynamic headers, query parameters, and a request body.

Only specific HTTP methods are supported: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, and `OPTIONS`. If no method is specified, `GET` is used by default.

### Internal Endpoint Behavior

By default, the `http-request` action only supports requests to internal ngrok endpoints. Requests to internal endpoints (those with `.internal` domains) are treated differently from public (or external) requests, these requests must resolve to an internal ngrok endpoint running on your account.

These requests also use a direct connection over ngrok's control plane and do not rely on the public internet. If the request targets an internal agent endpoint (e.g., your self-hosted ngrok agent), it will exit the control plane and traverse the public internet to reach your agent running locally.

### Retry Logic

You can use a condition to automatically retry failed requests. This is useful for handling transient errors, like a `500` response. The condition is written using an expression language and has access to:

- `attempts`: number of attempts so far
- `last_attempt.req`: the most recent request
- `last_attempt.res`: the most recent response
- `last_attempt.error`: any error that occurred

```js
last_attempt.res.statusCode == 500
```

The request will retry up to **3 times**.

### Timeout Behavior

The `timeout` setting defines the **maximum total time** allowed for the entire `http-request` action, including **all** retry attempts. This prevents long-running or stalled requests from delaying policy evaluation.

The default timeout is `3s`. You can configure any duration between **1 second** and **30 seconds** using standard duration formats like `5s` or `10s`.

#### Timeout Error Handling

Whether a timeout causes the policy to fail or continue depends on the `on_error` setting:

- `halt` will treat the timeout as a hard failure.
- `continue` will move forward even if the request timed out.

### Loop Protection

To prevent endless loops between services, ngrok tracks internal hops. If the same request loops more than 3 times internally, it will be stopped automatically.

### Redirect Behavior

By default, the `http-request` action does not follow HTTP redirects. You can enable redirect handling by setting the `max_redirects` field. The allowed range is `0` to `5`. By default it is set to `0`.

If the number of redirects exceeds `max_redirects`, the action fails. Redirect handling only applies to `3xx` responses from the target server.

### Response Body Size Limits

The response body returned by the `http-request` action is limited to **256 KB**. If the body size exceeds this limit, the action fails and returns a `response size exceeded` error.

This limit applies to the decoded body after any decompression and before retries are evaluated.

Each retry is also subject to the **256 KB** response size limit. If the response body exceeds this limit, the attempt fails and the error is included in the `retry_condition` evaluation.

### Error Handling

Set `on_error` to control what happens if the request fails:

- `continue`: Policy continues even if the request fails.
- `halt`: Policy stops immediately on failure.

This gives you control over how critical the request is to your policy logic.

### Automatically Injected Headers

ngrok automatically injects some headers into your request to help with debugging, tracing and abuse:

| Header | Purpose |
| -------------------- | -------------------------------------------- |
| `Ngrok-Report-Abuse` | Static URL for reporting abuse |
| `Ngrok-Req-Type` | Always set to `http-request` |
| `Ngrok-Req-Id` | Unique request identifier |
| `X-Forwarded-For` | Original client IP address |
| `User-Agent` | Identifies the request as from `ngrok/cloud` |

### CEL Interpolation

Certain fields in the `http-request` action support CEL (Common Expression Language) interpolation, allowing dynamic values based on the request context.

The following fields support CEL expressions:

- `url`
- `headers` (values only)
- `query_params` (values only)
- `body`

Expressions must be wrapped in `${...}` and are evaluated at runtime using the current request data.
110 changes: 110 additions & 0 deletions traffic-policy/actions/http-request/config.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import {
Config,
ConfigEnum,
ConfigEnumOption,
ConfigItem,
ConfigChildren
} from "/src/components/ConfigTable.tsx";

## Configuration Reference

The [Traffic Policy](/traffic-policy/) configuration
reference for this action.

### Supported Phases

`on_http_request`, `on_http_response`

### Type

`http-request`

### Configuration Fields

<Config>
<ConfigItem title="url" type="string" required={true} cel={true}>
<p>
The destination URL for the HTTP request.
</p>
</ConfigItem>
<ConfigItem title="method" type="enum">
<p>
The HTTP method to use for the request.
</p>
<ConfigEnum>
<ConfigEnumOption>`GET` (default)</ConfigEnumOption>
<ConfigEnumOption>`PUT`</ConfigEnumOption>
<ConfigEnumOption>`POST`</ConfigEnumOption>
<ConfigEnumOption>`PATCH`</ConfigEnumOption>
<ConfigEnumOption>`DELETE`</ConfigEnumOption>
<ConfigEnumOption>`OPTIONS`</ConfigEnumOption>
</ConfigEnum>
</ConfigItem>
<ConfigItem title="query_params" type="list of objects" cel={true}>
<p>
A list of query parameters to append to the URL. Each item is an object with the following structure:
</p>
```yaml
- key: "parameter_name"
value: "parameter_value"
```
<p>
This format supports CEL interpolation in both keys and values.
</p>
<p>
Maximum: `32` entries. Key max length: `128` chars. Value max length: `8192` chars.
</p>
</ConfigItem>
<ConfigItem title="headers" type="object" cel={true}>
<p>
A map of HTTP headers to include in the request. Keys are header names and values are header values.
</p>
<p>
Maximum: `10` entries.
</p>
</ConfigItem>
<ConfigItem title="body" type="string" cel={true}>
<p>
The body of the HTTP request. Supported on methods like `POST`, `PUT`, or `PATCH`.
</p>
</ConfigItem>
<ConfigItem title="max_redirects" type="int">
<p>
The maximum number of HTTP redirects to follow.
</p>
<p>
Default is `10`. The minimum allowed is `0`. The maximum allowed is `100`.
</p>
</ConfigItem>
<ConfigItem title="timeout" type="duration">
<p>
The maximum duration as a duration string to wait for the entire request (including retries and redirects).
</p>
<p>
Default is `3s`. The minimum allowed is `1s`. The maximum allowed is `30s`.
</p>
</ConfigItem>
<ConfigItem title="retry_condition" type="string" cel={true}>
<p>
A CEL expression evaluated after each failed attempt. If `true`, the request is retried (up to `3` times).
</p>
<ConfigEnum label="Supported retry variables">
<ConfigEnumOption>`attempts` (`int`): Total number of attempts so far</ConfigEnumOption>
<ConfigEnumOption>`last_attempt.req`: The last request object</ConfigEnumOption>
<ConfigEnumOption>`last_attempt.res`: The last response object (if any)</ConfigEnumOption>
<ConfigEnumOption>`last_attempt.error`: The error string (if any)</ConfigEnumOption>
</ConfigEnum>
<p>
Default: `false`
</p>
</ConfigItem>
<ConfigItem title="on_error" type="enum">
<p>
Determines how to proceed if the HTTP request fails.
</p>
<ConfigEnum>
<ConfigEnumOption>`continue` (default) – Proceed with remaining actions</ConfigEnumOption>
<ConfigEnumOption>`halt` – Stop processing the policy</ConfigEnumOption>
</ConfigEnum>
</ConfigItem>
</Config>
25 changes: 25 additions & 0 deletions traffic-policy/actions/http-request/examples/1-basic-request.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import ConfigExample from "/src/components/ConfigExample.tsx";

### Basic Example

Performs a simple GET request to an internal endpoint.

<ConfigExample
snippetText={null}
showLineNumbers={true}
config={{
"on_http_request": [
{
"name": "BasicInternalRequest",
"actions": [
{
"type": "http-request",
"config": {
"url": "https://auth.internal/ping"
}
}
]
}
]
}}
/>
5 changes: 5 additions & 0 deletions traffic-policy/actions/http-request/examples/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import BasicInternalRequest from "./1-basic-request.mdx";

## Examples

<BasicInternalRequest />
5 changes: 5 additions & 0 deletions traffic-policy/actions/http-request/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Overview

The HTTP Request Traffic Policy action performs an outbound HTTP request to a target URL. You can use it to integrate with external services through internal endpoints or—if enabled—with external APIs.

> **Note:** External requests are supported but disabled by default. [Contact ngrok support](https://ngrok.com/support) to enable external access.
97 changes: 97 additions & 0 deletions traffic-policy/actions/http-request/variables.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { Config, ConfigItem, ConfigChildren } from "/src/components/ConfigTable.tsx";

<Config>
<ConfigItem title="actions.ngrok.http_request.error.code" type="string">
</ConfigItem>
<ConfigItem title="actions.ngrok.http_request.error.message" type="string">
<p>
Message for an error that occurred during the invocation of an action.
</p>
</ConfigItem>
<ConfigItem title="actions.ngrok.http_request.attempts" type="array of objects">
<p>
A list of HTTP responses for each request attempt.
</p>
<ConfigChildren>
<ConfigItem title="attempts[i].resolved_ip" type="string">
<p>
The IP address of the host returning the response.
</p>
</ConfigItem>
<ConfigItem title="attempts[i].response_header" type="object">
<p>
A map of HTTP response headers.
</p>
</ConfigItem>
<ConfigItem title="attempts[i].response_status_code" type="int">
<p>
The HTTP Status code for the response attempt.
</p>
</ConfigItem>
<ConfigItem title="attempts[i].response_time_ms" type="string">
<p>
Time it took to recieve the response.
</p>
</ConfigItem>
</ConfigChildren>
</ConfigItem>
<ConfigItem title="actions.ngrok.http_request.req" type="object">
<p>
The HTTP request.
</p>
<ConfigChildren>
<ConfigItem title="req.method" type="string">
<p>
The HTTP method of the request.
</p>
</ConfigItem>
<ConfigItem title="req.header" type="object">
<p>
A map of HTTP request headers.
</p>
</ConfigItem>
<ConfigItem title="req.url" type="string">
<p>
The request url.
</p>
</ConfigItem>
<ConfigItem title="req.body" type="string">
<p>
The request body.
</p>
</ConfigItem>
</ConfigChildren>
</ConfigItem>
<ConfigItem title="actions.ngrok.http_request.res" type="object">
<p>
The last attempted HTTP response. Unlike `actions.ngrok.attempts[i]` this variable also contains the response body.
</p>
<ConfigChildren>
<ConfigItem title="res.resolved_ip" type="string">
<p>
The IP address of the host returning the response.
</p>
</ConfigItem>
<ConfigItem title="res.header" type="object">
<p>
A map of HTTP response headers.
</p>
</ConfigItem>
<ConfigItem title="res.status_code" type="int">
<p>
The HTTP Status code for the response.
</p>
</ConfigItem>
<ConfigItem title="res.time_ms" type="string">
<p>
Time it took to recieve the response.
</p>
</ConfigItem>
<ConfigItem title="res.body" type="string">
<p>
The response body
</p>
</ConfigItem>
</ConfigChildren>
</ConfigItem>
</Config>