Files
hop-gate/ARCHITECTURE.md
dalbodeule ebd8463c19 docs: align architecture and API docs with Go 1.27 ingress support
- document HTTP/1.1, HTTP/2, and HTTP/3 ingress
- describe SSE streaming and timeout behavior
- document HTTP/2 and HTTP/3 Extended CONNECT
- update yamux stream and deployment architecture
- document Go 1.27 and UDP HTTP/3 requirements
2026-09-04 16:58:14 +09:00

3.4 KiB

HopGate Architecture

HopGate exposes public HTTP traffic and forwards it to a private HTTP service through one outbound TLS connection per client.

                    TCP :80/:443  HTTP/1.1, HTTP/2
public clients  ------------------------------->  HopGate server
                    UDP :443     HTTP/3              |
                                                     | TLS/TCP
                                                     v
                                      yamux logical streams
                                                     |
                                                     v
                                           HopGate client
                                                     |
                                                     v
                                             localhost HTTP

Connection Model

The client opens one TLS connection to the server and the connection is multiplexed by yamux. The first logical stream is a bounded JSON control stream containing the registered domain, local target, and client API key. The server authenticates this stream before registering the session for the domain.

Each public request creates one bidirectional yamux stream. Every stream starts with a bounded JSON StreamMeta record and then carries HTTP/1.1 wire data. The stream kinds currently used are:

  • control: client registration and authentication metadata.
  • http: ordinary HTTP requests and responses.
  • websocket: HTTP/1.1 Upgrade and HTTP/2/HTTP/3 Extended CONNECT traffic.

Request and response bodies are copied between stream endpoints instead of being accumulated in memory. Long-lived SSE connections therefore occupy one yamux stream for their lifetime.

Ingress Protocols

The public server uses one common http.Handler for all ingress protocols:

  • HTTP/1.1: ordinary reverse proxy and raw WebSocket Upgrade.
  • HTTP/2: ordinary reverse proxy, SSE, and RFC 8441 Extended CONNECT.
  • HTTP/3: ordinary reverse proxy, SSE, and RFC 9220 Extended CONNECT.

HTTP/3 runs on a separate UDP listener using quic-go/http3, while the TCP HTTP/HTTPS listeners continue to serve HTTP/1.1 and HTTP/2. HTTP/1.1 and HTTP/2 responses advertise HTTP/3 with Alt-Svc.

For HTTP/2 Extended CONNECT, Go's compatibility setting must be enabled when starting the process:

GODEBUG=http2xconnect=1 ./bin/hop-gate-server

Streaming Policies

Requests accepting text/event-stream are treated as SSE. They bypass the normal request-level proxy timeout, and response writes are flushed to the public client as they arrive. The client or upstream service is responsible for closing the SSE request context.

WebSocket Extended CONNECT is translated to a local HTTP/1.1 WebSocket handshake. After the handshake, the payload is relayed as a bidirectional raw stream. HTTP/3 Extended CONNECT follows the same application path as HTTP/2.

Packages

  • internal/tunnel: TLS dialing, yamux sessions, metadata, and stream lifecycle.
  • cmd/server: public HTTP/HTTPS/HTTP/3 ingress and yamux tunnel listener.
  • cmd/client: outbound yamux client and local HTTP/WebSocket forwarding.
  • internal/admin: domain registration and API-key validation.
  • internal/acme: certificate acquisition, renewal, and TLS configuration.

The tunnel is intentionally stream-oriented. It does not implement application ACKs or retransmission; TLS over TCP and yamux provide ordered reliable delivery.