Skip to content

Latest commit

 

History

54 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

h2ts

HTTP/2 in the frontend — tunneled over WebSockets. Native HTTP/2 clients and servers that carry HTTP/2 frames inside a WebSocket, one language at a time.

CI license client tests

Browsers can't open raw TCP sockets or speak HTTP/2 with prior knowledge, and give you no control over framing, multiplexing, or server push. h2ts gives a frontend a real HTTP/2 client by carrying HTTP/2 frames inside a WebSocket, plus servers that terminate the WebSocket and hand the raw bytes to any HTTP/2 server.

It's a monorepo of native, per-language implementations. They share no code — they stay interoperable by conforming to one wire spec and passing one conformance suite. Shared behavior, not shared implementation.

Packages

Clients originate HTTP/2 from a frontend; servers terminate the WebSocket and serve or proxy HTTP/2.

Client (frontend) Server (gateway)
TypeScript @debdattabasu/h2ts — the ~9 KB, zero-dep browser/Node client · npm @h2ts/server · planned
Rust h2ts-client — for WASM frontends (Leptos/Yew/Dioxus), no hyper/tokio · crates.io h2ts-server — hyper/axum/tower + the h2ts-proxy binary · crates.io
Go .../h2ts/go — any net/http handler served as h2c over the tunnel (in-process serve shape)

Shared: spec/protocol.md (the wire contract) · conformance/ (cross-stack e2e) · wslay-sys (wslay FFI — powers the Rust server's sub-frame streaming, crates.io).

Writing your frontend in Rust? h2ts-client is a from-scratch, sans-I/O HTTP/2 implementation built for wasm32 — it won't pull in hyper, tokio, or any other heavy async/server crate — so Rust frontends (Leptos, Yew, Dioxus, …) get real multiplexed HTTP/2 with server push over a WebSocket, in Rust, without dropping to JS or bloating the bundle.

Architecture

   ┌─────────────────────────┐        ┌──────────────────────────┐        ┌─────────────────────┐
   │  frontend (client)       │  wss   │  gateway (h2ts server)    │  h2c   │  HTTP/2 server       │
   │  ── h2ts / h2ts-client ─ │ ─────▶ │  ── terminates the WS ──────── │ ─────▶ │  hyper / axum / any  │
   │  speaks real HTTP/2      │ frames │  WebSocket ⇄ raw TCP bytes │  TCP   │  h2c upstream        │
   └─────────────────────────┘ ◀───── └──────────────────────────┘ ◀───── └─────────────────────┘
          HPACK · multiplexing              full-duplex byte pump              plain HTTP/2 (cleartext)
          flow control · push

HTTP/2 frames ride inside the WebSocket as binary messages. TLS is provided by wss:// on the outside; the tunneled HTTP/2 is cleartext (h2c, prior-knowledge) — no TLS, ALPN, or Upgrade dance on the inside. The client offers the h2ts subprotocol; the gateway negotiates it and rejects clients that don't (unless configured otherwise). Full details — subprotocol negotiation, control frames, keepalive — are in spec/protocol.md.

Servers come in two shapes: a standalone proxy (h2ts-proxy — forward raw bytes to an upstream h2c server, a drop-in websockify replacement) or in-process (serve your own service over the tunnel).

Repo layout

h2ts/
├── spec/
│   └── protocol.md             # the language-neutral wire contract
├── conformance/                # cross-stack e2e (any client × any gateway, by WS_URL)
├── typescript/                 # npm workspace
│   ├── client/                 #   h2ts — the TypeScript client
│   └── server/                 #   @h2ts/server (planned)
├── rust/                       # Cargo workspace
│   └── crates/
│       ├── h2ts-client/        #   Rust client for WASM frontends
│       ├── h2ts-server/        #   server library + h2ts-proxy binary
│       └── wslay-sys/          #   wslay FFI framing backend
├── go/                         # Go module
│   ├── server/                 #   Accept + ServeH2 (h2c served over the tunnel)
│   └── examples/h2-server/     #   runnable serve gateway (conformance routes)
└── Makefile                    # top-level tasks (fan out to each stack)

Build & test

The top-level Makefile fans out across every stack; or drive each directly.

make test             # everything: rust + typescript + go + conformance
make conformance      # cross-stack e2e (client -> h2ts-proxy -> h2c origin)
make conformance-go   # same battery, but against the Go serve gateway

# or per stack:
cd rust && cargo test
cd typescript && npm install && npm test -w client
cd go && go test ./...

The conformance suite runs a fixed battery — routing, JSON, byte-exact uploads/downloads, concurrent multiplexed streams, streaming reads, ping, trailers, 1xx early hints, 404 — and passes identically across gateways, selected by GATEWAY: the Rust proxy (h2ts-proxy, default), the in-process Rust serve_h2 (h2-server example), and the Go ServeH2 gateway (GATEWAY=go) — each driven by both the TypeScript and Rust clients. Per-package usage lives in each package's README: @debdattabasu/h2ts client, h2ts-server, h2ts-client, Go server.

Roadmap

  • TypeScript client h2ts — HTTP/2 (framing, HPACK, flow control, multiplexing, push), WebSocket transport, fetch-like API
  • Rust server h2ts-serveraccept, bridge, WsByteStream, serve_h2, the h2ts-proxy binary, and wslay sub-frame streaming (via wslay-sys)
  • Publish h2ts-server + wslay-sys to crates.io
  • Monorepo restructure: one wire spec + conformance suite across languages
  • h2ts-client (Rust) — a wasm32, no-hyper client for Rust frontends, published to crates.io
  • Publish the @debdattabasu/h2ts client to npm
  • Go server (go/) — Accept + ServeH2 serve any net/http handler as h2c over the tunnel (in-process serve shape; the proxy stays the single Rust h2ts-proxy). Pure-Go RFC 6455 framing, control-frame hooks, server-initiated keepalive, and an HTTP/2 idle timeout; passes conformance under both the TypeScript and Rust clients
  • HTTP/2 idle timeout on the serve gateways — reap a healthy-but-idle connection (no open streams for a TTL) with a graceful GOAWAY, in both the Go server and the Rust serve_h2 (h2ts-server 0.1.2)
  • Node.js server (@h2ts/server) — serve a node:http2 service over the tunnel (scaffolded)
  • Envoy filter — terminate the WebSocket tunnel as an Envoy HTTP filter, to run the gateway inside an existing Envoy/proxy mesh

License

Licensed under either of

at your option. This is the customary dual license of the Rust ecosystem: the MIT option keeps things permissive and familiar, the Apache-2.0 option adds an explicit patent grant. The vendored wslay C sources under rust/crates/wslay-sys/vendor are third-party and remain MIT-only — see vendor/COPYING.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

About

HTTP/2 in the browser, tunneled over WebSockets: a tiny from-scratch TypeScript HTTP/2 client (~9 KB) + a Rust WebSocket⇄TCP gateway.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages