thttp

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Jan 13, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Overview

Package thttp contains HTTP server and client utilities.

HTTP Server

Most use cases for http.Server are covered by thttp.Server with the following advantages:

* Instead of pre-context-era start-and-stop paradigm, thttp.Server is controlled with a context passed to its Run method. This fits much better into hierarchies of internal components that need to be started and shut down as a whole. Plays especially nice with parallel.Run.

* The server code ensures that every incoming request has a context inherited from the context passed to Run, thus supporting the global expectation that every context contains a logger.

* The somewhat tricky graceful shutdown sequence is taken care of by thttp.Server.

Note that only a single handler is passed to thttp.NewServer as its second argument. Most use cases will need path-based routing. The standard solution is to use github.com/gorilla/mux as in the example below.

Example

thttp.Server fits best into components that themselves are context-controlled. The following example demonstrates the use of the parallel library to run both an HTTP server and a Limestone instance. When the parent context is closed, RunDataServer will return after graceful shutdown of both sub-components.

func RunDataServer(ctx context.Context, addr string) error {
    return parallel.Run(ctx, func(ctx context.Context, spawn parallel.SpawnFn) error {
        db := limestone.New(...)
        spawn("limestone", parallel.Fail, db.Run)

        // Creating the listener early allows incoming connections to be queued
        // while Limestone is catching up
        listener, err := tnet.Listen(addr)
        if err != nil {
            return errors.Wrap(err, "failed to run data server")
        }

        err := db.WaitReady(ctx)
        if err != nil {
            return errors.Wrap(err, "failed to run data server")
        }

        router := mux.NewRouter()
        router.HandleFunc("/data/{id}", getHandler(db)).Methods(http.MethodGet)
        router.HandleFunc("/data/{id}", putHandler(db)).Methods(http.MethodPut)

        server := thttp.NewServer(listener, router,
            thttp.Middleware(thttp.StandardMiddleware, thttp.LogBodies))
        spawn("http", parallel.Fail, server.Run)

        return nil
    })
}

Middleware

A middleware is a function that takes an http.Handler and returns an http.Handler, usually wrapping the handler with code that runs before, after or even instead of the one being wrapped.

One can use a middleware to wrap a handler manually:

handler = thttp.CORS(handler)

A middleware can also be applied to the mux router or a sub-router:

router.Use(thttp.CORS)

However, to apply a handler to all requests handled by the server, which is the most common use case, it's convenient to pass the thttp.Middleware option to thttp.NewServer. The option takes any number of middleware, which are applied in order so that the first one listed is the first to see the incoming request.

server := thttp.NewServer(listener, handler,
    thttp.Middleware(thttp.StandardMiddleware, thttp.LogBodies))

It is recommended to include at least thttp.StandardMiddleware, and put it first. This single middleware is equivalent to listing thttp.Log, thttp.Recover and thttp.CORS, in this order. It does not include LogBodies because its use is less universal.

Request context

In an HTTP handler, r.Context() returns the request context. It is a descendant of the context passed into the Run method of thttp.Server, and contains all the values stored there. However, during shutdown it will stay open for somewhat longer than the parent context to allow current running requests to complete.

Logging guidelines

For all logging in HTTP handlers, use the logger embedded in the request context:

logger := tlog.Get(r.Context())

This logger contains the following structured fields:

* httpServer: the local listening address (to distinguish between messages from several HTTP servers)

* remoteAddr: the IP address and port of the remote client

If the thttp.Log middleware is installed, which it should be (usually via thttp.StandardMiddleware), this logger will also contain the following fields:

* requestID: a unique ID generated for the request to tie the log messages together

* method, host, url: self-explanatory

Please avoid redundant logging. In particular:

* Don't log any of the information above explicitly.

* Don't include generic unconditional log messages at the beginning and end of your handler unless they contain informative fields. The thttp.Log middleware already logs before and after handling of each request.

* If you need to log all request and response bodies (at Debug level), use the thttp.LogBodies middleware.

* In case of an internal error, don't log it explicitly. Just panic, and the thttp.Recover middleware will log the complete error with the panic stack. The client will receive a generic error 500 (unless the headers have already been sent), without the details of the error being exposed.

Index

Constants

This section is empty.

Variables

View Source
var CORS = handlers.CORS(
	handlers.AllowedMethods(allowedMethods),
	handlers.AllowedHeaders(allowedHeaders),
	handlers.ExposedHeaders(exposedHeaders),
	handlers.AllowedOrigins([]string{"*"}),
)

CORS is a middleware that allows cross-origin requests.

View Source
var ErrMissingAuthToken = errors.New("missing authentication token")

ErrMissingAuthToken is an error return by BearerToken if there is no Authorization HTTP header.

View Source
var RetryingDNSClient = &http.Client{
	Transport: &http.Transport{
		DialContext: retryingDialer,
	},
}

RetryingDNSClient is a http.Client that retries in case of DNS "not found" errors.

Functions

func BearerToken

func BearerToken(header http.Header) (string, error)

BearerToken returns a bearer token, or an error if it is not found.

func CaptureStatus

func CaptureStatus(w http.ResponseWriter, status *int) http.ResponseWriter

CaptureStatus wraps a http.ResponseWriter to capture the response status code. The status code will be written into *status.

The returned ResponseWriter works the same way as the original one, including the http.Hijacker functionality, if available.

func Log

func Log(next http.Handler) http.Handler

Log is a middleware that logs before and after handling of each request. Does not include logging of request and response bodies.

Each request is assigned a unique ID which is logged and sent to the client as X-Ridge-RequestID header.

func Origin

func Origin(r *http.Request) (string, error)

Origin returns the origin of HTTP request.

func Recover

func Recover(next http.Handler) http.Handler

Recover is a middleware that catches and logs panics from HTTP handlers.

func StandardMiddleware

func StandardMiddleware(next http.Handler) http.Handler

StandardMiddleware is a composition of typically used middleware, in the recommended order:

1. Log (log before and after the request) 2. Recover (catch and log panic) 3. CORS (allow cross-origin requests).

func Test

func Test(handler http.Handler, r *http.Request) *http.Response

Test processes an http.Request (usually obtained from httptest.NewRequest) with the given handler as if it was received on the network. Only useful in tests.

Does not require a running HTTP server to be running.

func TestCtx

func TestCtx(ctx context.Context, handler http.Handler, r *http.Request) *http.Response

TestCtx is similar to Test, except that the given context is injected into the request.

func WithRequestsLogging

func WithRequestsLogging(ctx context.Context, client *http.Client) *http.Client

WithRequestsLogging returns an http client with logging from the context logger.

Types

type Config

type Config struct {
	// Handler is a request handler
	Handler http.Handler

	// GetCertificate if non-nil turns on TLS and returns certificate for request.
	GetCertificate func(*tls.ClientHelloInfo) (*tls.Certificate, error)
}

Config is a configuration of http service.

type LoggingTransport

type LoggingTransport struct {
	//nolint:containedctx // it's fine
	Context   context.Context
	Transport http.RoundTripper
}

LoggingTransport is HTTP transport with logging to context logger.

func (*LoggingTransport) RoundTrip

func (t *LoggingTransport) RoundTrip(req *http.Request) (*http.Response, error)

RoundTrip is an implementation of RoundTripper

RoundTripper is an interface representing the ability to execute a single HTTP transaction, obtaining the Response for a given Request.

A RoundTripper must be safe for concurrent use by multiple goroutines.

type MalformedAuthHeaderError

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

MalformedAuthHeaderError is an error returned by BearerToken if Authorization HTTP header is not in form "Bearer token".

func (MalformedAuthHeaderError) Error

func (e MalformedAuthHeaderError) Error() string

type Option

type Option func(*http.Server)

An Option is a server configuration mixin.

It is a function that modifies a http.Server before it starts serving requests.

func Middleware

func Middleware(mw ...func(http.Handler) http.Handler) Option

Middleware is an Option that installs top-level middleware on the HTTP server. The first middleware listed will be the first one to see the request.

type Server

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

Server wraps an HTTP server.

func NewServer

func NewServer(listener net.Listener, cfg Config, opt ...Option) *Server

NewServer creates a Server.

func (*Server) ListenAddr

func (s *Server) ListenAddr() net.Addr

ListenAddr returns the local address of the server's listener.

func (*Server) Run

func (s *Server) Run(ctx context.Context) error

Run serves requests until the context is closed, then performs graceful shutdown for up to gracefulShutdownTimeout.

func (*Server) Unlock

func (s *Server) Unlock()

Unlock implements locker.Locker.

Jump to

Keyboard shortcuts

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