bhttp

package module
v0.3.5 Latest Latest
Warning

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

Go to latest
Published: Feb 4, 2026 License: MIT Imports: 11 Imported by: 3

README

bhttp

Go Reference Go Report Card

Buffered HTTP handlers with error returns for Go.

Why bhttp?

Go's standard library HTTP handlers return nothing: func(w http.ResponseWriter, r *http.Request). This forces error handling inline with http.Error() calls scattered throughout your code, making it hard to centralize error responses or recover from partial writes.

bhttp adds one thing: handlers that return errors. It builds directly on net/http (including Go 1.22+ routing patterns) rather than replacing it. The buffered response writer is a necessary consequence—it allows the framework to discard partial output and generate clean error responses when handlers fail.

Overview

bhttp extends the standard library's HTTP handling with:

  • Buffered responses that allow complete response rewriting on errors
  • Error-returning handlers for clean error propagation
  • Named routes with URL generation
  • Typed contexts for request-scoped data

Installation

go get github.com/advdv/bhttp

Quick Start

package main

import (
    "context"
    "encoding/json"
    "errors"
    "net/http"

    "github.com/advdv/bhttp"
)

func main() {
    mux := bhttp.NewServeMux()

    mux.HandleFunc("GET /items/{id}", func(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
        id := r.PathValue("id")
        if id == "" {
            return bhttp.NewError(bhttp.CodeBadRequest, errors.New("missing id"))
        }

        item := map[string]string{"id": id, "name": "Example"}
        return json.NewEncoder(w).Encode(item)
    }, "get-item")

    // Generate URLs by name
    url, _ := mux.Reverse("get-item", "123") // "/items/123"
    _ = url

    http.ListenAndServe(":8080", mux)
}

Features

Buffered Response Writer

All writes are buffered, enabling complete response replacement when errors occur:

func handler(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
    w.Header().Set("Content-Type", "application/json")
    fmt.Fprintf(w, `{"status": "processing"`)

    result, err := expensiveOperation()
    if err != nil {
        // Buffer is cleared, clean error response sent instead
        return bhttp.NewError(bhttp.CodeInternalServerError, err)
    }

    fmt.Fprintf(w, `, "data": %q}`, result)
    return nil
}
Error Handling

Return errors with HTTP status codes:

return bhttp.NewError(bhttp.CodeNotFound, errors.New("user not found"))
return bhttp.NewError(bhttp.CodeBadRequest, errors.New("invalid input"))
return bhttp.NewError(bhttp.CodeForbidden, errors.New("access denied"))

Unhandled errors become 500 Internal Server Error responses.

Middleware

Middleware operates on the bare handler level:

func loggingMiddleware(next bhttp.BareHandler) bhttp.BareHandler {
    return bhttp.BareHandlerFunc(func(w bhttp.ResponseWriter, r *http.Request) error {
        start := time.Now()
        err := next.ServeBareBHTTP(w, r)
        log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
        return err
    })
}

mux := bhttp.NewServeMux()
mux.Use(loggingMiddleware)
Typed Contexts

Use custom context types for request-scoped data:

type AppContext struct {
    context.Context
    User *User
}

func initContext(r *http.Request) (AppContext, error) {
    user, err := authenticate(r)
    if err != nil {
        return AppContext{}, bhttp.NewError(bhttp.CodeUnauthorized, err)
    }
    return AppContext{Context: r.Context(), User: user}, nil
}

mux := bhttp.NewCustomServeMux(initContext, -1, logger, http.NewServeMux(), bhttp.NewReverser())
Named Routes

Register routes with names for URL generation:

mux.HandleFunc("GET /users/{id}", getUser, "get-user")
mux.HandleFunc("POST /users/{id}/posts/{slug}", getPost, "get-post")

url, err := mux.Reverse("get-user", "123")         // "/users/123"
url, err = mux.Reverse("get-post", "123", "hello") // "/users/123/posts/hello"

Documentation

See the Go documentation for complete API reference.

License

MIT License - see LICENSE for details.

Documentation

Overview

Package bhttp provides buffered HTTP response handling with error-returning handlers.

Overview

bhttp extends the standard library's HTTP handling with two key features: buffered response writers that allow complete response rewriting on errors, and handlers that return errors instead of requiring inline error handling. This design enables cleaner error propagation and centralized error responses.

A minimal example:

mux := bhttp.NewServeMux()
mux.HandleFunc("GET /items/{id}", func(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
    item, err := db.GetItem(r.PathValue("id"))
    if err != nil {
        return bhttp.NewError(bhttp.CodeNotFound, err)
    }
    json.NewEncoder(w).Encode(item)
    return nil
}, "get-item")

Handler Signature

bhttp handlers differ from standard http.Handlers in three ways:

  • They receive a typed context as the first argument (not embedded in the request)
  • They write to a ResponseWriter that buffers output
  • They return an error that triggers automatic response handling

The handler signature is:

func(ctx C, w bhttp.ResponseWriter, r *http.Request) error

Where C is any type satisfying the Context constraint (embedding context.Context). For simple cases, use context.Context directly with NewServeMux.

Buffered Response Writer

The ResponseWriter interface extends http.ResponseWriter with buffering. All writes are held in memory until explicitly flushed or until the handler returns successfully. This enables:

  • Complete response replacement when errors occur mid-handler
  • Headers modification after initial writes
  • Clean error responses without partial content

Key methods:

  • [ResponseWriter.Reset] clears the buffer and headers for a fresh response
  • [ResponseWriter.FlushBuffer] writes buffered content to the underlying writer
  • [ResponseWriter.Free] returns the buffer to a pool (called automatically by the mux)

Example of response replacement on error:

func handler(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
    w.Header().Set("Content-Type", "application/json")
    fmt.Fprintf(w, `{"status": "processing"`)

    result, err := process()
    if err != nil {
        // Everything written so far is discarded
        return bhttp.NewError(bhttp.CodeInternalServerError, err)
    }

    fmt.Fprintf(w, `, "result": %q}`, result)
    return nil
}

Error Handling

When a handler returns an error, the buffer is automatically reset and an appropriate HTTP error response is generated:

  • *Error (created with NewError): Uses the error's code and message
  • Other errors: Logged and converted to 500 Internal Server Error

Create errors with specific HTTP status codes using NewError:

return bhttp.NewError(bhttp.CodeBadRequest, errors.New("invalid input"))
return bhttp.NewError(bhttp.CodeNotFound, fmt.Errorf("user %s not found", id))
return bhttp.NewError(bhttp.CodeForbidden, errors.New("access denied"))

All standard HTTP 4xx and 5xx status codes are available as Code constants.

Middleware

Middleware wraps handlers to add cross-cutting concerns. The Middleware type operates on BareHandler, which lacks the typed context (middleware runs before context initialization):

func loggingMiddleware(next bhttp.BareHandler) bhttp.BareHandler {
    return bhttp.BareHandlerFunc(func(w bhttp.ResponseWriter, r *http.Request) error {
        start := time.Now()
        err := next.ServeBareBHTTP(w, r)
        log.Printf("%s %s took %v", r.Method, r.URL.Path, time.Since(start))
        return err
    })
}

mux := bhttp.NewServeMux()
mux.Use(loggingMiddleware)

Middleware can inspect and transform errors, modify the request context, or reset and replace responses entirely.

Named Routes and URL Reversing

Routes can be named for URL generation, avoiding hardcoded paths:

mux.HandleFunc("GET /users/{id}", getUser, "get-user")
mux.HandleFunc("POST /users", createUser, "create-user")

// Generate URLs by name
url, err := mux.Reverse("get-user", "123")  // returns "/users/123"

The Reverser component parses standard library route patterns and substitutes path parameters in order.

Typed Context

For applications requiring request-scoped typed data, use NewCustomServeMux with a custom context type:

type AppContext struct {
    context.Context
    User   *User
    Logger *slog.Logger
}

func initContext(r *http.Request) (AppContext, error) {
    user, err := authenticate(r)
    if err != nil {
        return AppContext{}, bhttp.NewError(bhttp.CodeUnauthorized, err)
    }
    return AppContext{
        Context: r.Context(),
        User:    user,
        Logger:  slog.Default().With("user", user.ID),
    }, nil
}

mux := bhttp.NewCustomServeMux(initContext, -1, logger, http.NewServeMux(), bhttp.NewReverser())

Handlers then receive the typed context directly:

func getProfile(ctx AppContext, w bhttp.ResponseWriter, r *http.Request) error {
    ctx.Logger.Info("fetching profile")
    return json.NewEncoder(w).Encode(ctx.User)
}

ServeMux

ServeMux combines all components into a complete HTTP multiplexer that implements http.Handler:

Converting to Standard Library

bhttp handlers can be converted to standard http.Handlers for use with any router or server:

handler := bhttp.HandlerFunc[context.Context](myHandler)
bare := bhttp.ToBare(handler, bhttp.StdContextInit)
stdHandler := bhttp.ToStd(bare, bufferLimit, logger)

The conversion chain is:

Handler[C] → BareHandler → http.Handler

ToBare applies the context initializer, ToStd wraps with buffering and error handling.

Example
package main

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/advdv/bhttp"
)

func main() {
	mux := bhttp.NewServeMux()

	mux.HandleFunc("GET /items/{id}", func(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
		id := r.PathValue("id")
		if id == "" {
			return bhttp.NewError(bhttp.CodeBadRequest, errors.New("missing id"))
		}

		w.Header().Set("Content-Type", "application/json")
		return json.NewEncoder(w).Encode(map[string]string{
			"id":   id,
			"name": "Example Item",
		})
	}, "get-item")

	// Generate URL by route name
	url, _ := mux.Reverse("get-item", "123")
	fmt.Println("URL:", url)

	// Test the handler
	rec := httptest.NewRecorder()
	req := httptest.NewRequest(http.MethodGet, "/items/42", nil)
	mux.ServeHTTP(rec, req)

	fmt.Println("Status:", rec.Code)
}
Output:

URL: /items/123
Status: 200

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrBufferFull = errors.New("buffer is full")

ErrBufferFull is returned when the write call will cause the buffer to be filled beyond its limit.

Functions

func StdContextInit added in v0.3.1

func StdContextInit(r *http.Request) (context.Context, error)

StdContextInit is a context initializer that just pulls the context from the http.Request.

func ToStd added in v0.3.0

func ToStd(h BareHandler, bufLimit int, logs Logger) http.Handler

ToStd converts a bare handler into a standard library http.Handler. The implementation creates a buffered response writer and flushes it implicitly after serving the request.

Types

type BareHandler added in v0.3.0

type BareHandler interface {
	ServeBareBHTTP(w ResponseWriter, r *http.Request) error
}

BareHandler describes how middleware servers HTTP requests. In this library the signature for handling middleware BareHandler is different from the signature of "leaf" handlers: Handler.

func ToBare added in v0.3.0

func ToBare[C Context](h Handler[C], contextInit ContextInitFunc[C]) BareHandler

ToBare converts a typed context handler 'h' into a bare buffered handler.

func Wrap added in v0.3.0

func Wrap[C Context](h Handler[C], contextInit ContextInitFunc[C], m ...Middleware) BareHandler

Wrap takes the inner handler h and wraps it with middleware. The order is that of the Gorilla and Chi router. That is: the middleware provided first is called first and is the "outer" most wrapping, the middleware provided last will be the "inner most" wrapping (closest to the handler).

type BareHandlerFunc added in v0.3.0

type BareHandlerFunc func(ResponseWriter, *http.Request) error

BareHandlerFunc allow casting a function to an implementation of Handler.

func (BareHandlerFunc) ServeBareBHTTP added in v0.3.0

func (f BareHandlerFunc) ServeBareBHTTP(w ResponseWriter, r *http.Request) error

ServeBareBHTTP implements the Handler interface.

type Code added in v0.3.2

type Code int

Code is an error code that mirrors the http status codes. It can be used to create errors to pass around across middleware layers to handle errors structurally.

const (
	CodeUnknown                      Code = 0
	CodeBadRequest                   Code = http.StatusBadRequest                   // RFC 9110, 15.5.1
	CodeUnauthorized                 Code = http.StatusUnauthorized                 // RFC 9110, 15.5.2
	CodePaymentRequired              Code = http.StatusPaymentRequired              // RFC 9110, 15.5.3
	CodeForbidden                    Code = http.StatusForbidden                    // RFC 9110, 15.5.4
	CodeNotFound                     Code = http.StatusNotFound                     // RFC 9110, 15.5.5
	CodeMethodNotAllowed             Code = http.StatusMethodNotAllowed             // RFC 9110, 15.5.6
	CodeNotAcceptable                Code = http.StatusNotAcceptable                // RFC 9110, 15.5.7
	CodeProxyAuthRequired            Code = http.StatusProxyAuthRequired            // RFC 9110, 15.5.8
	CodeRequestTimeout               Code = http.StatusRequestTimeout               // RFC 9110, 15.5.9
	CodeConflict                     Code = http.StatusConflict                     // RFC 9110, 15.5.10
	CodeGone                         Code = http.StatusGone                         // RFC 9110, 15.5.11
	CodeLengthRequired               Code = http.StatusLengthRequired               // RFC 9110, 15.5.12
	CodePreconditionFailed           Code = http.StatusPreconditionFailed           // RFC 9110, 15.5.13
	CodeRequestEntityTooLarge        Code = http.StatusRequestEntityTooLarge        // RFC 9110, 15.5.14
	CodeRequestURITooLong            Code = http.StatusRequestURITooLong            // RFC 9110, 15.5.15
	CodeUnsupportedMediaType         Code = http.StatusUnsupportedMediaType         // RFC 9110, 15.5.16
	CodeRequestedRangeNotSatisfiable Code = http.StatusRequestedRangeNotSatisfiable // RFC 9110, 15.5.17
	CodeExpectationFailed            Code = http.StatusExpectationFailed            // RFC 9110, 15.5.18
	CodeTeapot                       Code = http.StatusTeapot                       // RFC 9110, 15.5.19 (Unused)
	CodeMisdirectedRequest           Code = http.StatusMisdirectedRequest           // RFC 9110, 15.5.20
	CodeUnprocessableEntity          Code = http.StatusUnprocessableEntity          // RFC 9110, 15.5.21
	CodeLocked                       Code = http.StatusLocked                       // RFC 4918, 11.3
	CodeFailedDependency             Code = http.StatusFailedDependency             // RFC 4918, 11.4
	CodeTooEarly                     Code = http.StatusTooEarly                     // RFC 8470, 5.2.
	CodeUpgradeRequired              Code = http.StatusUpgradeRequired              // RFC 9110, 15.5.22
	CodePreconditionRequired         Code = http.StatusPreconditionRequired         // RFC 6585, 3
	CodeTooManyRequests              Code = http.StatusTooManyRequests              // RFC 6585, 4
	CodeRequestHeaderFieldsTooLarge  Code = http.StatusRequestHeaderFieldsTooLarge  // RFC 6585, 5
	CodeUnavailableForLegalReasons   Code = http.StatusUnavailableForLegalReasons   // RFC 7725, 3

	CodeInternalServerError           Code = http.StatusInternalServerError           // RFC 9110, 15.6.1
	CodeNotImplemented                Code = http.StatusNotImplemented                // RFC 9110, 15.6.2
	CodeBadGateway                    Code = http.StatusBadGateway                    // RFC 9110, 15.6.3
	CodeServiceUnavailable            Code = http.StatusServiceUnavailable            // RFC 9110, 15.6.4
	CodeGatewayTimeout                Code = http.StatusGatewayTimeout                // RFC 9110, 15.6.5
	CodeHTTPVersionNotSupported       Code = http.StatusHTTPVersionNotSupported       // RFC 9110, 15.6.6
	CodeVariantAlsoNegotiates         Code = http.StatusVariantAlsoNegotiates         // RFC 2295, 8.1
	CodeInsufficientStorage           Code = http.StatusInsufficientStorage           // RFC 4918, 11.5
	CodeLoopDetected                  Code = http.StatusLoopDetected                  // RFC 5842, 7.2
	CodeNotExtended                   Code = http.StatusNotExtended                   // RFC 2774, 7
	CodeNetworkAuthenticationRequired Code = http.StatusNetworkAuthenticationRequired // RFC 6585, 6
)

func CodeOf added in v0.3.2

func CodeOf(err error) Code

CodeOf returns the error's status code if it is or wraps an *Error and CodeUnknown otherwise.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/advdv/bhttp"
)

func main() {
	// Create an error with a specific code
	err := bhttp.NewError(bhttp.CodeNotFound, errors.New("user not found"))
	fmt.Println("Code:", bhttp.CodeOf(err))

	// Wrapped errors preserve the code
	wrapped := fmt.Errorf("handler failed: %w", err)
	fmt.Println("Wrapped code:", bhttp.CodeOf(wrapped))

	// Non-bhttp errors return CodeUnknown
	plainErr := errors.New("something went wrong")
	fmt.Println("Plain error code:", bhttp.CodeOf(plainErr))
}
Output:

Code: 404
Wrapped code: 404
Plain error code: 0

type Context

type Context interface{ context.Context }

Context constraint for "leaf" nodes.

type ContextInitFunc added in v0.2.0

type ContextInitFunc[C Context] func(*http.Request) (C, error)

ContextInitFunc describe functions that turn requests into a typed context for our "leaf" handlers.

type Error added in v0.3.2

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

Error describes an http error.

func NewError added in v0.3.2

func NewError(c Code, underlying error) *Error

NewError inits a new error given the error code.

Example
package main

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/advdv/bhttp"
)

func main() {
	mux := bhttp.NewServeMux()

	mux.HandleFunc("GET /protected", func(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
		token := r.Header.Get("Authorization")
		if token == "" {
			return bhttp.NewError(bhttp.CodeUnauthorized, errors.New("missing token"))
		}
		if token != "Bearer secret" {
			return bhttp.NewError(bhttp.CodeForbidden, errors.New("invalid token"))
		}
		fmt.Fprint(w, "welcome")
		return nil
	})

	// Request without token
	rec := httptest.NewRecorder()
	req := httptest.NewRequest(http.MethodGet, "/protected", nil)
	mux.ServeHTTP(rec, req)
	fmt.Println("No token:", rec.Code)

	// Request with invalid token
	rec = httptest.NewRecorder()
	req = httptest.NewRequest(http.MethodGet, "/protected", nil)
	req.Header.Set("Authorization", "Bearer wrong")
	mux.ServeHTTP(rec, req)
	fmt.Println("Bad token:", rec.Code)

	// Request with valid token
	rec = httptest.NewRecorder()
	req = httptest.NewRequest(http.MethodGet, "/protected", nil)
	req.Header.Set("Authorization", "Bearer secret")
	mux.ServeHTTP(rec, req)
	fmt.Println("Valid token:", rec.Code)
}
Output:

No token: 401
Bad token: 403
Valid token: 200

func (*Error) Code added in v0.3.2

func (e *Error) Code() Code

func (*Error) Error added in v0.3.2

func (e *Error) Error() string

type Handler

type Handler[C Context] interface {
	ServeBHTTP(ctx C, w ResponseWriter, r *http.Request) error
}

Handler mirrors http.Handler but it supports typed context values and a buffered response allow returning error.

type HandlerFunc

type HandlerFunc[C Context] func(C, ResponseWriter, *http.Request) error

HandlerFunc allow casting a function to imple Handler.

func (HandlerFunc[C]) ServeBHTTP

func (f HandlerFunc[C]) ServeBHTTP(ctx C, w ResponseWriter, r *http.Request) error

ServeBHTTP implements the Handler interface.

type Logger

type Logger interface {
	LogUnhandledServeError(err error)
	LogImplicitFlushError(err error)
}

Logger can be implemented to get informed about important states.

func NewStdLogger added in v0.3.0

func NewStdLogger(l *log.Logger) Logger

type Middleware

type Middleware func(BareHandler) BareHandler

Middleware for cross-cutting concerns with buffered responses.

type ResponseBuffer

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

ResponseBuffer is a http.ResponseWriter implementation that buffers writes up to configurable amount of bytes. This allows the implementation of handlers that can error halfway and return a completely different response instead of what was written before the error occurred.

func (*ResponseBuffer) FlushBuffer added in v0.3.0

func (w *ResponseBuffer) FlushBuffer() error

FlushBuffer flushes data to the underlying writer without calling .Flush on it by proxy. This is provided separately from FlushError to allow for emulating the original ResponseWriter behaviour more correctly.

func (*ResponseBuffer) FlushError

func (w *ResponseBuffer) FlushError() error

FlushError any buffered bytes to the underlying response writer and resets the buffer. After flush has been called the response data should be considered sent and in-transport to the client.

func (*ResponseBuffer) Free

func (w *ResponseBuffer) Free()

Free resets all members of the ResponseBuffer and puts it back in the sync pool to allow it to be re-used for a possible next initilization. It should be called after the handling has completed and the buffer should not be used after.

func (*ResponseBuffer) Header

func (w *ResponseBuffer) Header() http.Header

Header allows users to modify the headers (and trailers) sent to the client. The headers are not actually flushed to the underlying writer until a write or flush is being triggered.

func (*ResponseBuffer) Reset

func (w *ResponseBuffer) Reset()

Reset provides the differentiating feature from a regular ResponseWriter: it allows changing the response completely even if some data has been written already. This behaviour cannot be guaranteed if flush has been called explicitly so in that case it will panic.

func (*ResponseBuffer) Unwrap

func (w *ResponseBuffer) Unwrap() http.ResponseWriter

Unwrap returns the underlying response writer. This is expected by the http.ResponseController to allow it to call appropriate optional interface implementations.

func (*ResponseBuffer) Write

func (w *ResponseBuffer) Write(buf []byte) (int, error)

Write appends the contents of p to the buffered response, growing the internal buffer as needed. If the write will cause the buffer be larger then the configure limit it will return ErrBufferFull.

func (*ResponseBuffer) WriteHeader

func (w *ResponseBuffer) WriteHeader(statusCode int)

WriteHeader will cause headers to be flushed to the underlying writer while calling WriteHeader on the underlying writer with the given status code.

type ResponseWriter

type ResponseWriter interface {
	http.ResponseWriter
	Reset()
	Free()
	FlushBuffer() error
}

ResponseWriter implements the http.ResponseWriter but the underlying bytes are buffered. This allows middleware to reset the writer and formulate a completely new response.

func NewResponseWriter added in v0.3.0

func NewResponseWriter(resp http.ResponseWriter, limit int) ResponseWriter

NewResponseWriter inits a buffered response writer. It has a configurable limit after which the writing will return an error. This is to protect unchecked handlers from claiming too much memory. Limit can be set to -1 to disable this check.

type Reverser

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

Reverser keeps track of named patterns and allows building URLS.

func NewReverser

func NewReverser() *Reverser

NewReverser inits the reverser.

func (Reverser) Named

func (r Reverser) Named(name, str string) string

Named is a convenience method that panics if naming the pattern fails.

func (Reverser) NamedPattern

func (r Reverser) NamedPattern(name, str string) (string, error)

NamedPattern will parse 's' as a path pattern while returning it as well.

func (Reverser) Reverse

func (r Reverser) Reverse(name string, vals ...string) (string, error)

Reverse reverses the named pattern into a url.

type ServeMux

type ServeMux[C Context] struct {
	// contains filtered or unexported fields
}

func NewCustomServeMux added in v0.3.0

func NewCustomServeMux[C Context](
	contextInit ContextInitFunc[C],
	bufLimit int,
	logger Logger,
	baseMux *http.ServeMux,
	reverser *Reverser,
) *ServeMux[C]

NewCustomServeMux creates a mux that allows full customization of the context type and how it is initialized for each handler from the request.

Example
package main

import (
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/advdv/bhttp"
)

// AppContext demonstrates a typed context for request-scoped data.
type AppContext struct {
	context.Context
	RequestID string
}

func main() {
	// Context initializer extracts request-scoped data
	initContext := func(r *http.Request) (AppContext, error) {
		return AppContext{
			Context:   r.Context(),
			RequestID: r.Header.Get("X-Request-ID"),
		}, nil
	}

	mux := bhttp.NewCustomServeMux(
		initContext,
		-1, // no buffer limit
		bhttp.NewTestLogger(nil),
		http.NewServeMux(),
		bhttp.NewReverser(),
	)

	mux.HandleFunc("GET /info", func(ctx AppContext, w bhttp.ResponseWriter, r *http.Request) error {
		fmt.Fprintf(w, "Request ID: %s", ctx.RequestID)
		return nil
	})

	rec := httptest.NewRecorder()
	req := httptest.NewRequest(http.MethodGet, "/info", nil)
	req.Header.Set("X-Request-ID", "abc-123")
	mux.ServeHTTP(rec, req)

	fmt.Println(rec.Body.String())
}
Output:

Request ID: abc-123

func NewServeMux

func NewServeMux() *ServeMux[context.Context]

NewServeMux creates a http.Handler implementation that is akin to the http.ServeMux but allows named routes and buffered responses. It does no use a custom (typed) context, see NewCustomServeMux for that.

func (*ServeMux[C]) Handle

func (m *ServeMux[C]) Handle(pattern string, handler Handler[C], name ...string)

Handle handles the request given a handler.

func (*ServeMux[C]) HandleFunc

func (m *ServeMux[C]) HandleFunc(pattern string, handler HandlerFunc[C], name ...string)

HandleFunc handles the request given the pattern using a function.

func (*ServeMux[C]) Reverse

func (m *ServeMux[C]) Reverse(name string, vals ...string) (string, error)

Reverse returns the url based on the name and parameter values.

Example
package main

import (
	"context"
	"fmt"
	"net/http"

	"github.com/advdv/bhttp"
)

func main() {
	mux := bhttp.NewServeMux()

	mux.HandleFunc("GET /users/{id}", func(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
		return nil
	}, "get-user")

	mux.HandleFunc("GET /users/{userId}/posts/{postId}", func(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
		return nil
	}, "get-user-post")

	url1, _ := mux.Reverse("get-user", "42")
	url2, _ := mux.Reverse("get-user-post", "42", "101")

	fmt.Println(url1)
	fmt.Println(url2)
}
Output:

/users/42
/users/42/posts/101

func (*ServeMux[C]) ServeHTTP

func (m *ServeMux[C]) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP makes the server mux implement the http.Handler interface.

func (*ServeMux[C]) Use

func (m *ServeMux[C]) Use(mw ...Middleware)

Use allows providing of middleware.

Example
package main

import (
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/advdv/bhttp"
)

func main() {
	mux := bhttp.NewServeMux()

	// Add request ID middleware
	mux.Use(func(next bhttp.BareHandler) bhttp.BareHandler {
		return bhttp.BareHandlerFunc(func(w bhttp.ResponseWriter, r *http.Request) error {
			// Set header before calling next handler
			w.Header().Set("X-Request-ID", "req-123")
			return next.ServeBareBHTTP(w, r)
		})
	})

	mux.HandleFunc("GET /ping", func(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
		fmt.Fprint(w, "pong")
		return nil
	})

	rec := httptest.NewRecorder()
	req := httptest.NewRequest(http.MethodGet, "/ping", nil)
	mux.ServeHTTP(rec, req)

	fmt.Println("Body:", rec.Body.String())
	fmt.Println("Request ID:", rec.Header().Get("X-Request-ID"))
}
Output:

Body: pong
Request ID: req-123

type TestLogger added in v0.3.0

type TestLogger struct {
	NumLogUnhandledServeError int64
	NumLogImplicitFlushError  int64
	// contains filtered or unexported fields
}

func NewTestLogger added in v0.3.0

func NewTestLogger(tb testing.TB) *TestLogger

func (*TestLogger) LogImplicitFlushError added in v0.3.0

func (l *TestLogger) LogImplicitFlushError(err error)

func (*TestLogger) LogUnhandledServeError added in v0.3.0

func (l *TestLogger) LogUnhandledServeError(err error)

Directories

Path Synopsis
Package blwa provides a batteries-included framework for building HTTP services on AWS Lambda Web Adapter (LWA).
Package blwa provides a batteries-included framework for building HTTP services on AWS Lambda Web Adapter (LWA).
internal

Jump to

Keyboard shortcuts

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