server

package
v1.0.3 Latest Latest
Warning

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

Go to latest
Published: Apr 4, 2026 License: MPL-2.0 Imports: 17 Imported by: 0

Documentation

Overview

blocks/server/errors.go

blocks/server/http.go

blocks/server/lambda.go

blocks/server/middleware.go

blocks/server/options.go

blocks/server/router.go

blocks/server/tcp.go

blocks/server/types.go Package server provides inbound integration blocks that receive requests from AWS API Gateway (v1/v2), Application Load Balancer, or as a standalone HTTP server. All three transports normalize incoming events into the same *Request type and serialize *Response back to the appropriate wire format.

A single Handler function works unchanged across all three transports:

handler := func(ctx context.Context, req *server.Request) (*server.Response, error) {
    return server.JSON(200, map[string]string{"hello": req.PathParam("id")}), nil
}

// Run as HTTP server
http := server.NewHTTP("api", server.WithPort(8080), server.WithRouter(router))

// Run as Lambda (API Gateway v2 / ALB)
fn := server.NewLambda("api", server.WithSource(server.SourceAPIGatewayV2), server.WithRouter(router))

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CORSConfig

type CORSConfig struct {
	// AllowOrigins is the list of allowed origins.
	// Use ["*"] to allow all origins.
	AllowOrigins []string
	// AllowMethods lists the HTTP methods allowed for cross-origin requests.
	// Defaults to GET, POST, PUT, PATCH, DELETE, OPTIONS.
	AllowMethods []string
	// AllowHeaders lists additional headers the browser is allowed to send.
	AllowHeaders []string
	// ExposeHeaders lists headers the browser is allowed to read from the response.
	ExposeHeaders []string
	// AllowCredentials enables cookies and auth headers in cross-origin requests.
	AllowCredentials bool
	// MaxAge sets the preflight cache duration in seconds.
	MaxAge int
}

CORSConfig configures the CORS middleware.

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

Conn wraps a raw net.Conn and provides convenience methods for reading frames and writing responses.

func (*Conn) Close

func (c *Conn) Close() error

Close closes the connection immediately.

func (*Conn) LocalAddr

func (c *Conn) LocalAddr() string

LocalAddr returns the local address string of the server-side endpoint.

func (*Conn) Raw

func (c *Conn) Raw() net.Conn

Raw returns the underlying net.Conn for advanced use cases.

func (*Conn) Read

func (c *Conn) Read(p []byte) (int, error)

Read reads up to len(p) bytes into p from the connection. It is a direct pass-through to the underlying net.Conn.

func (*Conn) ReadFull

func (c *Conn) ReadFull(p []byte) error

ReadFull reads exactly len(p) bytes, blocking until all are received.

func (*Conn) ReadMessage

func (c *Conn) ReadMessage() ([]byte, error)

ReadMessage reads a single chunk of data up to the configured buffer size. Returns io.EOF when the client disconnects gracefully.

func (*Conn) RemoteAddr

func (c *Conn) RemoteAddr() string

RemoteAddr returns the remote address string of the connection.

func (*Conn) Write

func (c *Conn) Write(p []byte) (int, error)

Write sends p to the remote device.

type ConnHandler

type ConnHandler func(ctx context.Context, conn *Conn)

ConnHandler is the function signature for handling a raw TCP connection. It is called in its own goroutine for each accepted connection and must return when the connection is done (either by the client disconnecting or when the context is cancelled).

handler := func(ctx context.Context, conn *server.Conn) {
    for {
        msg, err := conn.ReadMessage()
        if err != nil { return }
        fmt.Printf("[%s] %x\n", conn.RemoteAddr(), msg)
    }
}

type HTTPBlock

type HTTPBlock struct {
	// contains filtered or unexported fields
}

HTTPBlock is a standalone HTTP server block. It listens on a configurable port and dispatches requests to the configured Router or Handler.

router := server.NewRouter()
router.GET("/health", func(ctx context.Context, req *server.Request) (*server.Response, error) {
    return server.JSON(200, map[string]string{"status": "ok"}), nil
})

httpBlock := server.NewHTTP("api",
    server.WithPort(8080),
    server.WithRouter(router),
    server.WithMiddleware(server.Logging(), server.Recovery()),
)

app.MustRegister(httpBlock)
app.InitAll(ctx)  // starts listening
httpBlock.Wait()  // blocks until Shutdown is called

func NewHTTP

func NewHTTP(name string, opts ...Option) *HTTPBlock

NewHTTP creates a new HTTP server block.

func (*HTTPBlock) Init

func (b *HTTPBlock) Init(_ context.Context) error

Init implements core.Block. It builds the http.Server and starts listening in a background goroutine. Returns immediately — call Wait to block until the server stops.

func (*HTTPBlock) Name

func (b *HTTPBlock) Name() string

Name implements core.Block.

func (*HTTPBlock) Port

func (b *HTTPBlock) Port() int

Port returns the configured listen port.

func (*HTTPBlock) Shutdown

func (b *HTTPBlock) Shutdown(ctx context.Context) error

Shutdown implements core.Block. It triggers a graceful shutdown of the HTTP server, waiting up to the configured ShutdownTimeout for in-flight requests to complete.

func (*HTTPBlock) Wait

func (b *HTTPBlock) Wait() error

Wait blocks until the server stops — either via Shutdown or due to a fatal error. Returns nil on clean shutdown, or the error that caused the stop. Typical usage:

app.InitAll(ctx)
defer app.ShutdownAll(ctx)
httpBlock.Wait()

type Handler

type Handler func(ctx context.Context, req *Request) (*Response, error)

Handler is the universal request handler. Its signature is identical regardless of transport — API Gateway, ALB, or direct HTTP.

type LambdaBlock

type LambdaBlock struct {
	// contains filtered or unexported fields
}

LambdaBlock is a Lambda integration block that normalizes API Gateway (v1/v2) and ALB events into the universal *Request type and translates *Response back to the wire format expected by each event source.

router := server.NewRouter()
router.GET("/users/:id", getUserHandler)
router.POST("/users",    createUserHandler)

fn := server.NewLambda("api",
    server.WithSource(server.SourceAPIGatewayV2),
    server.WithRouter(router),
    server.WithMiddleware(server.Logging(), server.Recovery()),
)

app.MustRegister(fn)
app.InitAll(ctx)
fn.Start() // blocks — calls lambda.Start() internally

func NewLambda

func NewLambda(name string, opts ...Option) *LambdaBlock

NewLambda creates a new Lambda block. WithSource is required — it determines how events are decoded and how responses are encoded.

func (*LambdaBlock) Init

func (b *LambdaBlock) Init(_ context.Context) error

Init implements core.Block. It resolves the effective handler and validates that a source type has been configured.

func (*LambdaBlock) Name

func (b *LambdaBlock) Name() string

Name implements core.Block.

func (*LambdaBlock) Shutdown

func (b *LambdaBlock) Shutdown(_ context.Context) error

Shutdown implements core.Block. Lambda manages its own lifecycle; this is a no-op but satisfies the interface.

func (*LambdaBlock) Start

func (b *LambdaBlock) Start()

Start registers the Lambda handler and begins polling the Lambda runtime. This call blocks until the Lambda execution environment shuts down. It must be called after app.InitAll.

app.InitAll(ctx)
defer app.ShutdownAll(ctx)
fn.Start() // hand control to the Lambda runtime

type Middleware

type Middleware func(next Handler) Handler

Middleware wraps a Handler to form a processing chain. The pattern is identical to standard http.Handler middleware:

logging := func(next server.Handler) server.Handler {
    return func(ctx context.Context, req *server.Request) (*server.Response, error) {
        start := time.Now()
        resp, err := next(ctx, req)
        slog.Info("request", "method", req.Method, "path", req.Path,
            "status", resp.StatusCode, "latency", time.Since(start))
        return resp, err
    }
}

func CORS

func CORS(cfg CORSConfig) Middleware

CORS returns a Middleware that adds Cross-Origin Resource Sharing headers. Pass an empty CORSConfig{} to use permissive defaults suitable for development.

func Logging

func Logging() Middleware

Logging returns a Middleware that logs each request using slog. It records method, path, status code, and latency at Info level. Errors returned by the handler are logged at Error level.

func Recovery

func Recovery() Middleware

Recovery returns a Middleware that catches panics and converts them into HTTP 500 responses, preventing the entire process from crashing.

func RequestID

func RequestID() Middleware

RequestID returns a Middleware that ensures every request has a request ID. It reads X-Request-Id from the incoming headers; if absent, it uses the transport-supplied ID (API Gateway / ALB request ID). The ID is propagated as X-Request-Id on the response.

type Option

type Option func(*blockConfig)

Option configures a server block.

func WithHandler

func WithHandler(h Handler) Option

WithHandler sets a single Handler that receives every request. Use WithRouter instead when you need method/path routing.

func WithIdleTimeout

func WithIdleTimeout(d time.Duration) Option

WithIdleTimeout sets the maximum time to wait for the next keep-alive request. Defaults to 60 s.

func WithMiddleware

func WithMiddleware(middleware ...Middleware) Option

WithMiddleware registers global middleware applied to every request before it reaches the router or handler. Declared in outermost-first order.

func WithPort

func WithPort(port int) Option

WithPort sets the TCP port the HTTP server listens on. Defaults to 8080.

func WithReadTimeout

func WithReadTimeout(d time.Duration) Option

WithReadTimeout sets the maximum duration for reading the full request. Defaults to 30 s.

func WithRouter

func WithRouter(r *Router) Option

WithRouter sets the Router used to dispatch incoming requests. Either WithRouter or WithHandler must be provided.

func WithShutdownTimeout

func WithShutdownTimeout(d time.Duration) Option

WithShutdownTimeout sets the maximum time allowed for graceful shutdown. Defaults to 10 s.

func WithSource

func WithSource(s Source) Option

WithSource declares which Lambda event source the block expects. Required for Lambda blocks — must be one of SourceAPIGatewayV1, SourceAPIGatewayV2, or SourceALB.

func WithTLS

func WithTLS(certFile, keyFile string) Option

WithTLS configures the server to use TLS with the given certificate and key files. Mutually exclusive with WithTLSConfig.

func WithTLSConfig

func WithTLSConfig(cfg *tls.Config) Option

WithTLSConfig injects a custom *tls.Config.

func WithWriteTimeout

func WithWriteTimeout(d time.Duration) Option

WithWriteTimeout sets the maximum duration for writing the full response. Defaults to 30 s.

type Request

type Request struct {
	// Method is the HTTP verb (GET, POST, PUT, PATCH, DELETE, …).
	Method string
	// Path is the raw URL path (e.g. "/users/123").
	Path string
	// PathParams holds named path parameters extracted by the Router.
	// For the path pattern "/users/:id", PathParams["id"] == "123".
	PathParams map[string]string
	// Query holds the parsed query string parameters, potentially multi-valued.
	Query map[string][]string
	// Headers holds the request headers, normalised to lowercase keys.
	Headers map[string]string
	// Body is the raw request body bytes.
	Body []byte
	// SourceIP is the originating client IP address.
	SourceIP string
	// RequestID is a unique identifier for this request (from X-Request-Id,
	// API Gateway requestId, or ALB traceId).
	RequestID string
	// Stage is the API Gateway deployment stage (e.g. "prod"). Empty for HTTP.
	Stage string
	// Source identifies the transport that delivered this request.
	Source Source
	// Raw holds the original transport-specific event for advanced use cases.
	// For HTTP it is *http.Request; for Lambda it is the raw events struct.
	Raw any
}

Request is the unified, transport-agnostic representation of an incoming HTTP request. It is populated identically regardless of whether the request arrived via API Gateway, ALB, or a direct HTTP connection.

func (*Request) BindJSON

func (r *Request) BindJSON(v any) error

BindJSON unmarshals the request body into v.

func (*Request) Header

func (r *Request) Header(name string) string

Header returns the value of the named header (case-insensitive), or empty string.

func (*Request) PathParam

func (r *Request) PathParam(name string) string

PathParam returns the value of the named path parameter, or empty string.

func (*Request) QueryParam

func (r *Request) QueryParam(name string) string

QueryParam returns the first value of the named query parameter, or empty string.

type Response

type Response struct {
	// StatusCode is the HTTP status code.
	StatusCode int
	// Headers are added to the response alongside any transport defaults.
	Headers map[string]string
	// Body is the response payload.
	// string and []byte are sent as-is; anything else is JSON-marshalled.
	Body any
	// IsBase64 signals that Body is a base64-encoded binary payload.
	// Only relevant for Lambda transports.
	IsBase64 bool
}

Response is the unified outgoing response. The server block serializes it to the appropriate wire format for the active transport.

func Error

func Error(statusCode int, message string) *Response

Error returns a JSON error response with a standard {"error": message} body.

func JSON

func JSON(statusCode int, body any) *Response

JSON returns a Response with the given status code and a JSON-marshalled body. Content-Type is set to application/json automatically.

func NoContent

func NoContent() *Response

NoContent returns a 204 No Content response with no body.

func Redirect

func Redirect(statusCode int, url string) *Response

Redirect returns a 301 or 302 redirect response to the target URL.

func Text

func Text(statusCode int, body string) *Response

Text returns a Response with the given status code and a plain-text body.

type Router

type Router struct {
	// contains filtered or unexported fields
}

Router dispatches incoming requests to registered handlers based on HTTP method and path pattern. Path patterns support named segments (:param) and a catch-all wildcard (*).

r := server.NewRouter()
r.GET("/users",       listUsersHandler)
r.POST("/users",      createUserHandler)
r.GET("/users/:id",   getUserHandler)
r.PUT("/users/:id",   updateUserHandler)
r.DELETE("/users/:id", deleteUserHandler)

Middleware can be applied per-route or globally via Use:

r.Use(loggingMiddleware, authMiddleware)
r.GET("/admin", adminHandler, adminOnlyMiddleware)

func NewRouter

func NewRouter() *Router

NewRouter creates an empty Router. The default not-found handler returns 404 with a JSON error body.

func (*Router) DELETE

func (r *Router) DELETE(pattern string, h Handler, middleware ...Middleware)

DELETE registers a handler for DELETE requests.

func (*Router) GET

func (r *Router) GET(pattern string, h Handler, middleware ...Middleware)

GET registers a handler for GET requests.

func (*Router) HEAD

func (r *Router) HEAD(pattern string, h Handler, middleware ...Middleware)

HEAD registers a handler for HEAD requests.

func (*Router) Handle

func (r *Router) Handle(method, pattern string, h Handler, middleware ...Middleware)

Handle registers a handler for the given method and path pattern. method is the uppercase HTTP verb; pass "" to match any method. Per-route middleware is applied inside the global middleware chain.

func (*Router) NotFound

func (r *Router) NotFound(h Handler)

NotFound sets a custom handler for unmatched routes.

func (*Router) OPTIONS

func (r *Router) OPTIONS(pattern string, h Handler, middleware ...Middleware)

OPTIONS registers a handler for OPTIONS requests.

func (*Router) PATCH

func (r *Router) PATCH(pattern string, h Handler, middleware ...Middleware)

PATCH registers a handler for PATCH requests.

func (*Router) POST

func (r *Router) POST(pattern string, h Handler, middleware ...Middleware)

POST registers a handler for POST requests.

func (*Router) PUT

func (r *Router) PUT(pattern string, h Handler, middleware ...Middleware)

PUT registers a handler for PUT requests.

func (*Router) ServeHTTP

func (r *Router) ServeHTTP(w http.ResponseWriter, httpReq *http.Request)

ServeHTTP adapts the Router to net/http.Handler, bridging the HTTP server block with the universal Handler interface.

func (*Router) Use

func (r *Router) Use(middleware ...Middleware)

Use registers middleware applied to every route in this router. Middleware is applied in declaration order (outermost first).

type Source

type Source string

Source identifies the transport that delivered a request.

const (
	// SourceHTTP identifies a request received directly via the HTTP server block.
	SourceHTTP Source = "http"
	// SourceAPIGatewayV1 identifies an AWS API Gateway REST API (v1) event.
	SourceAPIGatewayV1 Source = "apigateway_v1"
	// SourceAPIGatewayV2 identifies an AWS API Gateway HTTP API (v2) event.
	SourceAPIGatewayV2 Source = "apigateway_v2"
	// SourceALB identifies an AWS Application Load Balancer target group event.
	SourceALB Source = "alb"
)

type TCPBlock

type TCPBlock struct {
	// contains filtered or unexported fields
}

TCPBlock is a raw TCP server block. It listens on a configurable port and dispatches each accepted connection to the registered ConnHandler in its own goroutine.

handler := func(ctx context.Context, conn *server.Conn) {
    defer conn.Close()
    for {
        msg, err := conn.ReadMessage()
        if err != nil { return }
        // process msg...
    }
}

tcp := server.NewTCP("tracker",
    server.WithTCPPort(5001),
    server.WithConnHandler(handler),
    server.WithBufSize(1024),
)

app.MustRegister(tcp)
app.InitAll(ctx)
tcp.Wait()

func NewTCP

func NewTCP(name string, opts ...TCPOption) *TCPBlock

NewTCP creates a new TCP server block.

tcp := server.NewTCP("obd-tracker",
    server.WithTCPPort(5001),
    server.WithConnHandler(myHandler),
    server.WithBufSize(2048),
    server.WithConnReadTimeout(5*time.Minute),
)

func (*TCPBlock) Init

func (b *TCPBlock) Init(_ context.Context) error

Init implements core.Block. It opens the TCP listener and starts the accept loop in a background goroutine. Returns immediately.

func (*TCPBlock) Name

func (b *TCPBlock) Name() string

Name implements core.Block.

func (*TCPBlock) Port

func (b *TCPBlock) Port() int

Port returns the configured listen port.

func (*TCPBlock) Shutdown

func (b *TCPBlock) Shutdown(_ context.Context) error

Shutdown implements core.Block. Closes the listener (which unblocks Accept), then waits up to the configured shutdown timeout for active connections to finish.

func (*TCPBlock) Wait

func (b *TCPBlock) Wait() error

Wait blocks until the TCP server stops — either via Shutdown or a fatal accept error. Returns nil on clean shutdown.

type TCPOption

type TCPOption func(*tcpConfig)

TCPOption configures a TCPBlock.

func WithBufSize

func WithBufSize(n int) TCPOption

WithBufSize sets the default buffer size for ReadMessage. Defaults to 1024 bytes.

func WithConnHandler

func WithConnHandler(h ConnHandler) TCPOption

WithConnHandler sets the function called for each accepted connection. Required — Init returns an error if not provided.

func WithConnReadTimeout

func WithConnReadTimeout(d time.Duration) TCPOption

WithConnReadTimeout sets a per-read deadline on each connection. 0 means no deadline (default).

func WithConnWriteTimeout

func WithConnWriteTimeout(d time.Duration) TCPOption

WithConnWriteTimeout sets a per-write deadline on each connection.

func WithTCPPort

func WithTCPPort(port int) TCPOption

WithTCPPort sets the TCP port to listen on. Defaults to 5001.

func WithTCPShutdownTimeout

func WithTCPShutdownTimeout(d time.Duration) TCPOption

WithTCPShutdownTimeout sets the maximum time to wait for active connections to finish before forcing close. Defaults to 10 s.

Jump to

Keyboard shortcuts

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