oakmux

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Aug 31, 2023 License: MIT Imports: 9 Imported by: 0

README

OakMux: HTTP Router with Generic Adaptors

Inspiration

The core functionality was inspired by Jonathan Amsterdam's alternative ServeMux implementation draft. For full discussion see the proposal here.

Mr. Amsterdam's draft contains unnecessary complexity for detecting route intersections, because it includes HTTP method differentiation inside the routing tree and does not anchor to the end of the request path without an explicit [$] segment.

In this implementation, all request paths are anchored to the end, unless ending with [...], and the routing by method is set aside into a separate MethodMux handler. Therefore, the route intersections are always correctly caught at initialization, when the routing tree grows. This should also lead to slightly faster performance when working with live application routing trees.

Domain Adaptors

Domain logic adaptors come in three general flavors:

  1. UnaryFunc: func(context, inputStruct) (outputStruct, error)
  2. NullaryFunc: func(context) (outputStruct, error)
  3. VoidFunc: func(context, inputStruct) error

Each input requires implementation of adapt.Validatable for safety. Validation errors are decorated with the correct http.StatusUnprocessableEntity status code.

Documentation

Overview

Package oakmux is a tree HTTP router with generic domain adaptors.

Index

Constants

View Source
const (
	SegmentTypeUnknown = iota
	SegmentTypeStatic
	SegmentTypeDynamic
	SegmentTypeTerminal
	SegmentTypeTrailingSlash
)
View Source
const DefaultRequestReadLimitOf1MB = 1 << 20

Variables

View Source
var (
	ErrDoubleSlash        = NewRoutingError(errors.New("path contains double slash"))
	ErrPathNotFound       = NewRoutingError(errors.New("path not found"))
	ErrNotInteger         = NewRoutingError(errors.New("field is not an integer"))
	ErrNotUnsignedInteger = NewRoutingError(errors.New("field is not an unsigned integer"))
	ErrNotPageNumber      = NewRoutingError(errors.New("field is not an page number"))
)

Functions

func Must

func Must[T any](this T, err error) T

Types

type Branches

type Branches interface {
	Get(string) *Node
	Grow(string) (*Node, Branches)
	Keys() []string
}

Branches abstracts either map or list implementation for child [Node]s for performance. When there are more than [optimalMimimumBranchMapSize], the map implementation is prefered for faster look up.

TODO: John Amsterdam's implementation switched to generics instead of hybrid. Would it be faster than asserting the interface? Should not be. At GopherConEU he said that interfaces block compiler's escape analysis. Should this be re-written using generics?

type Error

type Error interface {
	error
	HyperTextStatusCode() int
}
var ErrNoRouteMatched Error = &noRouteMatchedError{}

func NewMethodNotAllowedError

func NewMethodNotAllowedError(method string) Error

type Handler

type Handler interface {
	ServeHyperText(http.ResponseWriter, *http.Request) error
}

func ApplyMiddleware

func ApplyMiddleware(h Handler, middleware ...Middleware) Handler

ApplyMiddleware applies Middleware to a Handler in reverse to preserve the logical order.

func New

func New(withOptions ...Option) (Handler, error)

func NewHostMux

func NewHostMux(withOptions ...HostMuxOption) (Handler, error)

NewHostMux creates a Handler that multiplexes by http.Request host name.

func NewMethodMux

func NewMethodMux(withOptions ...MethodMuxOption) (Handler, error)

func NewPermanentRedirect

func NewPermanentRedirect(to string) Handler

func NewTemporaryRedirect

func NewTemporaryRedirect(to string) Handler

type HandlerFunc

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

func (HandlerFunc) ServeHyperText

func (f HandlerFunc) ServeHyperText(w http.ResponseWriter, r *http.Request) error

type HostMuxOption

type HostMuxOption func(*hostMuxOptions) error

func WithHostHandler

func WithHostHandler(host string, handler Handler) HostMuxOption

type MatchedFields

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

func (*MatchedFields) Int

func (m *MatchedFields) Int(name string, value *int) error

func (*MatchedFields) Int64

func (m *MatchedFields) Int64(name string, value *int64) error

func (*MatchedFields) Page

func (m *MatchedFields) Page(name string, value *int) error

func (*MatchedFields) Str

func (m *MatchedFields) Str(name string, value *string) error

func (*MatchedFields) Uint

func (m *MatchedFields) Uint(name string, value *uint) error

func (*MatchedFields) Uint64

func (m *MatchedFields) Uint64(name string, value *uint64) error

type MethodMuxOption

type MethodMuxOption func(*methodMuxOptions) error

func WithDelete

func WithDelete(h Handler) MethodMuxOption

func WithDeleteCustomFunc

func WithDeleteCustomFunc[T any, V adapt.Validatable[T], O any](
	domainCall func(context.Context, V) (O, error),
	codec adapt.Codec[T, V, O],
	mws ...Middleware,
) MethodMuxOption

func WithDeleteCustomStringFunc

func WithDeleteCustomStringFunc[O any](
	domainCall func(context.Context, string) (O, error),
	extractor func(*http.Request) (string, error),
	encoder adapt.Encoder[O],
	mws ...Middleware,
) MethodMuxOption

func WithDeleteCustomVoidFunc

func WithDeleteCustomVoidFunc[T any, V adapt.Validatable[T]](
	domainCall func(context.Context, V) error,
	codec adapt.Codec[T, V, T],
	mws ...Middleware,
) MethodMuxOption

func WithDeleteFunc

func WithDeleteFunc[T any, V adapt.Validatable[T], O any](
	domainCall func(context.Context, V) (O, error),
	mws ...Middleware,
) MethodMuxOption

func WithDeleteHandler

func WithDeleteHandler(h Handler, mws ...Middleware) MethodMuxOption

func WithDeleteNullaryCustomFunc

func WithDeleteNullaryCustomFunc[O any](
	domainCall func(context.Context) (O, error),
	encoder adapt.Encoder[O],
	mws ...Middleware,
) MethodMuxOption

func WithDeleteNullaryFunc

func WithDeleteNullaryFunc[O any](
	domainCall func(context.Context) (O, error),
	mws ...Middleware,
) MethodMuxOption

func WithDeleteStringFunc

func WithDeleteStringFunc[O any](
	domainCall func(context.Context, string) (O, error),
	extractor func(*http.Request) (string, error),
	mws ...Middleware,
) MethodMuxOption

func WithDeleteStringVoidFunc

func WithDeleteStringVoidFunc(
	domainCall func(context.Context, string) error,
	extractor func(*http.Request) (string, error),
	mws ...Middleware,
) MethodMuxOption

func WithDeleteVoidFunc

func WithDeleteVoidFunc[T any, V adapt.Validatable[T]](
	domainCall func(context.Context, V) error,
	mws ...Middleware,
) MethodMuxOption

func WithGetCustomFunc

func WithGetCustomFunc[T any, V adapt.Validatable[T], O any](
	domainCall func(context.Context, V) (O, error),
	codec adapt.Codec[T, V, O],
	mws ...Middleware,
) MethodMuxOption

func WithGetCustomStringFunc

func WithGetCustomStringFunc[O any](
	domainCall func(context.Context, string) (O, error),
	extractor func(*http.Request) (string, error),
	encoder adapt.Encoder[O],
	mws ...Middleware,
) MethodMuxOption

func WithGetCustomVoidFunc

func WithGetCustomVoidFunc[T any, V adapt.Validatable[T]](
	domainCall func(context.Context, V) error,
	codec adapt.Codec[T, V, T],
	mws ...Middleware,
) MethodMuxOption

func WithGetFunc

func WithGetFunc[T any, V adapt.Validatable[T], O any](
	domainCall func(context.Context, V) (O, error),
	mws ...Middleware,
) MethodMuxOption

func WithGetHandler

func WithGetHandler(h Handler, mws ...Middleware) MethodMuxOption

func WithGetNullaryCustomFunc

func WithGetNullaryCustomFunc[O any](
	domainCall func(context.Context) (O, error),
	encoder adapt.Encoder[O],
	mws ...Middleware,
) MethodMuxOption

func WithGetNullaryFunc

func WithGetNullaryFunc[O any](
	domainCall func(context.Context) (O, error),
	mws ...Middleware,
) MethodMuxOption

func WithGetStringFunc

func WithGetStringFunc[O any](
	domainCall func(context.Context, string) (O, error),
	extractor func(*http.Request) (string, error),
	mws ...Middleware,
) MethodMuxOption

func WithGetStringVoidFunc

func WithGetStringVoidFunc(
	domainCall func(context.Context, string) error,
	extractor func(*http.Request) (string, error),
	mws ...Middleware,
) MethodMuxOption

func WithGetVoidFunc

func WithGetVoidFunc[T any, V adapt.Validatable[T]](
	domainCall func(context.Context, V) error,
	mws ...Middleware,
) MethodMuxOption

func WithPatch

func WithPatch(h Handler) MethodMuxOption

func WithPatchCustomFunc

func WithPatchCustomFunc[T any, V adapt.Validatable[T], O any](
	domainCall func(context.Context, V) (O, error),
	codec adapt.Codec[T, V, O],
	mws ...Middleware,
) MethodMuxOption

func WithPatchCustomStringFunc

func WithPatchCustomStringFunc[O any](
	domainCall func(context.Context, string) (O, error),
	extractor func(*http.Request) (string, error),
	encoder adapt.Encoder[O],
	mws ...Middleware,
) MethodMuxOption

func WithPatchCustomVoidFunc

func WithPatchCustomVoidFunc[T any, V adapt.Validatable[T]](
	domainCall func(context.Context, V) error,
	codec adapt.Codec[T, V, T],
	mws ...Middleware,
) MethodMuxOption

func WithPatchFunc

func WithPatchFunc[T any, V adapt.Validatable[T], O any](
	domainCall func(context.Context, V) (O, error),
	mws ...Middleware,
) MethodMuxOption

func WithPatchHandler

func WithPatchHandler(h Handler, mws ...Middleware) MethodMuxOption

func WithPatchNullaryCustomFunc

func WithPatchNullaryCustomFunc[O any](
	domainCall func(context.Context) (O, error),
	encoder adapt.Encoder[O],
	mws ...Middleware,
) MethodMuxOption

func WithPatchNullaryFunc

func WithPatchNullaryFunc[O any](
	domainCall func(context.Context) (O, error),
	mws ...Middleware,
) MethodMuxOption

func WithPatchStringFunc

func WithPatchStringFunc[O any](
	domainCall func(context.Context, string) (O, error),
	extractor func(*http.Request) (string, error),
	mws ...Middleware,
) MethodMuxOption

func WithPatchStringVoidFunc

func WithPatchStringVoidFunc(
	domainCall func(context.Context, string) error,
	extractor func(*http.Request) (string, error),
	mws ...Middleware,
) MethodMuxOption

func WithPatchVoidFunc

func WithPatchVoidFunc[T any, V adapt.Validatable[T]](
	domainCall func(context.Context, V) error,
	mws ...Middleware,
) MethodMuxOption

func WithPost

func WithPost(h Handler) MethodMuxOption

func WithPostCustomFunc

func WithPostCustomFunc[T any, V adapt.Validatable[T], O any](
	domainCall func(context.Context, V) (O, error),
	codec adapt.Codec[T, V, O],
	mws ...Middleware,
) MethodMuxOption

func WithPostCustomStringFunc

func WithPostCustomStringFunc[O any](
	domainCall func(context.Context, string) (O, error),
	extractor func(*http.Request) (string, error),
	encoder adapt.Encoder[O],
	mws ...Middleware,
) MethodMuxOption

func WithPostCustomVoidFunc

func WithPostCustomVoidFunc[T any, V adapt.Validatable[T]](
	domainCall func(context.Context, V) error,
	codec adapt.Codec[T, V, T],
	mws ...Middleware,
) MethodMuxOption

func WithPostFunc

func WithPostFunc[T any, V adapt.Validatable[T], O any](
	domainCall func(context.Context, V) (O, error),
	mws ...Middleware,
) MethodMuxOption

func WithPostHandler

func WithPostHandler(h Handler, mws ...Middleware) MethodMuxOption

func WithPostNullaryCustomFunc

func WithPostNullaryCustomFunc[O any](
	domainCall func(context.Context) (O, error),
	encoder adapt.Encoder[O],
	mws ...Middleware,
) MethodMuxOption

func WithPostNullaryFunc

func WithPostNullaryFunc[O any](
	domainCall func(context.Context) (O, error),
	mws ...Middleware,
) MethodMuxOption

func WithPostStringFunc

func WithPostStringFunc[O any](
	domainCall func(context.Context, string) (O, error),
	extractor func(*http.Request) (string, error),
	mws ...Middleware,
) MethodMuxOption

func WithPostStringVoidFunc

func WithPostStringVoidFunc(
	domainCall func(context.Context, string) error,
	extractor func(*http.Request) (string, error),
	mws ...Middleware,
) MethodMuxOption

func WithPostVoidFunc

func WithPostVoidFunc[T any, V adapt.Validatable[T]](
	domainCall func(context.Context, V) error,
	mws ...Middleware,
) MethodMuxOption

func WithPut

func WithPut(h Handler) MethodMuxOption

func WithPutCustomFunc

func WithPutCustomFunc[T any, V adapt.Validatable[T], O any](
	domainCall func(context.Context, V) (O, error),
	codec adapt.Codec[T, V, O],
	mws ...Middleware,
) MethodMuxOption

func WithPutCustomStringFunc

func WithPutCustomStringFunc[O any](
	domainCall func(context.Context, string) (O, error),
	extractor func(*http.Request) (string, error),
	encoder adapt.Encoder[O],
	mws ...Middleware,
) MethodMuxOption

func WithPutCustomVoidFunc

func WithPutCustomVoidFunc[T any, V adapt.Validatable[T]](
	domainCall func(context.Context, V) error,
	codec adapt.Codec[T, V, T],
	mws ...Middleware,
) MethodMuxOption

func WithPutFunc

func WithPutFunc[T any, V adapt.Validatable[T], O any](
	domainCall func(context.Context, V) (O, error),
	mws ...Middleware,
) MethodMuxOption

func WithPutHandler

func WithPutHandler(h Handler, mws ...Middleware) MethodMuxOption

func WithPutNullaryCustomFunc

func WithPutNullaryCustomFunc[O any](
	domainCall func(context.Context) (O, error),
	encoder adapt.Encoder[O],
	mws ...Middleware,
) MethodMuxOption

func WithPutNullaryFunc

func WithPutNullaryFunc[O any](
	domainCall func(context.Context) (O, error),
	mws ...Middleware,
) MethodMuxOption

func WithPutStringFunc

func WithPutStringFunc[O any](
	domainCall func(context.Context, string) (O, error),
	extractor func(*http.Request) (string, error),
	mws ...Middleware,
) MethodMuxOption

func WithPutStringVoidFunc

func WithPutStringVoidFunc(
	domainCall func(context.Context, string) error,
	extractor func(*http.Request) (string, error),
	mws ...Middleware,
) MethodMuxOption

func WithPutVoidFunc

func WithPutVoidFunc[T any, V adapt.Validatable[T]](
	domainCall func(context.Context, V) error,
	mws ...Middleware,
) MethodMuxOption

type Middleware

type Middleware func(Handler) Handler

func NewRequestReadLimiterMiddleware

func NewRequestReadLimiterMiddleware(readLimit int64) Middleware

type Node

type Node struct {
	Leaf              *Route
	TrailingSlashLeaf *Route
	TerminalLeaf      *Route
	Branches          Branches
	DynamicBranch     *Node
}

Node is the nesting routing tree component.

func (*Node) Grow

func (n *Node) Grow(route *Route, remaining []Segment) (err error)

func (*Node) MatchPath

func (n *Node) MatchPath(path string) (route *Route, matches []string)

func (*Node) String

func (n *Node) String() string

func (*Node) Walk

func (n *Node) Walk(walkFn WalkFunc) (err error)

type Option

type Option func(*options) error

func WithDefaultRequestReadLimitOf1MB

func WithDefaultRequestReadLimitOf1MB() Option

func WithLimitlessRequestBytes

func WithLimitlessRequestBytes() Option

func WithMiddleware

func WithMiddleware(mws ...Middleware) Option

func WithOptions

func WithOptions(withOptions ...Option) Option

func WithPrefix

func WithPrefix(p string) Option

func WithRequestReadLimitOf

func WithRequestReadLimitOf(maximumBytes int64) Option

func WithRouteCustomFunc

func WithRouteCustomFunc[T any, V adapt.Validatable[T], O any](
	name, pattern string,
	domainCall func(context.Context, V) (O, error),
	codec adapt.Codec[T, V, O],
	mws ...Middleware,
) Option

func WithRouteCustomStringFunc

func WithRouteCustomStringFunc[O any](
	name, pattern string,
	domainCall func(context.Context, string) (O, error),
	extractor func(*http.Request) (string, error),
	encoder adapt.Encoder[O],
	mws ...Middleware,
) Option

func WithRouteCustomVoidFunc

func WithRouteCustomVoidFunc[T any, V adapt.Validatable[T]](
	name, pattern string,
	domainCall func(context.Context, V) error,
	codec adapt.Codec[T, V, T],
	mws ...Middleware,
) Option

func WithRouteFunc

func WithRouteFunc[T any, V adapt.Validatable[T], O any](
	name, pattern string,
	domainCall func(context.Context, V) (O, error),
	mws ...Middleware,
) Option

func WithRouteHandler

func WithRouteHandler(name, pattern string, h Handler, mws ...Middleware) Option

func WithRouteNullaryCustomFunc

func WithRouteNullaryCustomFunc[O any](
	name, pattern string,
	domainCall func(context.Context) (O, error),
	encoder adapt.Encoder[O],
	mws ...Middleware,
) Option

func WithRouteNullaryFunc

func WithRouteNullaryFunc[O any](
	name, pattern string,
	domainCall func(context.Context) (O, error),
	mws ...Middleware,
) Option

func WithRouteStringFunc

func WithRouteStringFunc[O any](
	name, pattern string,
	domainCall func(context.Context, string) (O, error),
	extractor func(*http.Request) (string, error),
	mws ...Middleware,
) Option

func WithRouteStringVoidFunc

func WithRouteStringVoidFunc(
	name, pattern string,
	domainCall func(context.Context, string) error,
	extractor func(*http.Request) (string, error),
	mws ...Middleware,
) Option

func WithRouteVoidFunc

func WithRouteVoidFunc[T any, V adapt.Validatable[T]](
	name, pattern string,
	domainCall func(context.Context, V) error,
	mws ...Middleware,
) Option

func WithoutTrailingSlashRedirects

func WithoutTrailingSlashRedirects() Option

func WithoutTrailingSlashRedirectsFromSlash

func WithoutTrailingSlashRedirectsFromSlash() Option

func WithoutTrailingSlashRedirectsToSlash

func WithoutTrailingSlashRedirectsToSlash() Option

type RequestReadLimiter

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

func NewRequestReadLimiter

func NewRequestReadLimiter(next Handler, readLimit int64) *RequestReadLimiter

func (*RequestReadLimiter) ServeHyperText

func (l *RequestReadLimiter) ServeHyperText(
	w http.ResponseWriter,
	r *http.Request,
) error

type Route

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

func NewRoute

func NewRoute(name, fromPath string) (r *Route, err error)

func (*Route) Fields

func (r *Route) Fields(matchedValues []string) map[string]string

func (*Route) Name

func (r *Route) Name() string

func (*Route) Path

func (r *Route) Path(fields map[string]string) (string, error)

func (*Route) String

func (r *Route) String() string

type RoutingContext

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

func GetRoutingContext

func GetRoutingContext(ctx context.Context) *RoutingContext

func (*RoutingContext) MatchedFields

func (r *RoutingContext) MatchedFields() *MatchedFields

func (*RoutingContext) Path

func (r *RoutingContext) Path(routeName string, fields map[string]string) (string, error)

type RoutingError

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

func NewRoutingError

func NewRoutingError(cause error) *RoutingError

func (*RoutingError) Error

func (e *RoutingError) Error() string

func (*RoutingError) HyperTextStatusCode

func (e *RoutingError) HyperTextStatusCode() int

func (*RoutingError) LogValue

func (e *RoutingError) LogValue() slog.Value

func (*RoutingError) Unwrap

func (e *RoutingError) Unwrap() error

type Segment

type Segment interface {
	Name() string
	Type() SegmentType
	Match(path string) (value, remainder string, ok bool)
	String() string
}

func NewSegment

func NewSegment(segmentDefinition string) (Segment, error)

NewSegment converts a string to a Segment definition.

type SegmentType

type SegmentType uint8

func (SegmentType) String

func (s SegmentType) String() string

type UnknownHostError

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

func (*UnknownHostError) Error

func (e *UnknownHostError) Error() string

func (*UnknownHostError) HyperTextStatusCode

func (e *UnknownHostError) HyperTextStatusCode() int

func (*UnknownHostError) LogValue

func (e *UnknownHostError) LogValue() slog.Value

type WalkFunc

type WalkFunc func(*Node) (ok bool, err error)

Directories

Path Synopsis
Package adapt provides domain logic adaptors shaped to [oakmux.Handler].
Package adapt provides domain logic adaptors shaped to [oakmux.Handler].
examples
domaincalls
Package main demonstrates routing directly to domain function calls.
Package main demonstrates routing directly to domain function calls.
methods
Package main demonstrates routing directly to domain function calls.
Package main demonstrates routing directly to domain function calls.
staticfs
Package main demonstrates the basic use of [staticfs.FS]
Package main demonstrates the basic use of [staticfs.FS]
Package staticfs adapts [http.FS] to [oakmux.Handler] signature.
Package staticfs adapts [http.FS] to [oakmux.Handler] signature.

Jump to

Keyboard shortcuts

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