bhttp

package module
v0.7.1 Latest Latest
Warning

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

Go to latest
Published: Feb 7, 2026 License: MIT Imports: 13 Imported by: 3

README

bhttp

Go Reference Go Report Card

Buffered HTTP handlers with error returns, named routes, and prefix mounting 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, and net/http.ServeMux has no concept of mounting handlers under a prefix or generating URLs from named routes.

bhttp builds directly on net/http (including Go 1.22+ routing patterns) rather than replacing it. It adds error-returning handlers with context as the first argument, named routes with URL reversing, and prefix-based mounting for composing handler trees. 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:

  • Error-returning handlers for clean error propagation
  • Context-first handlers with context as the first argument
  • Named routes with URL generation
  • Prefix mounting for composing handlers under a sub-path
  • Buffered responses that allow complete response rewriting on errors

Installation

go get github.com/advdv/bhttp

Quick Start

package main

import (
    "context"
    "encoding/json"
    "errors"
    "fmt"
    "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")

    // Mount a sub-handler under /api; it sees paths with the prefix stripped
    mux.MountFunc("/api", func(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
        fmt.Fprintf(w, "api path: %s", r.URL.Path)
        return nil
    })

    // 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)
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"
Mounting

Mount handlers under a prefix. The mounted handler sees paths with the prefix stripped:

mux.MountFunc("/api", func(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
    // Request to /api/users arrives here with r.URL.Path == "/users"
    fmt.Fprintf(w, "api: %s", r.URL.Path)
    return nil
})

// Mount a standard library http.Handler (e.g. file server, pprof)
mux.MountStd("/debug", http.DefaultServeMux)

Middleware registered via Use() sees the original path; the prefix is stripped after middleware runs. Standard library handlers mounted via MountStd manage their own error responses—see the Go documentation for details.

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 context-first, error-returning handlers, named routes, and prefix mounting.

Overview

bhttp extends the standard library's HTTP handling with four key features: error-returning handlers for clean error propagation, context as the first handler argument, named routes with URL reversing, and prefix-based mounting for composing handler trees. The buffered response writer is a necessary consequence—it allows the framework to discard partial output and generate clean error responses when handlers fail.

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)
    }
    return json.NewEncoder(w).Encode(item)
}, "get-item")

Handler Signature

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

  • Context is the first argument (not extracted from 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 context.Context, w bhttp.ResponseWriter, r *http.Request) error

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:

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.

Mounting

Handlers can be mounted under a prefix using ServeMux.Mount, ServeMux.MountFunc, ServeMux.MountBare, or ServeMux.MountStd. The mounted handler receives requests with the mount prefix stripped from the path. Middleware registered via ServeMux.Use sees the original path; the strip happens after middleware runs.

mux.MountFunc("/api", func(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
    // Request to /api/users arrives with r.URL.Path == "/users"
    fmt.Fprintf(w, "api: %s", r.URL.Path)
    return nil
})

// Mount a standard library http.Handler
mux.MountStd("/debug", http.DefaultServeMux)

Mount registers both the exact prefix and the subtree (e.g. /api and /api/) on the underlying http.ServeMux.

ServeMux

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

Standard library handlers and error ownership

Methods ending in Std (ServeMux.HandleStd, ServeMux.MountStd) accept a standard library http.Handler instead of bhttp's error-returning Handler. Because http.Handler.ServeHTTP has no error return value, these handlers are fully responsible for writing their own error responses (status codes, headers, and body). bhttp's error-to-status-code mapping (as performed by ToStd) is not applied: whatever the handler writes is what the client receives.

Middleware registered via ServeMux.Use still runs before the handler and the response is still buffered through ResponseWriter, so middleware can inspect or [ResponseWriter.Reset] the response before the buffer is flushed. The handler's writes are otherwise passed through unchanged.

This makes the Std variants well suited for integrating third-party or stdlib handlers (e.g. http.FileServer, pprof, Prometheus metrics) that already manage their own HTTP responses.

Converting to Standard Library

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

handler := bhttp.HandlerFunc(myHandler)
bare := bhttp.ToBare(handler)
stdHandler := bhttp.ToStd(bare, bufferLimit, logger)

The conversion chain is:

Handler → BareHandler → http.Handler

ToBare extracts the context from the request, ToStd wraps with buffering and error handling.

Example
package main

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

	"github.com/advdv/bhttp"
	"github.com/cockroachdb/errors"
)

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 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 serves 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(h Handler) BareHandler

ToBare converts a handler into a bare buffered handler.

func Wrap added in v0.3.0

func Wrap(h Handler, 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 allows casting a function to an implementation of BareHandler.

func (BareHandlerFunc) ServeBareBHTTP added in v0.3.0

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

ServeBareBHTTP implements the BareHandler 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 (
	"fmt"

	"github.com/advdv/bhttp"
	"github.com/cockroachdb/errors"
)

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 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"
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/advdv/bhttp"
	"github.com/cockroachdb/errors"
)

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 interface {
	ServeBHTTP(ctx context.Context, w ResponseWriter, r *http.Request) error
}

Handler mirrors http.Handler but with a buffered response and error return.

type HandlerFunc

type HandlerFunc func(context.Context, ResponseWriter, *http.Request) error

HandlerFunc allows casting a function to implement Handler.

func (HandlerFunc) ServeBHTTP

func (f HandlerFunc) ServeBHTTP(ctx context.Context, 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 struct {
	// contains filtered or unexported fields
}

ServeMux is an HTTP multiplexer with buffered responses, error handling, and named routes.

func NewServeMux

func NewServeMux() *ServeMux

NewServeMux creates a new ServeMux with default settings.

func NewServeMuxWith added in v0.6.0

func NewServeMuxWith(bufLimit int, logger Logger, baseMux *http.ServeMux, reverser *Reverser) *ServeMux

NewServeMuxWith creates a ServeMux with custom settings.

Example
package main

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

	"github.com/advdv/bhttp"
)

func main() {
	mux := bhttp.NewServeMuxWith(
		-1, // no buffer limit
		bhttp.NewTestLogger(nil),
		http.NewServeMux(),
		bhttp.NewReverser(),
	)

	mux.HandleFunc("GET /info", func(_ context.Context, w bhttp.ResponseWriter, r *http.Request) error {
		reqID := r.Header.Get("X-Request-ID")
		fmt.Fprintf(w, "Request ID: %s", reqID)
		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 (*ServeMux) Handle

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

Handle handles the request given a handler.

func (*ServeMux) HandleFunc

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

HandleFunc handles the request given the pattern using a function.

func (*ServeMux) HandleStd added in v0.7.0

func (m *ServeMux) HandleStd(pattern string, handler http.Handler, name ...string)

HandleStd registers a standard library http.Handler for the given pattern. Middleware registered via ServeMux.Use is applied. See the package-level section "Standard library handlers and error ownership" for details on error handling behavior.

func (*ServeMux) Mount added in v0.7.0

func (m *ServeMux) Mount(pattern string, handler Handler)

Mount mounts a Handler on a sub-path pattern. The mounted handler receives requests with the mount prefix stripped from the path.

func (*ServeMux) MountBare added in v0.7.0

func (m *ServeMux) MountBare(pattern string, handler BareHandler)

MountBare mounts a BareHandler on a sub-path pattern. The mounted handler receives requests with the mount prefix stripped from the path. Middleware registered via Use() sees the original path; the strip happens after middleware.

func (*ServeMux) MountFunc added in v0.7.0

func (m *ServeMux) MountFunc(pattern string, handler HandlerFunc)

MountFunc mounts a HandlerFunc on a sub-path pattern. The mounted handler receives requests with the mount prefix stripped from the path.

func (*ServeMux) MountStd added in v0.7.0

func (m *ServeMux) MountStd(pattern string, handler http.Handler)

MountStd mounts a standard library http.Handler on a sub-path pattern. The mounted handler receives requests with the mount prefix stripped from the path. Middleware registered via ServeMux.Use is applied and sees the original path. See the package-level section "Standard library handlers and error ownership" for details on error handling behavior.

func (*ServeMux) Reverse

func (m *ServeMux) 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) ServeHTTP

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

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

func (*ServeMux) Use

func (m *ServeMux) 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).
blwatest
Package blwatest provides test helpers for blwa applications.
Package blwatest provides test helpers for blwa applications.
internal

Jump to

Keyboard shortcuts

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