proxy

package
v0.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 13, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package proxy provides NewReverseProxy — the one-liner that turns a session-affine process pool into an HTTP gateway.

Why this exists

Pool.Acquire gives you a *Session. But in the common case — proxying HTTP traffic to a subprocess — you still need to write the reverse-proxy loop yourself: acquire a session, build a *httputil.ReverseProxy pointing at session.Worker.Address(), forward the request, handle errors. That's 30 lines every user copy-pastes. NewReverseProxy collapses it to one call.

Usage

pool, _ := herd.New(herd.NewProcessFactory("./my-binary", "--port", "{{.Port}}"))

proxy := proxy.NewReverseProxy(pool, func(r *http.Request) string {
    return r.Header.Get("X-Session-ID")
})

http.ListenAndServe(":8080", proxy)

Session lifecycle

NewReverseProxy acquires a session at the START of each HTTP request and releases it at the END (after the response is written). This means a single HTTP request holds a worker exclusively for its duration — appropriate for request-scoped work (a browser API call, an LLM inference call, a REPL eval).

For long-lived sessions where the same sessionID should stay pinned across many requests — e.g. a stateful REPL session that must keep the same process — callers should call Pool.Acquire / Session.Release directly and store the *Session in their own state (e.g. an HTTP session cookie → in-memory map).

Error handling

If extractSessionID returns an empty string, ServeHTTP returns 400. If Pool.Acquire fails (timeout, all workers crashed), ServeHTTP returns 503. If the upstream subprocess returns a non-2xx, it is forwarded as-is — the proxy does not interfere with application-level error codes.

File layout

proxy/proxy.go   — NewReverseProxy + ReverseProxy.ServeHTTP (THIS FILE)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ReverseProxy

type ReverseProxy[C any] struct {
	// contains filtered or unexported fields
}

ReverseProxy is an http.Handler that acquires a session from pool, proxies the request to the worker's address, and releases the session when done.

C is the worker client type — for ProcessFactory this is *http.Client. ReverseProxy does not use C directly; it proxies via the worker's Address().

func NewReverseProxy

func NewReverseProxy[C any](
	pool *herd.Pool[C],
	extractSessionID func(*http.Request) string,
) *ReverseProxy[C]

NewReverseProxy returns an http.Handler that:

  1. Calls extractSessionID(r) to determine which session this request belongs to.
  2. Calls pool.Acquire(ctx, sessionID) to get (or create) the pinned worker.
  3. Reverse-proxies the request to worker.Address().
  4. Calls session.Release() after the response is written.

extractSessionID may inspect any part of the request — a header, a cookie, a path prefix, or a query parameter. It must return a non-empty string.

Example: route by X-Session-ID header:

proxy := proxy.NewReverseProxy(pool, func(r *http.Request) string {
    return r.Header.Get("X-Session-ID")
})

func (*ReverseProxy[C]) ServeHTTP

func (rp *ReverseProxy[C]) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler.

Steps:

  1. Extract sessionID — return 400 if empty.
  2. Acquire session — return 503 if pool is exhausted or ctx cancelled.
  3. Parse worker address into *url.URL.
  4. Build a per-request httputil.ReverseProxy targeting that URL.
  5. Forward request; on response write, release the session.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL