Skip to main content

HTTP Connector

The HTTP connector lets EventFlux ingest events over HTTP — by polling REST endpoints or by listening for webhooks — and deliver processed events as outbound webhook requests with retry. It supports JSON, CSV, and bytes formats.

The stack uses the best tool per side: a blocking ureq client (rustls — hermetic, no system TLS) for the sink and polling, and an axum server (on tokio) for the webhook listener, giving concurrent request handling out of the box.

Prerequisites​

The HTTP connector is feature-gated. Build EventFlux with the http feature (Docker images already include all connectors). Both underlying crates are pure Rust — no extra toolchain needed:

cargo build --release --features http
# or: cargo build --release --features connectors-all

HTTP Source​

One extension, two modes selected by http.mode.

Poll Mode​

Periodically requests a URL; each 2xx response body becomes one payload through the mapper.

CREATE STREAM Prices (symbol STRING, price DOUBLE) WITH (
type = 'source',
extension = 'http',
format = 'json',
"http.mode" = 'poll',
"http.url" = 'https://api.example.com/v1/price?symbol=AAPL',
"http.poll.interval.ms" = '2000',
"http.headers.X-Api-Key" = '${API_KEY}'
);
OptionRequiredDefaultDescription
http.modeYes-poll
http.urlYes-URL to poll (http/https)
http.poll.interval.msNo5000Time between polls (interruptible — long intervals never delay shutdown)
http.methodNoGETGET or POST
http.headers.<Name>No-Request headers; put secrets in env vars: 'Bearer ${TOKEN}'
http.timeout.msNo30000Per-request timeout
error.*No-Error strategies (drop/retry/dlq/fail) — same options as Kafka

Poll behavior: 2xx non-empty body → delivered · empty body / 204 / 304 → skipped · other statuses (including un-followed 3xx redirects) and transport errors → routed through the error strategy (use retry for polling backoff). Response bodies are capped at 10 MB (the HTTP client's read limit); larger responses surface as read errors. Each poll is independent — deduplication is the endpoint's or the query's concern.

Webhook Mode​

Runs an HTTP listener; each accepted POST body becomes one payload. Requests are handled concurrently (bounded by the configured cap).

CREATE STREAM Alerts (service STRING, level STRING, message STRING) WITH (
type = 'source',
extension = 'http',
format = 'json',
"http.mode" = 'webhook',
"http.port" = '8081',
"http.path" = '/alerts',
"http.auth.header" = 'X-Webhook-Token',
"http.auth.token" = '${WEBHOOK_TOKEN}'
);
OptionRequiredDefaultDescription
http.modeYes-webhook
http.portYes-Port to listen on
http.hostNo0.0.0.0Bind address
http.pathNo/Request path to accept
http.auth.header / http.auth.tokenNo-Inbound auth: required header name + exact value (set together)
http.max.concurrent.requestsNo256Concurrency cap; excess requests queue
http.max.body.bytesNo1048576Payload size cap
error.*No-Error strategies, as above

Response codes:

SituationResponse
Payload accepted (delivered, or disposed by drop/DLQ strategy)200
Empty body200 (skipped)
Auth missing/mismatched401
Wrong path / wrong method404 / 405
Body too large413
Unrecoverable delivery failure (fail strategy)500, listener shuts down gracefully
Listener shutting down (stop or after a failure)503

Deployment: run the listener behind a reverse proxy (nginx/caddy) — it terminates TLS and enforces client read timeouts (the listener itself does not time out slow clients).

HTTP Sink​

Sends each event as its own request (one event = one message). Retries with exponential backoff apply to transport errors and 408/429/5xx; other 4xx are returned to the pipeline immediately — retrying a wrong request can't fix it. Redirects are never followed (following a 302 would silently convert the POST to a body-less GET); a 3xx is a permanent error — point http.url at the final location.

CREATE STREAM Escalations (service STRING, message STRING) WITH (
type = 'sink',
extension = 'http',
format = 'json',
"http.url" = 'https://hooks.example.com/escalate',
"http.headers.Authorization" = 'Bearer ${HOOK_TOKEN}',
"http.retry.max-attempts" = '5'
);
OptionRequiredDefaultDescription
http.urlYes-Endpoint to send to
http.methodNoPOSTPOST, PUT, or PATCH
http.headers.<Name>No-Request headers (auth goes here)
http.content.typeNoderivedFrom the stream format: json → application/json, csv → text/csv, bytes → application/octet-stream. A verbatim http.headers.Content-Type wins instead; setting both is rejected
http.timeout.msNo30000Per-request timeout
http.retry.max-attemptsNo3Retries after the first attempt (0 disables)
http.retry.initial-delay-msNo100First retry delay
http.retry.max-delay-msNo10000Retry delay ceiling
http.retry.backoff-multiplierNo2.0Exponential factor
http.validation.methodNoHEADStartup reachability probe: HEAD, GET, OPTIONS, or none to skip

Retries block inside the publish call — backpressure by design.

Connection Validation​

At startup (fail-fast):

  • Sink / poll source: sends the probe (HEAD for polling; configurable for the sink). Any HTTP response — including 404 or 405 — proves reachability; only transport failures (connect/timeout/TLS) block startup. Endpoints that can't tolerate probes: "http.validation.method" = 'none'.
  • Webhook source: binds the configured port and releases it — port conflicts and permission errors fail immediately.

Complete End-to-End Example​

See examples/http.eventflux — a webhook-in → filter → webhook-out pipeline testable with curl alone, no external infrastructure.

Not Yet Supported​

  • Request/response mode (query result as the HTTP response) — designed, tracked in #134
  • Batched sink requests (N events per POST)
  • Connector-managed conditional GET (pass If-None-Match/If-Modified-Since through http.headers.* yourself; 304 responses are skipped)
  • OAuth token flows (use static bearer headers with env vars)
  • In-process TLS for the listener (reverse proxy)