Protocols

RelayCore is an L7 proxy: it parses, inspects, and rewrites application-layer protocols. Lower-layer transport is TCP (for HTTP/HTTPS/WebSocket); UDP is supported only in the transparent proxy path.

HTTP/1.1

Fully supported. Request/response headers, chunked encoding, Transfer-Encoding, bidirectional keep-alive, and streaming bodies are all parsed. Rules and scripts apply uniformly to headers and bodies.

HTTP/2

The client leg can be HTTP/1.1 or HTTP/2, depending on the client and ALPN. Only an HTTP/1.x version is copied upstream. An HTTP/2 client does not force the upstream connection to HTTP/2; that leg follows the target URL and ALPN. HPACK is fully decompressed internally — rules operate on decoded pseudo-headers (:method, :path, :scheme, :authority).

h2c (plaintext HTTP/2) is supported too. gRPC clients that speak HTTP/2 without TLS are detected by their connection preface, both when they connect directly and when they tunnel through CONNECT — which is what an HTTP_PROXY client does. Upstreams that only speak h2c are reachable as well: the proxy offers HTTP/2 by prior knowledge and falls back to HTTP/1.1 if the upstream rejects it. Because h2c has no negotiation, that fallback waits briefly before giving up, so a new connection to an h2c-only upstream costs a little latency.

HTTPS / TLS

MITM interception is performed for all HTTP-over-TLS traffic using dynamically-issued certificates (see Certificates). Upstream certificates are validated by default; in the replay endpoint you can pass ?accept_invalid_certs=true to skip verification (debug only).

WebSocket

Handshakes and uncompressed frames can be inspected and rewritten (RFC 6455). RelayCore parses the handshake, registers a Layer::WebSocket flow, and exposes each message. permessage-deflate is neither stripped nor inflated, so compressed frames pass through opaque.

  • Each message records direction (incoming / outgoing), opcode (text / binary / ping / pong / close / continuation), and content (encoding, size, base64 body).
  • Rules can use MockWebSocketMessage and DropWebSocketMessage.
  • The script hook onWebSocketMessage can read and rewrite every message.
  • The TUI's Messages detail tab displays the message log in real time.
  • A body over budget emits SSE body-budget-exceeded with a direction. There is no separate WebSocket budget event name.

QUIC / HTTP/3

QUIC runs over UDP; modern browsers prefer it when available. RelayCore controls this via ProxyPolicy.quic_mode:

ModeDescription
Downgrade (default)Strips Alt-Svc from the HTTP response. Clear-Site-Data: "cache" is added only when quic_downgrade_clear_cache=true (default false).
PassthroughLeaves Alt-Svc in place. The client may then speak QUIC directly to the origin; that traffic is not proxied and produces no flow.
ExperimentalMitmExperimental HTTP/3 MITM. Requires the quic_mitm_experimental feature flag; not yet production-ready.

QUIC itself produces no flow. Downgrade only rewrites an HTTP response that was already captured: stripping Alt-Svc tags that flow quic-downgraded.

TCP / UDP (transparent proxy)

  • TCP: redirected traffic is parsed as HTTP/1.1. Raw TCP (non-HTTP) connections are rejected and recorded as errors.
  • UDP: forwarded via --udp-tproxy-port (Linux only, requires IP_TRANSPARENT). No parsing, just forwarding.

Unsupported protocols

  • Custom protocols over raw TCP (non-HTTP): RelayCore does not parse them. In transparent mode such connections will fail.
  • gRPC message payloads: the framing is parsed — a gRPC body is read as its length-prefixed messages (count, size, compression flag, and a text preview when the payload is text), and HTTP trailers are recorded, so grpc-status and grpc-message are visible on the flow. Decoding the protobuf fields themselves still needs the service's schema, which the engine does not have, so payload contents remain opaque.
  • SSE (Server-Sent Events): long-lived HTTP/1.1. RelayCore uses SSE on its own GET /api/v1/events endpoint; external SSE traffic is recorded as plain HTTP without per-event boundaries.