Skip to content

Native HTTP Driver

The native HTTP driver adapts generated HTTP and WebSocket routes to an Anvil-owned fasthttp runtime.

Install it:

Terminal window
go get github.com/TDB-Group/anvil-http/native

Import it as:

import httpnative "github.com/TDB-Group/anvil-http/native"
app := anvil.New(
httpnative.Driver(),
)

When the native driver is the only HTTP-family transport, Anvil starts it directly on the public address. That avoids the shared net/http edge and gives HTTP and WebSocket routes the lowest-overhead official path.

When GraphQL or gRPC also need the same public listener, Anvil keeps the shared edge runtime. In that mode the native driver still works through its compatibility http.Handler path, so multi-protocol apps keep the same request classification behavior as the other official drivers.

The driver accepts zero options or one httpnative.Options value:

app := anvil.New(
httpnative.Driver(httpnative.Options{
MaxBodyBytes: 8 << 20,
WebSocket: httpnative.WebSocketOptions{
OriginPatterns: []string{"app.example.com"},
},
}),
)

Supported options:

OptionBehavior
ServerCloned *fasthttp.Server settings. Handler and ErrorHandler must be nil because the driver owns them.
CodecsRequest decoder and response encoder registry. Defaults to Anvil’s default codec registry.
ErrorPipelineError mapper and observer pipeline. Driver fills this from app.ErrorPipeline() when the option is nil.
TLSCert/key pair used when the driver owns the listener or is run standalone.
WebSocketOrigin policy, compression, read limits, and socket timeouts.
MaxBodyBytesMaximum request body size. 0 selects the driver default of 4 MiB; negative values are rejected.

The driver applies defensive server defaults when the supplied server leaves fields empty:

Server settingDefault
ReadTimeout30s
WriteTimeout30s
IdleTimeout2m
MaxRequestBodySizeMaxBodyBytes

The driver also enables secure error-log messages, removes the default server header, removes the default content type, and marks connections for close during shutdown.

The native driver exposes an internal listener-owner marker to Anvil. Anvil only uses that direct path when exactly one transport claims the listener and no other HTTP-family edge target is registered.

This keeps the architecture explicit:

  • Native-only HTTP and WebSocket apps run directly on the native server.
  • Apps with GraphQL or gRPC keep Anvil’s shared edge listener.
  • Queue transports still run as background transports alongside either path.

If more than one transport claims listener ownership, Anvil rejects the app before it starts.

sdk.WS routes use the driver’s native WebSocket integration. HTTP middleware runs before the upgrade, which is where auth, origin policy, rate limits, and tenant checks belong.

Origin policy is same-origin by default. Configure OriginPatterns for allowed cross-origin hosts, using the same host or scheme://host pattern style as the standard driver. The native driver rejects a bare * pattern; use InsecureSkipVerify only for explicit test or controlled internal bypasses.

Before the upgrade, middleware and handler errors are mapped into normal HTTP responses. After the upgrade succeeds, failures are mapped and published through the Anvil error pipeline because there is no HTTP response left to write.

The driver uses github.com/fasthttp/websocket. socket.Native() returns *websocket.Conn. The portable socket.Write method supports text and binary messages. socket.Close sends a close control frame and closes the connection.

During shutdown, the driver closes active upgraded sockets with 1001 Going Away before shutting down the server. This covers hijacked connections that the underlying HTTP server cannot close by itself.

ctx.Native() returns:

httpnative.NativeContext{
RequestCtx: requestCtx,
Method: method,
Path: path,
Params: params,
Query: query,
Headers: headers,
Body: body,
}

For normal HTTP handlers, RequestCtx is the active *fasthttp.RequestCtx. For WebSocket handlers, RequestCtx is nil and the remaining fields contain a detached request snapshot, because fasthttp request contexts are reused after an upgrade.

Use Native() for deliberate driver escape hatches. Portable Anvil code should prefer ctx.Request(), ctx.Response(), ctx.Locals(), and ctx.Errors().

httpnative.New creates the transport directly. DriverFrom installs an existing transport into an app:

transport := httpnative.New()
app := anvil.New(httpnative.DriverFrom(transport))

Standalone helpers:

  • Start(addr) listens with the cloned native server.
  • Serve(listener) serves a caller-owned listener.
  • ServeConn(conn) serves one accepted connection.
  • ServeFastHTTP(ctx) executes the native request path directly.
  • ServeHTTP(writer, request) runs the compatibility path used by Anvil’s shared edge.
  • Shutdown(ctx) closes active WebSockets and stops the server.

httpnative.RunTestbed(ctx, suite, opts) executes HTTP, GraphQL-over-HTTP, and WebSocket cases against a running server. The WebSocket runner converts http URLs to ws and https URLs to wss, supports text, binary, ping, and close input messages, and checks expected text, binary, and close output.