httpserver

package
v0.0.7 Latest Latest
Warning

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

Go to latest
Published: Oct 5, 2024 License: BSD-3-Clause Imports: 16 Imported by: 0

Documentation

Overview

Package httpserver extends the functionality of the standard library http.Server.

Key Features:

  • Manage routing paths, middleware registration, and handler registrations of the standard library http.Server.
  • Some built-in middleware.

ServeMux

ServeMux extends http.Handler designed to manage routing paths, middleware registration, and handler registrations of the standard library http.Server. It serves as a versatile routing mechanism that can handle middleware and nested routers efficiently.

Methods for adding middleware:

Methods for managing the routing path:

Methods for registering an http handler:

Middlewares

  • MiddlewareLogging: Logs each incoming request along with useful metadata regarding the request.
  • MiddlewareRecover: Recovers from panics, logs the panic, and responds with an HTTP status of 500 (Internal Server Error).
  • MiddlewareTelemetryTag: Adds attributes to spans and metrics for telemetry purposes.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func MiddlewareLogging added in v0.0.6

func MiddlewareLogging(next http.Handler) http.Handler

MiddlewareLogging is a middleware that logs each incoming request along with useful metadata regarding the request.

Response Status Handling:

  • Error: For response status < 100 and >= 500
  • Warn: For response status < 200 and >= 400
  • Info: Other response status

Log Identifier Handling:

  • If the X-Logger-ID header is present in the request, its value will be used as the log identifier.
  • If the header is not present or if the value is invalid, a new log identifier will be generated using UUID v7.
  • The log identifier is then added to the context logger.ContextLogID.

Log Level Handling:

  • If the X-Logger-Level header is present in the request, its value will be used as the minimum log level. Allowing lower priority logs at runtime.
  • The minimum log level is then added to the context logger.ContextMinLevel.

Important Note:

  • MiddlewareLogging should be positioned before any other middleware that may alter the response, such as MiddlewareRecover.
  • Must be used with logger.NewHandler to register the log handle and allow lower priority logging at runtime.

Example:

mux := httpserver.NewServeMux()
mux.Use(httpserver.MiddlewareLogging)   // <--<< MiddlewareLogging should come before MiddlewareRecover
mux.Use(httpserver.MiddlewareRecover)
mux.Get("/", handler)
Example
package main

import (
	"fmt"
	"log"
	"log/slog"
	"net/http"

	"github.com/telmoandrade/go-library/httpserver"
	"github.com/telmoandrade/go-library/logger"
	"go.opentelemetry.io/contrib/bridges/otelslog"
	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)

func handlerHello(w http.ResponseWriter, r *http.Request) {
	if _, err := w.Write([]byte("hello")); err != nil {
		log.Printf("Write failed: %v\n", err)
	}
}

func main() {
	slog.SetDefault(logger.NewLogger(
		logger.WithMinLevel(slog.LevelInfo),
		logger.WithHandler(otelslog.NewHandler("")),
	))

	mux := httpserver.NewServeMux()
	mux.Use(
		httpserver.MiddlewareLogging,
		httpserver.MiddlewareRecover,
		otelhttp.NewMiddleware(""),
		httpserver.MiddlewareTelemetryTag,
	)
	mux.Get("/hello", handlerHello)

	s := &http.Server{
		Addr:    "0.0.0.0:8080",
		Handler: mux,
	}
	fmt.Print(s != nil)
}
Output:

true

func MiddlewareRecover added in v0.0.6

func MiddlewareRecover(next http.Handler) http.Handler

MiddlewareRecover is a middleware that recovers from panics, logs the panic, and responds with an HTTP status of 500 (Internal Server Error).

Example
package main

import (
	"fmt"
	"log"
	"log/slog"
	"net/http"

	"github.com/telmoandrade/go-library/httpserver"
	"github.com/telmoandrade/go-library/logger"
	"go.opentelemetry.io/contrib/bridges/otelslog"
	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)

func handlerHello(w http.ResponseWriter, r *http.Request) {
	if _, err := w.Write([]byte("hello")); err != nil {
		log.Printf("Write failed: %v\n", err)
	}
}

func main() {
	slog.SetDefault(logger.NewLogger(
		logger.WithMinLevel(slog.LevelInfo),
		logger.WithHandler(otelslog.NewHandler("")),
	))

	mux := httpserver.NewServeMux()
	mux.Use(
		httpserver.MiddlewareLogging,
		httpserver.MiddlewareRecover,
		otelhttp.NewMiddleware(""),
		httpserver.MiddlewareTelemetryTag,
	)
	mux.Get("/hello", handlerHello)

	s := &http.Server{
		Addr:    "0.0.0.0:8080",
		Handler: mux,
	}
	fmt.Print(s != nil)
}
Output:

true

func MiddlewareTelemetryTag added in v0.0.6

func MiddlewareTelemetryTag(next http.Handler) http.Handler

MiddlewareTelemetryTag is a middleware that adds attributes to spans and metrics for telemetry purposes.

Adds telemetry attributes for monitoring:

  • http.route: Indicates the route of the HTTP request used in spans and metrics.
  • log.id: Log identifier associated with the request used in spans.
Example
package main

import (
	"fmt"
	"log"
	"log/slog"
	"net/http"

	"github.com/telmoandrade/go-library/httpserver"
	"github.com/telmoandrade/go-library/logger"
	"go.opentelemetry.io/contrib/bridges/otelslog"
	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)

func handlerHello(w http.ResponseWriter, r *http.Request) {
	if _, err := w.Write([]byte("hello")); err != nil {
		log.Printf("Write failed: %v\n", err)
	}
}

func main() {
	slog.SetDefault(logger.NewLogger(
		logger.WithMinLevel(slog.LevelInfo),
		logger.WithHandler(otelslog.NewHandler("")),
	))

	mux := httpserver.NewServeMux()
	mux.Use(
		httpserver.MiddlewareLogging,
		httpserver.MiddlewareRecover,
		otelhttp.NewMiddleware(""),
		httpserver.MiddlewareTelemetryTag,
	)
	mux.Get("/hello", handlerHello)

	s := &http.Server{
		Addr:    "0.0.0.0:8080",
		Handler: mux,
	}
	fmt.Print(s != nil)
}
Output:

true

Types

type Handle

type Handle interface {
	// Connect registers a handler for the HTTP CONNECT method, under the current routing path plus the specified pattern.
	Connect(pattern string, handlerFn http.HandlerFunc)
	// Delete registers a handler for the HTTP DELETE method, under the current routing path plus the specified pattern.
	Delete(pattern string, handlerFn http.HandlerFunc)
	// Get registers a handler for the HTTP GET method, under the current routing path plus the specified pattern.
	Get(pattern string, handlerFn http.HandlerFunc)
	// Head registers a handler for the HTTP HEAD method, under the current routing path plus the specified pattern.
	Head(pattern string, handlerFn http.HandlerFunc)
	// Options registers a handler for the HTTP OPTIONS method, under the current routing path plus the specified pattern.
	Options(pattern string, handlerFn http.HandlerFunc)
	// Patch registers a handler for the HTTP PATCH method, under the current routing path plus the specified pattern.
	Patch(pattern string, handlerFn http.HandlerFunc)
	// Post registers a handler for the HTTP POST method, under the current routing path plus the specified pattern.
	Post(pattern string, handlerFn http.HandlerFunc)
	// Put registers a handler for the HTTP PUT method, under the current routing path plus the specified pattern.
	Put(pattern string, handlerFn http.HandlerFunc)
	// Trace registers a handler for the HTTP TRACE method, under the current routing path plus the specified pattern.
	Trace(pattern string, handlerFn http.HandlerFunc)
	// Method registers a handler for the custom HTTP method, under the current routing path plus the specified pattern.
	Method(method, pattern string, handlerFn http.HandlerFunc)
}

Handle interface allows the registration of HTTP handlers under the current routing path plus the specified pattern. It includes methods for each standard HTTP method.

type Router

type Router interface {
	Handle

	// Use appends one or more middlewares to the middleware stack for the Router in the current routing path.
	Use(middlewares ...func(http.Handler) http.Handler)
	// With appends one or more middlewares to the middleware stack in the current routing path to register the inline Handle.
	With(middlewares ...func(http.Handler) http.Handler) Handle
	// Group return a new inline Router under the current routing path plus the specified pattern, inheriting the middleware stack.
	Group(pattern string) Router
	// Route allowing additional routes to be defined within the subrouter under the current routing path plus the specified pattern, inheriting the middleware stack.
	Route(pattern string, fn func(sub Router))
	// Mount attaches another http.Handler under the current routing path plus the specified pattern, useful for integrating external routers or handlers.
	Mount(pattern string, h http.Handler)
}

Router interface extends the Handle interface. It is designed to manage routing paths, middleware registration, and handler registrations.

type ServeMux

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

ServeMux extends http.Handler designed to manage routing paths, middleware registration, and handler registrations of the standard library http.Server. It serves as a versatile routing mechanism that can handle middleware and nested routers efficiently.

Behavior:

  • The ServeMux stores the routing path to register middlewares and handlers.
  • If a pattern using the host conflicts with one that is already registered, it will cause a panic.

func NewServeMux

func NewServeMux() *ServeMux

NewServeMux creates and returns a new instance of ServeMux with enhanced routing and middleware capabilities.

Example
package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/telmoandrade/go-library/httpserver"
)

func handlerHello(w http.ResponseWriter, r *http.Request) {
	if _, err := w.Write([]byte("hello")); err != nil {
		log.Printf("Write failed: %v\n", err)
	}
}

func main() {
	mux := httpserver.NewServeMux()
	mux.Get("/hello", handlerHello)

	s := &http.Server{
		Addr:    "0.0.0.0:8080",
		Handler: mux,
	}
	fmt.Print(s != nil)
}
Output:

true

func (*ServeMux) Connect

func (mux *ServeMux) Connect(pattern string, handlerFn http.HandlerFunc)

Connect registers a handler for the HTTP CONNECT method, under the current routing path plus the specified pattern.

Example
package main

import (
	"net/http"

	"github.com/telmoandrade/go-library/httpserver"
)

func handler(w http.ResponseWriter, r *http.Request) {}

func main() {
	mux := httpserver.NewServeMux()
	mux.Connect("/pattern", handler)
}

func (*ServeMux) Delete

func (mux *ServeMux) Delete(pattern string, handlerFn http.HandlerFunc)

Delete registers a handler for the HTTP DELETE method, under the current routing path plus the specified pattern.

Example
package main

import (
	"net/http"

	"github.com/telmoandrade/go-library/httpserver"
)

func handler(w http.ResponseWriter, r *http.Request) {}

func main() {
	mux := httpserver.NewServeMux()
	mux.Delete("/pattern", handler)
}

func (*ServeMux) Get

func (mux *ServeMux) Get(pattern string, handlerFn http.HandlerFunc)

Get registers a handler for the HTTP GET method, under the current routing path plus the specified pattern.

Example
package main

import (
	"net/http"

	"github.com/telmoandrade/go-library/httpserver"
)

func handler(w http.ResponseWriter, r *http.Request) {}

func main() {
	mux := httpserver.NewServeMux()
	mux.Get("/pattern", handler)
}

func (*ServeMux) Group

func (mux *ServeMux) Group(pattern string) Router

Group return a new inline Router under the current routing path plus the specified pattern, inheriting the middleware stack.

Example
package main

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

	"github.com/telmoandrade/go-library/httpserver"
)

type ctxKey struct {
	name string
}

func middlewarePathValue(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		id := r.PathValue("id")
		ctx := context.WithValue(r.Context(), ctxKey{"id"}, id)
		req := r.WithContext(ctx)
		next.ServeHTTP(w, req)
	})
}

func handlerGetUser(w http.ResponseWriter, r *http.Request) {
	id := r.Context().Value(ctxKey{"id"}).(string)

	fmt.Println(id)
}

func handlerPutUser(w http.ResponseWriter, r *http.Request) {
	id := r.Context().Value(ctxKey{"id"}).(string)

	fmt.Println(id)
}

func main() {
	mux := httpserver.NewServeMux()
	muxUser := mux.Group("/user")

	muxUser.Use(middlewarePathValue)
	muxUser.Get("/{id}", handlerGetUser)
	muxUser.Put("/{id}", handlerPutUser)

	s := &http.Server{
		Addr:    "0.0.0.0:8080",
		Handler: mux,
	}
	fmt.Print(s != nil)
}
Output:

true

func (*ServeMux) Head

func (mux *ServeMux) Head(pattern string, handlerFn http.HandlerFunc)

Head registers a handler for the HTTP HEAD method, under the current routing path plus the specified pattern.

Example
package main

import (
	"net/http"

	"github.com/telmoandrade/go-library/httpserver"
)

func handler(w http.ResponseWriter, r *http.Request) {}

func main() {
	mux := httpserver.NewServeMux()
	mux.Head("/pattern", handler)
}

func (*ServeMux) Method

func (mux *ServeMux) Method(method, pattern string, handlerFn http.HandlerFunc)

Method registers a handler for the custom HTTP method, under the current routing path plus the specified pattern.

Example
package main

import (
	"net/http"

	"github.com/telmoandrade/go-library/httpserver"
)

func handler(w http.ResponseWriter, r *http.Request) {}

func main() {
	mux := httpserver.NewServeMux()
	mux.Method("CUSTOM", "/pattern", handler)
}

func (*ServeMux) Mount

func (mux *ServeMux) Mount(pattern string, handler http.Handler)

Mount attaches another http.Handler under the current routing path plus the specified pattern, useful for integrating external routers or handlers.

Important Note:

  • Avoid using this method to attach another multiplexer as it does not inherit the middleware stack.
  • It is slower to resolve the multiplexer for HTTP requests compared to using the built-in routing methods.
Example
package main

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

	"github.com/telmoandrade/go-library/httpserver"
)

type ctxKey struct {
	name string
}

func middlewarePathValue(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		id := r.PathValue("id")
		ctx := context.WithValue(r.Context(), ctxKey{"id"}, id)
		req := r.WithContext(ctx)
		next.ServeHTTP(w, req)
	})
}

func handlerGetUser(w http.ResponseWriter, r *http.Request) {
	id := r.Context().Value(ctxKey{"id"}).(string)

	fmt.Println(id)
}

func handlerPutUser(w http.ResponseWriter, r *http.Request) {
	id := r.Context().Value(ctxKey{"id"}).(string)

	fmt.Println(id)
}

func main() {
	muxUser := httpserver.NewServeMux()
	muxUser.Use(middlewarePathValue)
	muxUser.Get("/{id}", handlerGetUser)
	muxUser.Put("/{id}", handlerPutUser)

	mux := httpserver.NewServeMux()
	mux.Mount("/user", muxUser)

	s := &http.Server{
		Addr:    "0.0.0.0:8080",
		Handler: mux,
	}
	fmt.Print(s != nil)
}
Output:

true

func (*ServeMux) Options

func (mux *ServeMux) Options(pattern string, handlerFn http.HandlerFunc)

Options registers a handler for the HTTP OPTIONS method, under the current routing path plus the specified pattern.

Example
package main

import (
	"net/http"

	"github.com/telmoandrade/go-library/httpserver"
)

func handler(w http.ResponseWriter, r *http.Request) {}

func main() {
	mux := httpserver.NewServeMux()
	mux.Options("/pattern", handler)
}

func (*ServeMux) Patch

func (mux *ServeMux) Patch(pattern string, handlerFn http.HandlerFunc)

Patch registers a handler for the HTTP PATCH method, under the current routing path plus the specified pattern.

Example
package main

import (
	"net/http"

	"github.com/telmoandrade/go-library/httpserver"
)

func handler(w http.ResponseWriter, r *http.Request) {}

func main() {
	mux := httpserver.NewServeMux()
	mux.Patch("/pattern", handler)
}

func (*ServeMux) Post

func (mux *ServeMux) Post(pattern string, handlerFn http.HandlerFunc)

Post registers a handler for the HTTP POST method, under the current routing path plus the specified pattern.

Example
package main

import (
	"net/http"

	"github.com/telmoandrade/go-library/httpserver"
)

func handler(w http.ResponseWriter, r *http.Request) {}

func main() {
	mux := httpserver.NewServeMux()
	mux.Post("/pattern", handler)
}

func (*ServeMux) Put

func (mux *ServeMux) Put(pattern string, handlerFn http.HandlerFunc)

Put registers a handler for the HTTP PUT method, under the current routing path plus the specified pattern.

Example
package main

import (
	"net/http"

	"github.com/telmoandrade/go-library/httpserver"
)

func handler(w http.ResponseWriter, r *http.Request) {}

func main() {
	mux := httpserver.NewServeMux()
	mux.Put("/pattern", handler)
}

func (*ServeMux) Route

func (mux *ServeMux) Route(pattern string, fn func(sub Router))

Route allowing additional routes to be defined within the sub-Router under the current routing path plus the specified pattern, inheriting the middleware stack.

Example
package main

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

	"github.com/telmoandrade/go-library/httpserver"
)

type ctxKey struct {
	name string
}

func middlewarePathValue(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		id := r.PathValue("id")
		ctx := context.WithValue(r.Context(), ctxKey{"id"}, id)
		req := r.WithContext(ctx)
		next.ServeHTTP(w, req)
	})
}

func handlerGetUser(w http.ResponseWriter, r *http.Request) {
	id := r.Context().Value(ctxKey{"id"}).(string)

	fmt.Println(id)
}

func handlerPutUser(w http.ResponseWriter, r *http.Request) {
	id := r.Context().Value(ctxKey{"id"}).(string)

	fmt.Println(id)
}

func main() {
	mux := httpserver.NewServeMux()
	mux.Route("/user", func(muxUser httpserver.Router) {
		muxUser.Use(middlewarePathValue)
		muxUser.Get("/{id}", handlerGetUser)
		muxUser.Put("/{id}", handlerPutUser)
	})

	s := &http.Server{
		Addr:    "0.0.0.0:8080",
		Handler: mux,
	}
	fmt.Print(s != nil)
}
Output:

true

func (*ServeMux) ServeHTTP added in v0.0.6

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

ServeHTTP dispatches the request to the handler whose pattern most closely matches the request URL.

func (*ServeMux) Trace

func (mux *ServeMux) Trace(pattern string, handlerFn http.HandlerFunc)

Trace registers a handler for the HTTP TRACE method, under the current routing path plus the specified pattern.

Example
package main

import (
	"net/http"

	"github.com/telmoandrade/go-library/httpserver"
)

func handler(w http.ResponseWriter, r *http.Request) {}

func main() {
	mux := httpserver.NewServeMux()
	mux.Trace("/pattern", handler)
}

func (*ServeMux) Use

func (mux *ServeMux) Use(middlewares ...func(http.Handler) http.Handler)

Use appends one or more middlewares to the middleware stack for the Router in the current routing path.

Example
package main

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

	"github.com/telmoandrade/go-library/httpserver"
)

type ctxKey struct {
	name string
}

func middlewarePathValue(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		id := r.PathValue("id")
		ctx := context.WithValue(r.Context(), ctxKey{"id"}, id)
		req := r.WithContext(ctx)
		next.ServeHTTP(w, req)
	})
}

func handlerGetUser(w http.ResponseWriter, r *http.Request) {
	id := r.Context().Value(ctxKey{"id"}).(string)

	fmt.Println(id)
}

func handlerPutUser(w http.ResponseWriter, r *http.Request) {
	id := r.Context().Value(ctxKey{"id"}).(string)

	fmt.Println(id)
}

func main() {
	mux := httpserver.NewServeMux()
	mux.Use(middlewarePathValue)
	mux.Get("/user/{id}", handlerGetUser)
	mux.Put("/user/{id}", handlerPutUser)
}

func (*ServeMux) With

func (mux *ServeMux) With(middlewares ...func(http.Handler) http.Handler) Handle

With appends one or more middlewares to the middleware stack in the current routing path to register the inline Handle.

Example
package main

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

	"github.com/telmoandrade/go-library/httpserver"
)

type ctxKey struct {
	name string
}

func middlewarePathValue(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		id := r.PathValue("id")
		ctx := context.WithValue(r.Context(), ctxKey{"id"}, id)
		req := r.WithContext(ctx)
		next.ServeHTTP(w, req)
	})
}

func handlerGetUser(w http.ResponseWriter, r *http.Request) {
	id := r.Context().Value(ctxKey{"id"}).(string)

	fmt.Println(id)
}

func main() {
	mux := httpserver.NewServeMux()
	mux.With(middlewarePathValue).Get("/user/{id}", handlerGetUser)
}

Jump to

Keyboard shortcuts

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