Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions docs/http2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# HTTP/2 with Granian

[← Documentation index](index.md)

OxyRoute does not implement HTTP transport itself. HTTP/2 is negotiated and served by the host server (typically Granian) and, in many deployments, by a reverse proxy or load balancer in front of it.

## What OxyRoute guarantees

- The same request pipeline works for HTTP/1.1 and HTTP/2 once a request reaches OxyRoute (`__rsgi__` -> native `run_rsgi`).
- Routing, body parsing, JWT checks, middleware, and response mapping are protocol-version agnostic at the framework layer.
- If `scope.http_version` is exposed by the server, handlers can inspect it via the injected `request` context (`request["scope"]`).

## What depends on server/deployment

- TLS termination and ALPN negotiation (`h2` vs `http/1.1`).
- End-to-end HTTP/2 preservation across proxy hops.
- Connection-level behavior (stream prioritization, flow control, max concurrent streams).

In short: OxyRoute guarantees HTTP semantics on requests it receives; transport negotiation is owned by Granian and your edge/proxy stack.

## Current caveats

- No framework API for HTTP/2 server push (deprecated in browsers anyway).
- No first-class API for HTTP trailers.
- No pseudo-header API (`:authority`, `:scheme`); consume normalized values from the request scope exposed by the server.

These are intentional for now and match the "server owns transport details" design.

## Practical smoke check

You can verify deployment behavior with an HTTP/2-capable client:

```python
import httpx

with httpx.Client(http2=True, verify=True) as c:
r = c.get("https://api.example.com/health")
print(r.http_version) # expected: HTTP/2
```

If you see `HTTP/1.1`, the fallback happened at server/proxy/TLS level, not in OxyRoute routing code.

## See also

- [RSGI and Granian](rsgi.md)
- [ASGI bridge](asgi.md)
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Granian still invokes a Python `App` object; the “win” is doing routing, bod
| [Security headers](security-headers.md) | `SecurityHeadersConfig`, HSTS, CSP |
| [CSRF](csrf.md) | Double-submit, `apply_csrf`, `csrf_layer` + CORS |
| [JWT](jwt.md) | `require_jwt`, HS* / RSA / EC PEM, `decode_jwt_hs` (HS* tests) |
| [HTTP/2 with Granian](http2.md) | Transport guarantees vs server/proxy responsibilities |
| [Dependencies](dependencies.md) | `Depends`, `dependencies=[...]`, `freeze` |
| [OpenAPI](openapi.md) | `openapi.json` route, title, `openapi_json()` |
| [ASGI bridge](asgi.md) | Optional `__call__` for ASGI 3, limitations |
Expand Down
Loading