Documentation
¶
Overview ¶
Package routeit is a lightweight web framework for Go.
Copyright (c) 2025 Sam Taylor ¶
Licensed under the MIT License. You may obtain a copy of the License at https://opensource.org/licenses/MIT
This package provides HTTP/1.1 request parsing, routing, middleware chaining, error handling, and testing utilities. It is designed as a learning framework for Go, demonstrating core web server patterns without external dependencies.
Example usage:
server := routeit.NewServer(routeit.ServerConfig{Debug: true})
server.RegisterRoutes(routeit.RouteRegistry{
"/hello": func(rw *routeit.ResponseWriter, req *routeit.Request) error {
rw.Text("Hello, World!")
return nil
},
})
server.StartOrPanic()
Index ¶
- Constants
- Variables
- func ContextValueAs[T any](req *Request, key any) (T, bool)
- func NewTlsConfigForCertAndKey(certPath, keyPath string) *tls.Config
- type AllowOriginFunc
- type Chain
- type ContentType
- type CorsConfig
- type ErrorMapper
- type ErrorResponseHandler
- type ErrorResponseWriter
- func (erw *ErrorResponseWriter) Error() (error, bool)
- func (erw *ErrorResponseWriter) Json(v any)
- func (erw *ErrorResponseWriter) Raw(raw []byte)
- func (erw *ErrorResponseWriter) RawWithContentType(raw []byte, ct ContentType)
- func (erw *ErrorResponseWriter) Text(text string)
- func (erw *ErrorResponseWriter) Textf(format string, a ...any)
- type Handler
- type HandlerFunc
- type HttpConfig
- type HttpError
- func ErrBadGateway() *HttpError
- func ErrBadRequest() *HttpError
- func ErrConflict() *HttpError
- func ErrContentTooLarge() *HttpError
- func ErrExpectationFailed() *HttpError
- func ErrFailedDependency() *HttpError
- func ErrForbidden() *HttpError
- func ErrGatewayTimeout() *HttpError
- func ErrGone() *HttpError
- func ErrHttpVersionNotSupported() *HttpError
- func ErrImATeapot() *HttpError
- func ErrInsufficientStorage() *HttpError
- func ErrInternalServerError() *HttpError
- func ErrLengthRequired() *HttpError
- func ErrLocked() *HttpError
- func ErrLoopDetected() *HttpError
- func ErrMethodNotAllowed(allowed ...HttpMethod) *HttpError
- func ErrMisdirectedRequest() *HttpError
- func ErrNetworkAuthenticationRequired() *HttpError
- func ErrNotAcceptable() *HttpError
- func ErrNotExtended() *HttpError
- func ErrNotFound() *HttpError
- func ErrNotImplemented() *HttpError
- func ErrPaymentRequired() *HttpError
- func ErrPreconditionFailed() *HttpError
- func ErrPreconditionRequired() *HttpError
- func ErrProxyAuthenticationRequired() *HttpError
- func ErrRangeNotSatisfiable() *HttpError
- func ErrRequestHeaderFieldsTooLarge() *HttpError
- func ErrRequestTimeout() *HttpError
- func ErrServiceUnavailable() *HttpError
- func ErrTooEarly() *HttpError
- func ErrTooManyRequests() *HttpError
- func ErrURITooLong() *HttpError
- func ErrUnauthorized() *HttpError
- func ErrUnavailableForLegalReasons() *HttpError
- func ErrUnprocessableContent() *HttpError
- func ErrUnsupportedMediaType(accepted ...ContentType) *HttpError
- func ErrUpgradeRequired() *HttpError
- func ErrVariantAlsoNegotiates() *HttpError
- func (e *HttpError) Error() string
- func (e *HttpError) Is(err error) bool
- func (he *HttpError) Status() HttpStatus
- func (he *HttpError) WithCause(cause error) *HttpError
- func (he *HttpError) WithMessage(message string) *HttpError
- func (he *HttpError) WithMessagef(format string, args ...any) *HttpError
- type HttpMethod
- type HttpStatus
- type LogAttrExtractor
- type Middleware
- type MultiMethodHandler
- type QueryParams
- type Request
- func (req *Request) AcceptsContentType(other ContentType) bool
- func (req *Request) BodyFromJson(to any) error
- func (req *Request) BodyFromRaw(ct ContentType) ([]byte, error)
- func (req *Request) BodyFromText() (string, error)
- func (req *Request) ClientIP() string
- func (req *Request) ContentType() ContentType
- func (req *Request) Context() context.Context
- func (req *Request) ContextValue(key any) (any, bool)
- func (req *Request) Headers() *RequestHeaders
- func (req *Request) Host() string
- func (req *Request) Id() string
- func (req *Request) Method() HttpMethod
- func (req *Request) NewContextValue(key any, val any)
- func (req *Request) Path() string
- func (req *Request) PathParam(param string) string
- func (req *Request) Queries() *QueryParams
- func (req *Request) RawPath() string
- func (req *Request) Tls() *tls.ConnectionState
- func (req *Request) UnsafeBodyFromJson(to any) error
- func (req *Request) UnsafeBodyFromRaw() []byte
- func (req *Request) UnsafeBodyFromText() string
- func (req *Request) UserAgent() string
- type RequestHeaders
- type RequestIdProvider
- type RequestSize
- type ResponseHeaders
- type ResponseWriter
- func (rw *ResponseWriter) Headers() *ResponseHeaders
- func (rw *ResponseWriter) Json(v any) error
- func (rw *ResponseWriter) Raw(raw []byte)
- func (rw *ResponseWriter) RawWithContentType(raw []byte, ct ContentType)
- func (rw *ResponseWriter) Status(s HttpStatus)
- func (rw *ResponseWriter) Text(text string)
- func (rw *ResponseWriter) Textf(format string, a ...any)
- type RouteRegistry
- type Server
- func (s *Server) RegisterErrorHandlers(handlers map[HttpStatus]ErrorResponseHandler)
- func (s *Server) RegisterMiddleware(ms ...Middleware)
- func (s *Server) RegisterRoutes(rreg RouteRegistry)
- func (s *Server) RegisterRoutesUnderNamespace(namespace string, rreg RouteRegistry)
- func (s *Server) Start() error
- func (s *Server) StartOrPanic()
- type ServerConfig
- type TestClient
- func (tc TestClient) Delete(path string, h ...string) *TestResponse
- func (tc TestClient) Get(path string, h ...string) *TestResponse
- func (tc TestClient) Head(path string, h ...string) *TestResponse
- func (tc TestClient) Options(path string, h ...string) *TestResponse
- func (tc TestClient) PatchJson(path string, body any, h ...string) *TestResponse
- func (tc TestClient) PatchRaw(path string, body []byte, ct ContentType, h ...string) *TestResponse
- func (tc TestClient) PatchText(path string, text string, h ...string) *TestResponse
- func (tc TestClient) PostJson(path string, body any, h ...string) *TestResponse
- func (tc TestClient) PostRaw(path string, body []byte, ct ContentType, h ...string) *TestResponse
- func (tc TestClient) PostText(path string, text string, h ...string) *TestResponse
- func (tc TestClient) PutJson(path string, body any, h ...string) *TestResponse
- func (tc TestClient) PutRaw(path string, body []byte, ct ContentType, h ...string) *TestResponse
- func (tc TestClient) PutText(path string, text string, h ...string) *TestResponse
- func (tc TestClient) Trace(path string, h ...string) *TestResponse
- func (tc TestClient) WithTestConfig(c TestConfig) TestClient
- type TestConfig
- type TestRequest
- type TestRequestOptions
- type TestResponse
- func (tr *TestResponse) AssertBodyContainsString(t testing.TB, want string)
- func (tr *TestResponse) AssertBodyEmpty(t testing.TB)
- func (tr *TestResponse) AssertBodyMatchesString(t testing.TB, want string)
- func (tr *TestResponse) AssertBodyMatchesStringf(t testing.TB, wantf string, args ...any)
- func (tr *TestResponse) AssertBodyStartsWithString(t testing.TB, want string)
- func (tr *TestResponse) AssertHeaderContains(t testing.TB, header, want string)
- func (tr *TestResponse) AssertHeaderMatches(t testing.TB, header string, want []string)
- func (tr *TestResponse) AssertHeaderMatchesString(t testing.TB, header, want string)
- func (tr *TestResponse) AssertStatusCode(t testing.TB, want HttpStatus)
- func (tr *TestResponse) BodyToJson(t testing.TB, to any)
- func (tr *TestResponse) RefuteHeaderPresent(t testing.TB, header string)
Constants ¶
const ( Byte RequestSize = 1 KiB = 1024 * Byte MiB = 1024 * KiB )
Variables ¶
var ( CTApplicationFormUrlEncoded = ContentType{/* contains filtered or unexported fields */} CTApplicationGraphQL = ContentType{/* contains filtered or unexported fields */} CTApplicationJavaScript = ContentType{/* contains filtered or unexported fields */} CTApplicationJson = ContentType{/* contains filtered or unexported fields */} CTApplicationOctetStream = ContentType{/* contains filtered or unexported fields */} CTApplicationPdf = ContentType{/* contains filtered or unexported fields */} CTApplicationXml = ContentType{/* contains filtered or unexported fields */} CTApplicationZip = ContentType{/* contains filtered or unexported fields */} CTTextCss = ContentType{/* contains filtered or unexported fields */} CTTextCsv = ContentType{/* contains filtered or unexported fields */} CTTextHtml = ContentType{/* contains filtered or unexported fields */} CTTextJavaScript = ContentType{/* contains filtered or unexported fields */} CTTextMarkdown = ContentType{/* contains filtered or unexported fields */} CTTextPlain = ContentType{/* contains filtered or unexported fields */} CTImageAvif = ContentType{/* contains filtered or unexported fields */} CTImageGif = ContentType{/* contains filtered or unexported fields */} CTImageJpeg = ContentType{/* contains filtered or unexported fields */} CTImagePng = ContentType{/* contains filtered or unexported fields */} CTImageSvg = ContentType{/* contains filtered or unexported fields */} CTImageWebp = ContentType{/* contains filtered or unexported fields */} CTAudioMpeg = ContentType{/* contains filtered or unexported fields */} CTAudioOgg = ContentType{/* contains filtered or unexported fields */} CTAudioWav = ContentType{/* contains filtered or unexported fields */} CTVideoMp4 = ContentType{/* contains filtered or unexported fields */} CTVideoOgg = ContentType{/* contains filtered or unexported fields */} CTVideoWebm = ContentType{/* contains filtered or unexported fields */} CTMultipartByteranges = ContentType{/* contains filtered or unexported fields */} CTMultipartFormData = ContentType{/* contains filtered or unexported fields */} CTAcceptAll = ContentType{/* contains filtered or unexported fields */} )
var ( GET = HttpMethod{/* contains filtered or unexported fields */} HEAD = HttpMethod{/* contains filtered or unexported fields */} POST = HttpMethod{/* contains filtered or unexported fields */} PUT = HttpMethod{/* contains filtered or unexported fields */} DELETE = HttpMethod{/* contains filtered or unexported fields */} PATCH = HttpMethod{/* contains filtered or unexported fields */} OPTIONS = HttpMethod{/* contains filtered or unexported fields */} TRACE = HttpMethod{/* contains filtered or unexported fields */} )
var ( StatusContinue = HttpStatus{100, "Continue"} StatusSwitchingProtocols = HttpStatus{101, "Switching Protocols"} StatusProcessing = HttpStatus{102, "Processing"} StatusEarlyHints = HttpStatus{103, "Early Hints"} )
1xx codes
var ( StatusOK = HttpStatus{200, "OK"} StatusCreated = HttpStatus{201, "Created"} StatusAccepted = HttpStatus{202, "Accepted"} StatusNonAuthoritativeInformation = HttpStatus{203, "Non-Authoritative Information"} StatusNoContent = HttpStatus{204, "No Content"} StatusResetContent = HttpStatus{205, "Reset Content"} StatusPartialContent = HttpStatus{206, "Partial Content"} StatusMultiStatus = HttpStatus{207, "Multi-Status"} StatusAlreadyReported = HttpStatus{208, "Already Reported"} StatusIMUsed = HttpStatus{226, "IM Used"} )
2xx codes
var ( StatusMultipleChoices = HttpStatus{300, "Multiple Choices"} StatusMovedPermanently = HttpStatus{301, "Moved Permanently"} StatusFound = HttpStatus{302, "Found"} StatusSeeOther = HttpStatus{303, "See Other"} StatusNotModified = HttpStatus{304, "Not Modified"} StatusUseProxy = HttpStatus{305, "Use Proxy"} StatusUnused = HttpStatus{306, "Unused"} StatusTemporaryRedirect = HttpStatus{307, "Temporary Redirect"} StatusPermanentRedirect = HttpStatus{308, "Permanent Redirect"} )
3xx codes
var ( StatusBadRequest = HttpStatus{400, "Bad Request"} StatusPaymentRequired = HttpStatus{402, "Payment Required"} StatusForbidden = HttpStatus{403, "Forbidden"} StatusNotFound = HttpStatus{404, "Not Found"} StatusMethodNotAllowed = HttpStatus{405, "Method Not Allowed"} StatusNotAcceptable = HttpStatus{406, "Not Acceptable"} StatusProxyAuthenticationRequired = HttpStatus{407, "Proxy Authentication Required"} StatusRequestTimeout = HttpStatus{408, "Request Timeout"} StatusConflict = HttpStatus{409, "Conflict"} StatusGone = HttpStatus{410, "Gone"} StatusLengthRequired = HttpStatus{411, "Length Required"} StatusPreconditionFailed = HttpStatus{412, "Precondition Failed"} StatusContentTooLarge = HttpStatus{413, "Content Too Large"} StatusURITooLong = HttpStatus{414, "URI Too Long"} StatusUnsupportedMediaType = HttpStatus{415, "Unsupported Media Type"} StatusRangeNotSatisfiable = HttpStatus{416, "Range Not Satisfiable"} StatusExpectationFailed = HttpStatus{417, "Expectation Failed"} StatusImATeapot = HttpStatus{418, "I'm a teapot"} StatusMisdirectedRequest = HttpStatus{421, "Misdirected Request"} StatusUnprocessableContent = HttpStatus{422, "Unprocessable Content"} StatusLocked = HttpStatus{423, "Locked"} StatusFailedDependency = HttpStatus{424, "Failed Dependency"} StatusTooEarly = HttpStatus{425, "Too Early"} StatusUpgradeRequired = HttpStatus{426, "Upgrade Required"} StatusPreconditionRequired = HttpStatus{428, "Precondition Required"} StatusTooManyRequests = HttpStatus{429, "Too Many Requests"} StatusRequestHeaderFieldsTooLarge = HttpStatus{431, "Request Header Fields Too Large"} )
4xx codes
var ( StatusInternalServerError = HttpStatus{500, "Internal Server Error"} StatusNotImplemented = HttpStatus{501, "Not Implemented"} StatusBadGateway = HttpStatus{502, "Bad Gateway"} StatusGatewayTimeout = HttpStatus{504, "Gateway Timeout"} StatusHttpVersionNotSupported = HttpStatus{505, "HTTP Version Not Supported"} StatusVariantAlsoNegotiates = HttpStatus{506, "Variant Also Negotiates"} StatusInsufficientStorage = HttpStatus{507, "Insufficient Storage"} StatusLoopDetected = HttpStatus{508, "Loop Detected"} StatusNotExtended = HttpStatus{510, "Not Extended"} StatusNetworkAuthenticationRequired = HttpStatus{511, "Network Authentication Required"} )
5xx codes
Functions ¶
func ContextValueAs ¶
ContextValueAs is a shorthand to casting and using Request.ContextValue It will return a value and true whenever the request context has a key that matches the generic type, and will return false if either the context does not have a matching key, or the value stored under that key does not match the generic type.
func NewTlsConfigForCertAndKey ¶ added in v1.2.0
This is a convenience method for instantiating a TLS config with a single certificate and key. This will panic if the certificate or key cannot be loaded. crypto/tls sets sensible defaults for TLS config, so this is safe to use unless specific fine-grained control is needed.
Types ¶
type AllowOriginFunc ¶
This function should validate the provided origin, returning true if the server should accept cross-origin requests from this origin. Optionally, the function can choose to return values for the Vary response header, to ensure that caches are serving responses correctly. This will be appended to the existing Vary response header, which will already contain Origin.
type Chain ¶
type Chain interface {
Proceed(rw *ResponseWriter, req *Request) error
}
The Chain manages the arrangement of middleware and can be used to invoke the next piece of middleware.
type ContentType ¶
type ContentType struct {
// contains filtered or unexported fields
}
func (ContentType) Matches ¶
func (a ContentType) Matches(b ContentType) bool
Compare two content types for equality. For ease, this considers two content types to be equal of they share the same part and subtype, and their charset is the same OR one charset is UTF-8 and the other is unset. This is because UTF-8 is the default charset used by routeit but is sometimes omitted due to being the de-facto standard across the web.
func (ContentType) WithCharset ¶
func (ct ContentType) WithCharset(cs string) ContentType
Destructively sets the charset of the content type
type CorsConfig ¶
type CorsConfig struct {
// A list of origins the server can accept cross-origin requests from.
// These can either be literal matches for the origin (case sensitive), or
// wildcard matches, denoted by the * character. There can only be at most
// 1 wildcard character in each element in this list.
//
// Example:
// ["http://localhost:*", "http://example.com", "http://*.example.com"]
//
// This will match against any requests from localhost, regardless of the
// port, any request from example.com, and any request from a subdomain of
// example.com. If the Origin header is present and does not match any of
// these patterns, we will reject the request.
AllowedOrigins []string
// Set to true if all origins should be allowed. This is equivalent to
// setting [CorsConfig.AllowedOrigins] = ["*"] or always returning true, ""
// from [CorsConfig.AllowOriginFunc]. Takes precedence over
// [CorsConfig.AllowedOrigins] if both are set.
AllowAllOrigins bool
// A function that determines if the request's origin should be allowed.
// This is only called when the request contains the Origin header, and can
// also return additional Vary header values. Will take precedence over
// [CorsConfig.AllowAllOrigins] and [CorsConfig.AllowedOrigins].
AllowOriginFunc AllowOriginFunc
// The allowed methods for cross-origin requests. This will always contain
// simple methods (GET, HEAD and POST), as well as OPTIONS, which is
// required for pre-flight requests. Note: this only carries relevance when
// handling pre-flight requests initiated by the browser. It will not block
// actual requests containing these methods. If you would like to block
// specific HTTP methods from reaching the server, it is best to implement
// your own middleware. Otherwise, you can simply avoid providing an
// implementation of the corresponding method (i.e. never implement a PUT
// handler); in this way the server will never respond successfully to
// requests of those methods.
AllowedMethods []HttpMethod
// A list of the headers that the server will also accept. These do not
// need to include the CORS safe headers (Accept, Accept-Language,
// Content-Language, Content-Type and Range) and should be headers that
// this server is willing to accept in incoming requests. This only
// confirms to the client that it is permitted to send a request with these
// headers. The server may still employ additional middleware or a handler
// function that rejects the request if the header is not correctly
// populated.
AllowedHeaders []string
// The maximum age that the client (or intermediaries) should cache the
// pre-flight response for. Set to 0 or leave unset if you don't want this
// to be included. Set to a negative number to explicitly set to 0 - i.e.
// the client cannot cache responses.
MaxAge time.Duration
// The headers that should be exposed to the client JavaScript when
// receiving a response to a cross-origin request. By default, browsers
// will expose the Cache-Control, Content-Language, Content-Type, Expires,
// Last-Modified and Pragma response headers to the client JavaScript. If
// additional headers should be included (e.g. X-My-Custom-Header), then
// they should be included here. The casing of the header values does not
// matter.
ExposeHeaders []string
// Determines whether the Access-Control-Allow-Credentials header should be
// included in responses and set to "true". This is required if the client
// and server want the client to send credentials (cookies, HTTP
// authentication, TLS certificates etc.).
IncludeCredentials bool
}
func DefaultCors ¶
func DefaultCors() CorsConfig
Default CORS config that will allow all origins and all methods and not include any special headers or authentication details
type ErrorMapper ¶
The ErrorMapper can be implemented to provide more granular control over error mapping in the server. This function will be called whenever an application error is returned or recovered from a panic and can provide extra control over how errors are transformed. By default, the server will handle known mappings, such as interpreting an ErrNotExists error as a 404: Not Found error. If no mapping can be found, the server will default to a 500: Internal Server Error. If the custom ErrorMapper cannot identify the HttpError it should return, it may return nil
type ErrorResponseHandler ¶
type ErrorResponseHandler func(erw *ErrorResponseWriter, req *Request)
type ErrorResponseWriter ¶
type ErrorResponseWriter struct {
// contains filtered or unexported fields
}
The ErrorResponseWriter is very similar to the ResponseWriter, except it does not allow mutation of the status code of the response. It is used in custom error handlers that can provide a uniform response to the client in the event of a 4xx or 5xx error. Common use cases include a custom 404 response.
func (*ErrorResponseWriter) Error ¶
func (erw *ErrorResponseWriter) Error() (error, bool)
Returns the underlying error that caused the application to return a 4xx or 5xx response. This error is not always present, such as when the server returns 404: Not Found.
func (*ErrorResponseWriter) Json ¶
func (erw *ErrorResponseWriter) Json(v any)
Write a Json response. If the input cannot be marshalled to Json, the original default routeit response for the corresponding status code will be used.
func (*ErrorResponseWriter) Raw ¶
func (erw *ErrorResponseWriter) Raw(raw []byte)
func (*ErrorResponseWriter) RawWithContentType ¶
func (erw *ErrorResponseWriter) RawWithContentType(raw []byte, ct ContentType)
func (*ErrorResponseWriter) Text ¶
func (erw *ErrorResponseWriter) Text(text string)
func (*ErrorResponseWriter) Textf ¶
func (erw *ErrorResponseWriter) Textf(format string, a ...any)
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
func Delete ¶
func Delete(fn HandlerFunc) Handler
Creates a handler that responds to DELETE requests
func Get ¶
func Get(fn HandlerFunc) Handler
Creates a handler that will handle GET request. Internally this will also handle HEAD requests which behave the same as GET requests, except the response does not contain the body, instead it only contains the headers that the GET request would return.
func MultiMethod ¶
func MultiMethod(mmh MultiMethodHandler) Handler
Creates a handler that responds to multiple HTTP methods (e.g. GET and POST on the same route). The router internally will decide which handler to invoke depending on the method of the request. An implementation does not need to be provided for each of the methods in MultiMethodHandler, it is sufficient to only implement the methods that the endpoint should respond to. The handler will ensure that any non-implemented methods return a 405: Method Not Allowed response.
type HandlerFunc ¶
type HandlerFunc func(rw *ResponseWriter, req *Request) error
type HttpConfig ¶ added in v1.2.0
type HttpConfig struct {
// This is the port that the HTTP listener will listen on. If the entire
// [HttpConfig] is left empty, this will default to port 8080. If a
// [tls.Config] is set, this will be left empty (meaning the server will
// not respond to plain HTTP messages) unless explicitly configured.
HttpPort uint16
// The port that HTTPS messages are expected to be sent to. This only has
// relevance if [HttpConfig.TlsConfig] is non-nil. If the HTTPS port is set
// with no TLS config, server setup will panic. When a TLS config is
// provided, the server by default listens on port 443 for HTTPS requests,
// which can be changed with this property if required.
HttpsPort uint16
// The TLS config for the server. This is required if the server wishes to
// receive and respond to HTTPS messages. When provided with no ports
// configured, the server will listen for HTTPS messages on port 443, and
// will not expect HTTP messages. Configure the [HttpConfig.HttpPort]
// explicitly if it is desirable to listen to both HTTP and HTTPS requests.
TlsConfig *tls.Config
// Set this flag if you want the server to instruct clients to upgrade
// their HTTP messages to HTTPS. Enabling this flag with a valid TLS config
// and no HTTP port selected will default to the server listening for plain
// HTTP messages on port 80, and HTTPS messages on the chosen port (or
// defaulted to 443).
UpgradeToHttps bool
// Determines how long clients are instructed to remember to use HTTPS for
// the server and its host subdomains. Set to 0 if the client should not
// choose to remember. Only relevant when [HttpConfig.UpgradeToHttps] is
// set, otherwise its value is ignored.
UpgradeInstructionMaxAge time.Duration
}
The HttpConfig is used to specify details of the port(s) that the server will listen on. If left empty, the server will respond to HTTP requests on port 8080. If a tls.Config is provided, the server will respond to HTTPS requests, defaulting to listening on port 443. If it is desirable to listen to both HTTP and HTTPS requests, the HTTP port will need to be explicitly configured, commonly to port 80. In such cases, the HTTPS port only needs to be set if listening to HTTPS requests on port 443 is not desired. When HTTPS is enabled, the server can also instruct client to upgrade any HTTP communication to HTTPS, controlled using [HttpConfig.UpgradeToHttps] and [HttpConfig.UpgradeInstructionMaxAge]. If a HTTP port is not selected, but UpgradeToHttps is enabled, the server will listen for HTTP messages on port 80.
type HttpError ¶
type HttpError struct {
// contains filtered or unexported fields
}
func ErrBadGateway ¶
func ErrBadGateway() *HttpError
func ErrBadRequest ¶
func ErrBadRequest() *HttpError
func ErrConflict ¶
func ErrConflict() *HttpError
func ErrContentTooLarge ¶
func ErrContentTooLarge() *HttpError
func ErrExpectationFailed ¶
func ErrExpectationFailed() *HttpError
func ErrFailedDependency ¶
func ErrFailedDependency() *HttpError
func ErrForbidden ¶
func ErrForbidden() *HttpError
func ErrGatewayTimeout ¶
func ErrGatewayTimeout() *HttpError
func ErrHttpVersionNotSupported ¶
func ErrHttpVersionNotSupported() *HttpError
func ErrImATeapot ¶
func ErrImATeapot() *HttpError
func ErrInsufficientStorage ¶
func ErrInsufficientStorage() *HttpError
func ErrInternalServerError ¶
func ErrInternalServerError() *HttpError
func ErrLengthRequired ¶
func ErrLengthRequired() *HttpError
func ErrLoopDetected ¶
func ErrLoopDetected() *HttpError
func ErrMethodNotAllowed ¶
func ErrMethodNotAllowed(allowed ...HttpMethod) *HttpError
func ErrMisdirectedRequest ¶
func ErrMisdirectedRequest() *HttpError
func ErrNetworkAuthenticationRequired ¶
func ErrNetworkAuthenticationRequired() *HttpError
func ErrNotAcceptable ¶
func ErrNotAcceptable() *HttpError
func ErrNotExtended ¶
func ErrNotExtended() *HttpError
func ErrNotFound ¶
func ErrNotFound() *HttpError
func ErrNotImplemented ¶
func ErrNotImplemented() *HttpError
func ErrPaymentRequired ¶
func ErrPaymentRequired() *HttpError
func ErrPreconditionFailed ¶
func ErrPreconditionFailed() *HttpError
func ErrPreconditionRequired ¶
func ErrPreconditionRequired() *HttpError
func ErrProxyAuthenticationRequired ¶
func ErrProxyAuthenticationRequired() *HttpError
func ErrRangeNotSatisfiable ¶
func ErrRangeNotSatisfiable() *HttpError
func ErrRequestHeaderFieldsTooLarge ¶
func ErrRequestHeaderFieldsTooLarge() *HttpError
func ErrRequestTimeout ¶
func ErrRequestTimeout() *HttpError
func ErrServiceUnavailable ¶
func ErrServiceUnavailable() *HttpError
func ErrTooEarly ¶
func ErrTooEarly() *HttpError
func ErrTooManyRequests ¶
func ErrTooManyRequests() *HttpError
func ErrURITooLong ¶
func ErrURITooLong() *HttpError
func ErrUnauthorized ¶
func ErrUnauthorized() *HttpError
func ErrUnavailableForLegalReasons ¶
func ErrUnavailableForLegalReasons() *HttpError
func ErrUnprocessableContent ¶
func ErrUnprocessableContent() *HttpError
func ErrUnsupportedMediaType ¶
func ErrUnsupportedMediaType(accepted ...ContentType) *HttpError
func ErrUpgradeRequired ¶
func ErrUpgradeRequired() *HttpError
func ErrVariantAlsoNegotiates ¶
func ErrVariantAlsoNegotiates() *HttpError
func (*HttpError) Is ¶
Is is used to compare error types. routeit considers two HttpError's to be the same if they share the same status code, so long as the status code is valid (i.e. >= 100, < 600)
func (*HttpError) Status ¶
func (he *HttpError) Status() HttpStatus
func (*HttpError) WithMessage ¶
Add a custom message to the response exception. This is destructive and overwrites the previous message if present.
func (*HttpError) WithMessagef ¶
Shorthand for calling HttpError.WithMessage using a format string.
type HttpMethod ¶
type HttpMethod struct {
// contains filtered or unexported fields
}
type HttpStatus ¶
type HttpStatus struct {
// contains filtered or unexported fields
}
Http Status codes for responses. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status
func (HttpStatus) Is1xx ¶
func (s HttpStatus) Is1xx() bool
func (HttpStatus) Is2xx ¶
func (s HttpStatus) Is2xx() bool
func (HttpStatus) Is3xx ¶
func (s HttpStatus) Is3xx() bool
func (HttpStatus) Is4xx ¶
func (s HttpStatus) Is4xx() bool
func (HttpStatus) Is5xx ¶
func (s HttpStatus) Is5xx() bool
type LogAttrExtractor ¶
type LogAttrExtractor func(*Request, HttpStatus) []slog.Attr
The LogAttrExtractor can be used to output additional request metadata to the default log line that is produced on every request. By default, routeit will log the request path (including the rewritten and edge path), the HTTP status of the response, the request's User-Agent field, the request's method and the client's IP address.
type Middleware ¶
type Middleware func(c Chain, rw *ResponseWriter, req *Request) error
A middleware function is called for all incoming requests that reach the server. It can choose to block the request, or pass it off to the next middleware in the chain using [Chain.Proceed]. Common use cases include authentication or rate-limiting. The order with which middleware is registered to the server is important, as it defines the order of the chain. If a middleware chooses to block a request (by returning an error), it will not be propagated through to the rest of the chain, nor the handler defined by the application for the route and method of the request. If headers are set on the response, using ResponseWriter.Headers, the headers will be propagated to the response - even if the handler or intermediary middleware returns an error or panics. The error's headers take precedence and will overwrite any headers of the same name that are already set.
func CorsMiddleware ¶
func CorsMiddleware(cc CorsConfig) Middleware
Returns middleware that allows CORS requests from the client to function properly on the server. This should be placed early in the middleware stack and ideally before authentication middleware or most custom middleware. This middleware will handle any cross-origin request, including pre-flight and actual requests.
type MultiMethodHandler ¶
type MultiMethodHandler struct {
Get HandlerFunc
Post HandlerFunc
Put HandlerFunc
Delete HandlerFunc
Patch HandlerFunc
}
type QueryParams ¶
type QueryParams struct {
// contains filtered or unexported fields
}
func (*QueryParams) All ¶
func (q *QueryParams) All(key string) ([]string, bool)
Access a query parameter if present. This returns a slice that may contain multiple elements, some of which may be empty (e.g. if the client sends `?foo=`).
func (*QueryParams) First ¶
func (q *QueryParams) First(key string) (string, bool)
Access the first query parameter for the given key, if present
func (*QueryParams) Last ¶
func (q *QueryParams) Last(key string) (string, bool)
Access the last query parameter for the given key, if present
func (*QueryParams) Only ¶
func (q *QueryParams) Only(key string) (string, bool, error)
Access a query parameter, asserting that it is only present exactly once in the request URI. This will return false if the query parameter is not present, and a 400: Bad Request error if the query parameter is present more than once.
type Request ¶
type Request struct {
// contains filtered or unexported fields
}
func (*Request) AcceptsContentType ¶
func (req *Request) AcceptsContentType(other ContentType) bool
Can be used to determine whether the client will accept the provided ContentType
func (*Request) BodyFromJson ¶
Parses the Json request body into the destination. Ensures that the Content-Type header is application/json and will return a 415: Unsupported Media Type error if this is not the case. Will panic if the destination is not a pointer. Will also panic if the request cannot contain a request body, such as GET requests. Should be preferred to Request.UnsafeBodyFromJson.
func (*Request) BodyFromRaw ¶
func (req *Request) BodyFromRaw(ct ContentType) ([]byte, error)
Returns the raw body content provided it matches the given content type, otherwise a 415: Unsupported Media Type error is returned. Will panic if this is called on a method that cannot support a request body, such as GET, HEAD or OPTIONS. Should be preferred to Request.UnsafeBodyFromRaw.
func (*Request) BodyFromText ¶
Parses the text/plain content from the request. This method checks that the Content-Type header is set to text/plain, returning a 415: Unsupported Media Type error if that is not the case. Panics if the request method is GET, since GET requests cannot support bodies. Should be preferred to Request.UnsafeBodyFromText.
func (*Request) ContentType ¶
func (req *Request) ContentType() ContentType
Access the content type of the request body. Does not return a meaningful value for requests without the Content-Type header, or requests that should not contain a body, such as GET. The ContentType.Matches method can be used to determine whether two content types are equivalent.
func (*Request) Context ¶
Access the request's context. This should be used for context aware calculations within handlers or middleware, for example to load an object from a database.
func (*Request) ContextValue ¶
Access a value stored on the request's context.
func (*Request) Headers ¶
func (req *Request) Headers() *RequestHeaders
Access the headers of the request.
func (*Request) Id ¶ added in v1.1.0
The ID of the request. This will only be populated if an implementation is provided to [ServerConfig.RequestIdProvider].
func (*Request) NewContextValue ¶
Store a value on the request's context. This is particularly useful for middleware, which can set particular values the can be read in later middleware or the handler itself.
func (*Request) Path ¶
The request's URL excluding the host. Does not include query parameters. Where the server has URL rewrites configured, this will be the rewritten URL. This URL has been escaped correctly, so cannot be used for additional routing where dynamic path segment routing is used in the server, as decoded "/"s could inadvertently be treated as control characters. If additional routing needs to be performed based on the path delimiter, use Request.RawPath.
func (*Request) PathParam ¶
Extract a path parameter from the request path. The name must exactly match the name of the parameter when it was registered to the router. For example, if the route was registered under `/:foO|prefix|suffix`, then this method should be called with `"foO"`. This will always be a non-empty value corresponding to the named path segment in the request, unless the name does not match any segments, in which case it will be empty.
func (*Request) Queries ¶
func (req *Request) Queries() *QueryParams
Access the query parameters of the request URI. This will always return a non-nil pointer, even if the URI contains no query parameters. See the QueryParams type for access methods to retrieve individual keys.
func (*Request) RawPath ¶
The raw path received at the edge of the server. This is not url-decoded and has not been rewritten if URL rewriting is enabled for the server.
func (*Request) Tls ¶ added in v1.2.0
func (req *Request) Tls() *tls.ConnectionState
The request's TLS connection state. This is nil when the request was received over HTTP and non-nil whenever the request was made using HTTPS. It contains details about the TLS connection, such as the version and cipher suites.
func (*Request) UnsafeBodyFromJson ¶
Parses the Json request body into the destination. Does not check the Content-Type header to confirm that the request body has application/json type body. Will panic if the destination is not a pointer, or the request cannot support a body (such as GET requests).
func (*Request) UnsafeBodyFromRaw ¶
Returns the raw body content. Will panic if this is called on a method that cannot support a request body, such as GET, HEAD or OPTIONS. This does not assert that the body is present nor contains a corresponding Content-Type header
func (*Request) UnsafeBodyFromText ¶
Returns the raw body content as a string. Will panic if this is called on a method that cannot support a request body, such as GET, HEAD or OPTIONS.
type RequestHeaders ¶
type RequestHeaders struct {
// contains filtered or unexported fields
}
The RequestHeaders type can be used to read the headers on the incoming request. Lookup is case insensitive, and the header may appear multiple times within the request.
func (*RequestHeaders) All ¶
func (rh *RequestHeaders) All(key string) ([]string, bool)
Access all request header values for the given key.
func (*RequestHeaders) First ¶
func (rh *RequestHeaders) First(key string) (string, bool)
A wrapper over RequestHeaders.All that extracts the first element, if present.
func (*RequestHeaders) Last ¶
func (rh *RequestHeaders) Last(key string) (string, bool)
A wrapper over RequestHeaders.All that extracts the last element, if present.
func (*RequestHeaders) Only ¶
func (rh *RequestHeaders) Only(key string) (string, bool, error)
Use RequestHeaders.Only when the header is expected to appear exactly once in the request. It will return an error if the headers is present but does not contain exactly 1 value.
type RequestIdProvider ¶ added in v1.1.0
A RequestIdProvider is used to provide each incoming request with an ID. Each valid request that reaches the server will be tagged with an ID returned from this function. The resultant ID will be available on the request using Request.Id.
type RequestSize ¶
type RequestSize uint32
The RequestSize type is used to define what size request the server is willing to accept from the client. It accounts for the entire request - including the request line and headers.
type ResponseHeaders ¶
type ResponseHeaders struct {
// contains filtered or unexported fields
}
The ResponseHeaders can be used to mutate the headers for a given ResponseWriter. It allows writing of header values and uses case- insensitive insertion for the header key.
func (*ResponseHeaders) Append ¶
func (rh *ResponseHeaders) Append(key, val string)
Append the given value to the current header value for the key. Prefer this to ResponseHeaders.Set unless the header explicitly needs to be overwritten. Header key and values will be sanitised per HTTP spec before being added to the response. It is the user's responsibility to ensure that the headers are safe and non-conflicting. For example, it is heavily discouraged to modify the Content-Type or Content-Length headers as they are managed implicitly whenever a body is written to a response and can cause issues on the client if they contain incorrect values.
func (*ResponseHeaders) Set ¶
func (rh *ResponseHeaders) Set(key, val string)
Use ResponseHeaders.Set to completely overwrite whatever headers are already set for the given (case-insensitive) key. Prefer ResponseHeaders.Append where possible. This is destructive, meaning repeated calls using the same key will preserve the last value. Header key and values will be sanitised per HTTP spec before being added to the response. It is the user's responsibility to ensure that the headers are safe and non-conflicting. For example, it is heavily discouraged to modify the Content-Type or Content-Length headers as they are managed implicitly whenever a body is written to a response and can cause issues on the client if they contain incorrect values.
type ResponseWriter ¶
type ResponseWriter struct {
// contains filtered or unexported fields
}
func (*ResponseWriter) Headers ¶
func (rw *ResponseWriter) Headers() *ResponseHeaders
Access the headers for the response so they can be mutated. It is the user's responsibility to ensure that the headers are safe and non-conflicting. For example, it is heavily discouraged to modify the Content-Type or Content-Length headers as they are managed implicitly whenever a body is written to a response and can cause issues on the client if they contain incorrect values.
func (*ResponseWriter) Json ¶
func (rw *ResponseWriter) Json(v any) error
Adds a JSON response body to the response and sets the corresponding Content-Length and Content-Type headers. This is a destructive operation, meaning repeated calls to Json(...) only preserve the last invocation.
func (*ResponseWriter) Raw ¶
func (rw *ResponseWriter) Raw(raw []byte)
Adds a raw response body to the response and sets the corresponding Content-Length and Content-Type headers. This is a destructive operation, meaning repeated calls to Raw(...) only preserve the last invocation. The mimetype of the body is inferred from its content.
func (*ResponseWriter) RawWithContentType ¶
func (rw *ResponseWriter) RawWithContentType(raw []byte, ct ContentType)
Destructively sets the body of the response and updates headers accordingly
func (*ResponseWriter) Status ¶
func (rw *ResponseWriter) Status(s HttpStatus)
Sets the status of the response. The server sets an opinionated default depending on the incoming request. POST requests default to 201: Created and DELETE requests default to 204: No Content. All other request methods default to 200: OK.
func (*ResponseWriter) Text ¶
func (rw *ResponseWriter) Text(text string)
Adds a plaintext response body to the response and sets the corresponding Content-Length and Content-Type headers. This is a destructive operation, meaning repeated calls to Text(...) only preserve the last invocation.
func (*ResponseWriter) Textf ¶
func (rw *ResponseWriter) Textf(format string, a ...any)
Shorthand for the Text function using a format string.
type RouteRegistry ¶
The RouteRegistry is used to associate routes with their corresponding handlers. Routing supports both static and dynamic routes. The keys of the RouteRegistry represent the route that the handler will be matched against. They can optionally include a leading slash and all trailing slashes will be stripped. Static routes can be supplied by providing the exact path the route should match against. Dynamic routes can be provided using a colon-notation. Optional prefixes and/or suffixes can be provided for dynamic routes, that will only match against the incoming URI if the corresponding path segments start or end with the given prefix or suffix. The extracted path parameter contains the entire matched path segment - including the prefix and/or suffix. The substring between the prefix and suffix must have non-zero length.
Examples:
- "/foo/:bar" -> This will match against "/foo/<anything>" and name the first matched parameter "bar".
- "/:foo/bar/:baz" -> This will match against "/<anything>/bar/<anything>". The first matched parameter will be named "foo", while the second will be named "baz".
- "/:foo|pref" -> This will match against "/pref<anything>".
- "/:foo||suf" -> This will match against "/<anything>suf".
- "/:foo|pref|suf" -> This will match against "/pref<anything>suf".
Registering routes with dynamic components with the same name (such as "/:foo/bar/:foo") will cause the application to panic.
Names parameters can be accessed using Request.PathParam, providing the case-sensitive name of the parameter as provided in the route registration.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
func NewServer ¶
func NewServer(conf ServerConfig) *Server
Constructs a new server given the config. Defaults are provided for all options to provide a base of sane values. When setting up for the first time make sure to set [ServerConfig.Debug] to true, or include valid hosts in [ServerConfig.AllowedHosts], to allow the server to receive requests that it will accept.
func (*Server) RegisterErrorHandlers ¶
func (s *Server) RegisterErrorHandlers(handlers map[HttpStatus]ErrorResponseHandler)
Register specific handlers for a given status code. These are called automatically after the entire request has finished processing, and allow the integrator to uniformly respond to certain 4xx or 5xx status codes. Common use cases include for 401 or 404 handling. For example, it may be desired for all 404 responses to return application/json content, which can be done in one place. This method will panic if handlers are attempted to be registered for non 4xx or 5xx status codes.
func (*Server) RegisterMiddleware ¶
func (s *Server) RegisterMiddleware(ms ...Middleware)
Registers middleware to the server. The order of registration matters, where the first middleware registered will be the first middleware called in the chain, the second will be the second and so on.
func (*Server) RegisterRoutes ¶
func (s *Server) RegisterRoutes(rreg RouteRegistry)
Register all routes in the provided registry to the router on the server. All routes will already obey the global namespace (if configured). This is a destructive operation, meaning that if there are multiple calls to RegisterRoutes with overlapping values, the latest value takes precedence.
func (*Server) RegisterRoutesUnderNamespace ¶
func (s *Server) RegisterRoutesUnderNamespace(namespace string, rreg RouteRegistry)
RegisterRoutesUnderNamespace registers all routes in the registry under a specific namespace. All routes already obey the global namespace (if configured). This is a destructive operation - for example, if the /api/foo route has already been registered, and this function is called with the /api namespace and the registry contains a /foo route, this function will overwrite the original routing entry. The local namespace used here may contain dynamic path components, and will match in the same manner that regular dynamic path components do in their routing.
Examples:
Namespace = /api
RegisterRoutesUnderNamespace("/foo", {"/bar": ...})
The route registered will be /api/foo/bar
Namespace = <not initialised>
RegisterRoutesUnderNamespace("/foo/bar", {"/baz": ...})
The route will be registered under /foo/bar/baz
func (*Server) Start ¶
Starts the server using the config and registered routes. This should be the last line of a main function as any code after this call is not executable. The server's config is thread-safe - meaning that if thread A initialised the server, registered routes and started the server, and thread B attempted to register additional routes to the same server, then thread B would panic. The server may also not be started multiple times from different threads as this will also cause a panic
func (*Server) StartOrPanic ¶
func (s *Server) StartOrPanic()
Attempts to start the server, panicking if that fails
type ServerConfig ¶
type ServerConfig struct {
// The maximum request size (headers, request line and body inclusive) that
// the server will accept. Anything above this will be rejected.
RequestSize RequestSize
// The read deadline to leave the connection with the client open for.
ReadDeadline time.Duration
// The write deadline that the connection is left open with the client
// for responses.
WriteDeadline time.Duration
// A global namespace that **all** routes are registered under. Common
// examples include /api. Does not need to include a leading slash. The
// global namespace may not contain dynamic routing segments - e.g. a
// global namespace of /:foo will register all routes on the server
// directly under the literal "/:foo" route.
Namespace string
// The location of the statically loaded files served by the server. All
// requests to reach these files must also start with this prefix and may
// also need the global Namespace if configured. The path must be a
// subdirectory of the project's root and the server will panic if this is
// not the case. The path does not have to point to a _valid_ directory as
// this allows the server to dynamically write to disk and serve files from
// there, though this is discouraged. The path is interpreted as a relative
// path, not an absolute path, regardless of the presence of a leading slash.
StaticDir string
// Enables debug information, such as logs. Do not enable for production
// servers. Example behaviour includes logging request bodies for 4xx or
// 5xx responses.
Debug bool
// The path to the configuration file holding the URL rewrite information
// for the server. It may be anywhere on the system, but must exist and be
// readable by the server, otherwise the setup will panic. The file must be
// a .conf file following the URL rewrite syntax. If the rewrites are
// illegal (e.g. conflicting entries, invalid URI's or malformed in
// general), the server setup will panic. Rewriting is not recursive, i.e.
// if a server has rules /foo -> /bar and /bar -> /baz, an incoming request
// to /foo will only be rewritten to /bar, it will not take the second step
// to /baz. The [Request.Path] method always returns the request path
// **after** rewriting.
URLRewritePath string
// An optional mapper to map application errors to routeit [HttpError]s
// that are transformed to valid responses. Called whenever the application
// code returns or panics an error
ErrorMapper ErrorMapper
// The allowed hosts that this server can serve from. If the incoming
// request contains a Host header that is not satisfied by this list,
// routeit will reject the request. Fully qualified domains can be specific
// (e.g. www.example.com), or the domain can be prepended with . to match
// against all subdomains. For example, .example.com will match against
// api.example.com, www.example.com, example.com and any other subdomains
// of example.com. Only a single layer of subdomains is considered (i.e.
// this will not match against site.web.example.com). When Debug is enabled
// this defaults to [".localhost", "127.0.0.1", "[::1]"] if no list is
// specified.
AllowedHosts []string
// When enabled, the server will only return responses to the client that
// strictly match the client's Accept header, if it is included in the
// request. If the application code returns a non-compliant response
// Content-Type, the server will automatically transform this to a 406: Not
// Acceptable response.
StrictClientAcceptance bool
// By default, routeit registers TRACE handlers for all routes registered
// to the server. If this is not desirable, leave AllowTraceRequests at its
// default value of false. If you are happy to support TRACE requests, set
// AllowTraceRequests to true.
AllowTraceRequests bool
// The logging handler used for server-specific logging. Logging within the
// application code may use its own logging handler that is independent of
// the given handler. If not supplied, this will use a [slog.JSONHandler]
// that outputs to [os.Stdout]. Level defaults to INFO but will be set to
// DEBUG if [ServerConfig.Debug] is true.
LoggingHandler slog.Handler
// Http request and response headers may appear multiple times in the
// message. For certain headers, repeated entries can pose security risks
// or not make sense when attempting to interpret the request. routeit will
// block requests that repeat specific header values automatically. By
// default, the list of headers where only 0 or 1 value is allowed is
// "Authorization", "Content-Length", "Content-Type", "Cookie", "Expect",
// "Host", "Origin", "Range", "Referer" and "User-Agent". If you have
// additional headers that should be limited to 0 or 1 appearances, they
// can be included in this property. Lookup is case-insensitive.
StrictSingletonHeaders []string
// Use [LogAttrExtractor] to include additional metadata in the default
// request line that is dumped for all incoming requests.
LogAttrExtractor LogAttrExtractor
// Use [RequestIdProvider] to tag each incoming request with an ID. This
// will automatically be logged and will be included as the "X-Request-Id"
// header in the response. Note that the ID returned by this function is
// allowed to be empty. In such cases, routeit will proceed the request as
// normal, since the lack of request ID is not a strong enough reason to
// entirely block a request. If such behaviour is desirable, it is
// recommended to introduce custom middleware that blocks requests that
// contain empty request ID's.
RequestIdProvider RequestIdProvider
// The header that each request ID is given in the response. This will
// default to "X-Request-Id" if [ServerConfig.RequestIdProvider] is
// non-nil. Otherwise, the header value will be ignored.
RequestIdHeader string
// Use [HttpConfig] to control whether the server responds to HTTP
// requests, HTTPS requests, or both.
HttpConfig
}
type TestClient ¶
type TestClient struct {
// contains filtered or unexported fields
}
func NewTestClient ¶
func NewTestClient(s *Server) TestClient
Instantiates a test client that can be used to perform end-to-end tests on the server.
func NewTestTlsClient ¶ added in v1.2.0
func NewTestTlsClient(s *Server, tlsState *tls.ConnectionState) TestClient
Creates a client that will simulate having a valid TLS connection to the server. Use this method when the handlers or middleware performs TLS dependent actions.
func (TestClient) Delete ¶
func (tc TestClient) Delete(path string, h ...string) *TestResponse
Makes a DELETE request against the resource. Can include an arbitrary number of headers, specific individually by their keys and values
func (TestClient) Get ¶
func (tc TestClient) Get(path string, h ...string) *TestResponse
Makes a GET request against the specific path. Should not include a trailing slash but may optionally omit a leading slash. Can include an arbitrary number of headers, specified after the path. Keys and values of headers should be individual arguments.
func (TestClient) Head ¶
func (tc TestClient) Head(path string, h ...string) *TestResponse
Makes a HEAD request against the specific path. Should not include a trailing slash but may optionally omit the leading slash. Can include an arbitrary number of headers, specified after the path. Keys and values of headers should be individual arguments.
func (TestClient) Options ¶
func (tc TestClient) Options(path string, h ...string) *TestResponse
Makes an OPTIONS request against the specified endpoint. Can include key, value pairs representing the headers of the request.
func (TestClient) PatchJson ¶
func (tc TestClient) PatchJson(path string, body any, h ...string) *TestResponse
Makes a PATCH request against the specified path, using a Json request body. Will panic if the Json marshalling fails. Can include an arbitrary number of headers, specified as key, value pairs after the request body.
func (TestClient) PatchRaw ¶
func (tc TestClient) PatchRaw(path string, body []byte, ct ContentType, h ...string) *TestResponse
Makes a PATCH request with the corresponding body and content type. Content length is calculated automatically. Additional headers can also be specified in pairs after the content type.
func (TestClient) PatchText ¶
func (tc TestClient) PatchText(path string, text string, h ...string) *TestResponse
Makes a PATCH request against the specified path, using a text request body. Allows for inclusion of an arbitrary number of headers, specified in key, value format after the body.
func (TestClient) PostJson ¶
func (tc TestClient) PostJson(path string, body any, h ...string) *TestResponse
Makes a POST request against the specified path, using the second argument as the request body, which is converted to Json. Panics if the Json conversion fails. Can include an arbitrary number of headers, specified after the request body. Keys and values of headers should be individual arguments.
func (TestClient) PostRaw ¶
func (tc TestClient) PostRaw(path string, body []byte, ct ContentType, h ...string) *TestResponse
Makes a POST request with the corresponding body and content type. Content length is calculated automatically. Additional headers can also be specified in pairs after the content type.
func (TestClient) PostText ¶
func (tc TestClient) PostText(path string, text string, h ...string) *TestResponse
Makes a POST request against the specified path, using a text request body. Can include an arbitrary number of headers, specified after the request body. Keys and values of headers should be individual arguments.
func (TestClient) PutJson ¶
func (tc TestClient) PutJson(path string, body any, h ...string) *TestResponse
Makes a PUT request against the specified path, using a Json request body. Will panic if the Json marshalling fails. Can include an arbitrary number of headers, specified as key, value pairs after the request body.
func (TestClient) PutRaw ¶
func (tc TestClient) PutRaw(path string, body []byte, ct ContentType, h ...string) *TestResponse
Makes a PUT request with the corresponding body and content type. Content length is calculated automatically. Additional headers can also be specified in pairs after the content type.
func (TestClient) PutText ¶
func (tc TestClient) PutText(path string, text string, h ...string) *TestResponse
Makes a PUT request against the specified path, using a text request body. Allows for inclusion of an arbitrary number of headers, specified in key, value format after the body.
func (TestClient) Trace ¶
func (tc TestClient) Trace(path string, h ...string) *TestResponse
Makes a TRACE request against the specified endpoint. Can include key, value pairs representing the headers of the request. This will only be accepted by the server if it supports the TRACE method, which is disabled by default.
func (TestClient) WithTestConfig ¶
func (tc TestClient) WithTestConfig(c TestConfig) TestClient
Instantiate a new test client with the given overwrite config. These values (so long as they are non-zero) will overwrite the server's configuration used in the test. They are especially useful for time related tests, as they can be used to reduce the write deadline for the server, helping to keep tests running quickly
type TestConfig ¶
type TestRequest ¶
type TestRequest struct {
// contains filtered or unexported fields
}
The TestRequest object can be used when unit testing specific components of a routeit application, such as middleware. It should be constructed using NewTestRequest and provides ways to verify behaviour that happens to a request, for example if a context value was added properly.
func NewTestRequest ¶
func NewTestRequest(t testing.TB, path string, m HttpMethod, opts TestRequestOptions) *TestRequest
This will create a new test request object that can be used in tests, for example when unit testing middleware.
func (*TestRequest) ContextValue ¶
func (tr *TestRequest) ContextValue(key string) (any, bool)
func (*TestRequest) NewContextValue ¶
func (tr *TestRequest) NewContextValue(key string, val any)
type TestRequestOptions ¶
type TestRequestOptions struct {
// The raw body of the request. This must be marshalled by the user of
// [TestRequestOptions] and the corresponding Content-Type header should be
// included in [TestRequestOptions.Headers] if the handler or middleware
// uses safe body loads. Additionally, the Content-Length header should
// also be provided.
Body []byte
// The headers of the request, if any. These should be specific as key,
// value pairs in order. For example:
// []string{"Authorization", "Bearer foo", "Content-Type": "text/plain"}
Headers []string
// The Ip address of the client. This may be useful for security middleware
Ip string
// Specific path parameters that the URI should contain and extract.
// Currently this needs to be explicitly defined since unit tests do not go
// through the entire handling flow, meaning requests are not routed
// properly so path parameters are not extracted.
PathParams map[string]string
// The TLS connection state. Use this if the middleware or handler under
// test is TLS aware (e.g. middleware that may block clients if they are
// not using TLS)
TlsConnectionState *tls.ConnectionState
}
The TestRequestOptions allow you to specify certain traits the request will have under test. You do not need to use any of the fields, though it can be helpful to set certain state on the request, such as a specific header, if the unit under test relies on the state to perform an action.
type TestResponse ¶
type TestResponse struct {
// contains filtered or unexported fields
}
func TestHandler ¶
func TestHandler(h Handler, tr *TestRequest) (*TestResponse, error)
The TestHandler function allows for isolated unit tests of a handler function. The TestRequest can be composed using NewTestRequest and can take whatever headers, body and context the test needs to test the handler appropriately. Under test circumstances, the handler will **not** reject the request if the URI does not match the URI the handler is registered to.
func TestMiddleware ¶
func TestMiddleware(m Middleware, tr *TestRequest) (*TestResponse, bool, error)
The TestMiddleware function can be used in unit tests to test a middleware's implementation. A TestResponse is returned alongside a boolean flag and an error. The TestResponse allows assertions to be made on the ResponseWriter, for example to assert that a particular header was included or not. The boolean confirms whether the middleware proceeded or not, and the error is the error that the middleware ultimately returned.
func (*TestResponse) AssertBodyContainsString ¶
func (tr *TestResponse) AssertBodyContainsString(t testing.TB, want string)
Assert that a body contains the given substring. Supports improper substrings (i.e. where the substring is exactly equal to the superstring).
func (*TestResponse) AssertBodyEmpty ¶
func (tr *TestResponse) AssertBodyEmpty(t testing.TB)
Assert that the response body is empty
func (*TestResponse) AssertBodyMatchesString ¶
func (tr *TestResponse) AssertBodyMatchesString(t testing.TB, want string)
Assert that a body exactly matches the given string
func (*TestResponse) AssertBodyMatchesStringf ¶
func (tr *TestResponse) AssertBodyMatchesStringf(t testing.TB, wantf string, args ...any)
Assert that a body exactly matches the given string with format options This is the same as formatting the string using fmt.Sprintf and calling TestResponse.AssertBodyMatchesString directly
func (*TestResponse) AssertBodyStartsWithString ¶
func (tr *TestResponse) AssertBodyStartsWithString(t testing.TB, want string)
Assert that a body starts with the given prefix. Supports improper substrings (i.e. where the prefix exactly equals the whole body).
func (*TestResponse) AssertHeaderContains ¶
func (tr *TestResponse) AssertHeaderContains(t testing.TB, header, want string)
Assert a header is present and that it contains the given string. For clarity, this will only compare over a single slice element, it will not join multiple elements into 1 string for comparison.
func (*TestResponse) AssertHeaderMatches ¶
func (tr *TestResponse) AssertHeaderMatches(t testing.TB, header string, want []string)
Assert that a header is present and matches the given slice of strings
func (*TestResponse) AssertHeaderMatchesString ¶
func (tr *TestResponse) AssertHeaderMatchesString(t testing.TB, header, want string)
Similar to TestResponse.AssertHeaderMatches, except we assert there is only exactly 1 element in the header slice, and it matches the given string exactly.
func (*TestResponse) AssertStatusCode ¶
func (tr *TestResponse) AssertStatusCode(t testing.TB, want HttpStatus)
Assert that the status code of the response matches
func (*TestResponse) BodyToJson ¶
func (tr *TestResponse) BodyToJson(t testing.TB, to any)
Parses the Json response into a destination object. Fails if the Json parsing fails or if the response is not a Json response. The destination must be passed by reference and not by value.
func (*TestResponse) RefuteHeaderPresent ¶
func (tr *TestResponse) RefuteHeaderPresent(t testing.TB, header string)
Asserts that a header key is not present in the response
Source Files
¶
- config.go
- content_type.go
- cors.go
- doc.go
- errors.go
- handler.go
- header_validation_middleware.go
- host_middleware.go
- https_middleware.go
- logging.go
- middleware.go
- query.go
- request.go
- request_headers.go
- request_id_middleware.go
- response.go
- response_headers.go
- router.go
- server.go
- status.go
- test_client.go
- test_handler.go
- test_middleware.go
- test_request.go
- test_response.go
- uri.go