HTTP API proxy

Forward requests to a real API, mock selected routes and test upstream failures.

On this page

The HTTP proxy sits between your application and a real HTTP API. Your application sends its requests to FlowReactor instead; FlowReactor forwards each one to the real API, called the upstream, and shows both sides in the Activity timeline.

From there you can answer some routes yourself, block others, and add faults, while everything else keeps reaching the real API.

Add the proxy

  1. + Add › HTTP proxy.
  2. Enter the real API's URL in Forwards to, such as https://api.example.com or http://localhost:5001/api.
  3. Check Listens on 127.0.0.1, port (default 18081).
  4. Choose Add HTTP proxy, then Start.

A new proxy has no routes, so every request is forwarded. The service header shows both addresses, such as http://127.0.0.1:18081 → https://api.example.com.

The HTTP proxy Configuration tab with its base URL, listen address and upstream URLThe HTTP proxy Configuration tab with its base URL, listen address and upstream URL
Point your application at the base URL; FlowReactor forwards to the upstream.

Point your application at it

Replace the real API's base URL with http://127.0.0.1:18081. Configuration › Base URL has it with a copy button. Then try it:

bash
curl -i http://127.0.0.1:18081/users/42

The request reaches https://api.example.com/users/42, and the timeline shows it tagged Proxied.

.NETcs
builder.Services.AddHttpClient("payments", client =>
    client.BaseAddress = new Uri("http://127.0.0.1:18081/"));
Node.jsjs
const baseUrl = process.env.PAYMENTS_URL ?? 'http://127.0.0.1:18081'
const response = await fetch(`${baseUrl}/users/42`)
Pythonpython
import requests

BASE_URL = "http://127.0.0.1:18081"
response = requests.get(f"{BASE_URL}/users/42")

How requests are forwarded

FlowReactor adds the request's path and query string to the upstream URL, exactly as your application sent them. A path in the upstream URL isn't removed from requests that repeat it:

text
Upstream URL                Request        Sent to
https://api.example.com     /users?id=1    https://api.example.com/users?id=1
http://localhost:5001/api   /users         http://localhost:5001/api/users
http://localhost:5001/api   /api/users     http://localhost:5001/api/api/users
  • The upstream URL can include a path, such as /v1, but not a query string, a fragment, or a user name and password.
  • The Host header is set to the upstream's host. Other headers pass through unchanged, except connection headers such as Connection, Keep-Alive, Transfer-Encoding and Upgrade, which apply to one connection only.
  • Redirects aren't followed: your application gets the 302 and its Location. Cookies and Location aren't rewritten, so a redirect to the real host leaves FlowReactor.
  • Only the upstream URL decides where a request goes. A Host or X-Forwarded-Host header from your application never sends it elsewhere.
  • Request and response bodies of any size are passed on in full while they stream.

Mock, forward or block a route

Routes work as in the HTTP API simulator: a request gets the first route that is on and matches. Each route has an Action:

Action What the route does
Simulate Answers with its own responses. The upstream isn't contacted.
Proxy Forwards to the upstream. Use it to give one path its own faults or breakpoint.
Block Answers without contacting anything: Answer with a status (400–599, default 403) or Drop the connection.

Requests that match no route are forwarded. The last row of the routes list says so: Everything else → forwarded to https://api.example.com. Without an upstream, it reads Everything else → 404 Not Found.

A route keeps its responses when you switch it to Proxy or Block, so you can switch back to Simulate later. Choose Save routes: changes apply to the running proxy immediately.

For example, to test your application while the real /payments endpoint is down, add a POST /payments/* route with the action Block, Answer with a status, 503. Every other request still reaches the real API.

Create a mock from a real response

Turn a real response into a mock route, so you can work on that endpoint without the real API.

  1. In the timeline, select a forwarded request.
  2. Choose Create mock route.
  3. Pick the Path: exactly the captured path, or Any id, which replaces ids with a path parameter, such as /patients/{patientId}. With a query string, Match this query limits the mock to requests with those parameters.
  4. Choose Create draft. FlowReactor adds a Simulate route at the top of the service's Routes, answering with the status, headers and body the upstream sent.
  5. Check it, then choose Save routes to turn it on. Saving the project alone doesn't.
Create mock route with Path set to Any id: /patients/{patientId} and Match this query offCreate mock route with Path set to Any id: /patients/{patientId} and Match this query off
Any id turns the captured path into a route with a path parameter.

A generalized route still sends the captured response: ids in the body aren't replaced. Use response templates, such as {{path.patientId}}, if the body should follow the request.

If the body looks like it contains secrets, such as tokens or passwords, the draft lists the fields. Redact these replaces their values and leaves the rest of the body as it was.

Create mock route isn't available for requests that didn't complete, binary or undecoded bodies, or partial (206) responses.

Inspect forwarded requests

Rows in the timeline are tagged with what answered them:

Tag Meaning
Proxied Forwarded to the upstream, which answered.
Mocked Answered by a Simulate route; the upstream wasn't contacted.
Blocked Answered by a Block route.

The Proxied and Mocked filters, above the timeline, show only those.

Select a row to see it in full:

  • Request shows Sent to, the upstream URL the request went to, and FlowReactor changed, the headers FlowReactor removed or set, such as host.
  • Response shows the upstream's answer. When your application got something else, such as an injected 503 or a block, it shows What your app got and What the upstream sent side by side.
  • Timing splits the time into any pause, injected latency, the Upstream's own time, and FlowReactor's overhead. The response notes whether the upstream used HTTP/1.1 or HTTP/2.
The Response tab of a proxied GET /patients/1042, with the upstream JSON body and response headersThe Response tab of a proxied GET /patients/1042, with the upstream JSON body and response headers
A forwarded request shows what the upstream answered.

Compressed bodies (gzip, deflate and br) are shown decoded, and your application still gets the original bytes. Other encodings, such as zstd, are shown as they arrived, marked not decoded.

The timeline keeps the first 256 KB of each request and response body. Change it in Configuration › Upstream › Capture limit (each way), from 16 KB to 1 MB. Your application always gets the whole body.

Authorization, Proxy-Authorization, Cookie, Set-Cookie and X-Api-Key headers show as [redacted] in the timeline. They're still sent.

Configure latency and failures

Configuration › Faults applies to every request; a route's Faults can use The service's faults, None, or Its own faults instead. Each fault has a When:

  • before the upstream sees it: the upstream never gets the request.
  • after the upstream answers: the upstream handled the request, but your application doesn't hear back. The timeline still shows the upstream's real answer, so you can see that a payment went through while your application got a 503.
Service faults with 300 ms latency and Fail answering 503 for 10% of requests, before the upstream sees themService faults with 300 ms latency and Fail answering 503 for 10% of requests, before the upstream sees them
Faults apply to every request unless a route sets its own.

See Faults for each fault.

Breakpoints pause proxied requests too:

  • Before handling: the upstream hasn't seen the request. Continue sends it on; Return status… answers without contacting the upstream.
  • Before response: the upstream's status and headers have arrived, and your application hasn't got them yet. Return status… replaces the real answer.

Route changes made while a request is paused apply to it when it continues.

HTTPS upstreams and certificates

FlowReactor checks an https:// upstream's certificate against your computer's trusted certificates. A development certificate you trusted, such as one from dotnet dev-certs https --trust, works as it is.

For a local server with a self-signed certificate, stop the service and turn on Configuration › Upstream › Allow untrusted certificate. It applies to that service only. Leave it off for anything that isn't on your own computer.

FlowReactor uses HTTP/2 with an https:// upstream that supports it, and HTTP/1.1 otherwise.

Settings

The Upstream section of Configuration has the proxy's settings. Stop the service to change them, then choose Save settings.

Setting Default What it does
Upstream URL none Where Proxy routes and unmatched requests go. Empty: unmatched requests get 404.
Allow untrusted certificate Off Accepts any certificate from this upstream.
Capture limit (each way) 256 KB How much of each request and response body the timeline keeps: 16 KB, 64 KB, 256 KB, 512 KB or 1 MB.

You can't remove the upstream while a route's action is Proxy: change those routes first.

When it can't reach the upstream

Your application gets an answer from FlowReactor, with a plain-text body that starts FlowReactor couldn't reach the upstream: and gives the reason.

What happened Your application gets
Couldn't connect within 10 seconds, or the certificate refused 502 Bad Gateway
No response headers within 60 seconds 504 Gateway Timeout
The response body stopped for 60 seconds The response is cut off

There's no limit on a request's total time, so long polling and streaming responses work. The timeline shows the reason under What the upstream sent, such as No response (tls). These timeouts can't be changed yet.

If the certificate is refused, see HTTPS upstreams and certificates.

Limits

  • Your application talks to FlowReactor over plain http:// with HTTP/1.1. FlowReactor can't intercept HTTPS your application sends elsewhere.
  • WebSockets aren't forwarded yet: an upgrade request gets 501.
  • No X-Forwarded-For, X-Forwarded-Host or X-Forwarded-Proto headers are added.
  • Requests answered by Simulate or Block routes, or with a 404, can have bodies up to 10 MiB; larger ones get 413. Forwarded bodies have no limit.