Documentation ¶
Overview ¶
Package waf provides reusable components of a Vue-compatible web application framework.
Those components can be combined in a way to create a production ready HTTP/1.1 and HTTP2 application server.
Index ¶
- func Error(w http.ResponseWriter, _ *http.Request, code int)
- func GetSite[SiteT hasSite](ctx context.Context) (SiteT, bool)
- func MustGetSite[SiteT hasSite](ctx context.Context) SiteT
- func MustRequestID(ctx context.Context) identifier.Identifier
- func RequestID(ctx context.Context) (identifier.Identifier, bool)
- type Handler
- type MethodNotAllowedError
- type Params
- type Route
- type Router
- func (r *Router) Handle(name, method, path string, api bool, handler Handler) errors.E
- func (r *Router) Reverse(name string, params Params, qs url.Values) (string, errors.E)
- func (r *Router) ReverseAPI(name string, params Params, qs url.Values) (string, errors.E)
- func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)
- type Server
- type Service
- func (s *Service[SiteT]) AddMetadata(w http.ResponseWriter, req *http.Request, metadata map[string]interface{}) ([]byte, errors.E)
- func (s *Service[SiteT]) BadRequest(w http.ResponseWriter, req *http.Request)
- func (s *Service[SiteT]) BadRequestWithError(w http.ResponseWriter, req *http.Request, err errors.E)
- func (s *Service[SiteT]) InternalServerError(w http.ResponseWriter, req *http.Request)
- func (s *Service[SiteT]) InternalServerErrorWithError(w http.ResponseWriter, req *http.Request, err errors.E)
- func (s *Service[SiteT]) MethodNotAllowed(w http.ResponseWriter, req *http.Request, allow []string)
- func (s *Service[SiteT]) NotFound(w http.ResponseWriter, req *http.Request)
- func (s *Service[SiteT]) Proxy(w http.ResponseWriter, req *http.Request)
- func (s *Service[SiteT]) RedirectToMainSite(mainDomain string) func(next http.Handler) http.Handler
- func (s *Service[SiteT]) Reverse(name string, params Params, qs url.Values) (string, errors.E)
- func (s *Service[SiteT]) ReverseAPI(name string, params Params, qs url.Values) (string, errors.E)
- func (s *Service[SiteT]) RouteWith(service interface{}, router *Router) (http.Handler, errors.E)
- func (s *Service[SiteT]) ServeStaticFile(w http.ResponseWriter, req *http.Request, path string)
- func (s *Service[SiteT]) TemporaryRedirectGetMethod(w http.ResponseWriter, req *http.Request, location string)
- func (s *Service[SiteT]) TemporaryRedirectSameMethod(w http.ResponseWriter, req *http.Request, location string)
- func (s *Service[SiteT]) WithError(ctx context.Context, err errors.E)
- func (s *Service[SiteT]) WriteJSON(w http.ResponseWriter, req *http.Request, data interface{}, ...)
- type Site
- type TLS
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Error ¶
func Error(w http.ResponseWriter, _ *http.Request, code int)
Error replies to the request with the specified HTTP code. Error message is automatically generated based on the HTTP code using http.StatusText.
It does not otherwise end the request; the caller should ensure no further writes are done to w.
func GetSite ¶
GetSite returns the site from context ctx and true if the site is stored in the context.
Note, Waf service always stores the site (based on host header in the request) in the request context.
func MustGetSite ¶
MustGetSite returns the site from context ctx or panics if the site is not stored in the context.
Note, Waf service always stores the site (based on host header in the request) in the request context.
func MustRequestID ¶
func MustRequestID(ctx context.Context) identifier.Identifier
MustRequestID returns the request identifier from context ctx or panics if the request identifier is not stored in the context.
Note, Waf service always stores the request identifier in the request context.
func RequestID ¶
func RequestID(ctx context.Context) (identifier.Identifier, bool)
RequestID returns the request identifier from context ctx and true if the request identifier is stored in the context.
Note, Waf service always stores the request identifier in the request context.
Types ¶
type Handler ¶
type Handler func(http.ResponseWriter, *http.Request, Params)
Handler type defines Waf router handler function signature.
The function signature is similar to http.HandlerFunc, but has additional parameter with Params parsed from the request URL path.
type MethodNotAllowedError ¶ added in v0.5.0
type MethodNotAllowedError struct {
Allow []string
}
func (*MethodNotAllowedError) Error ¶ added in v0.5.0
func (*MethodNotAllowedError) Error() string
type Params ¶
Params are parsed from the request URL path based on the matched route. Map keys are parameter names.
type Route ¶
type Route struct { // Name of the route. It should be unique. Name string `json:"name"` // Path for the route. It can contain parameters. Path string `json:"path"` // Does this route support API handlers. // API paths are automatically prefixed with /api. API bool `json:"api,omitempty"` // Does this route have a non-API handler. Get bool `json:"get,omitempty"` }
Route is a high-level route definition which is used by a service to register handlers with the router. It can also be used by Vue Router to register routes there.
type Router ¶
type Router struct { // NotFound is called if no route matches URL path. // If not defined, the request is replied with the 404 (not found) HTTP code error. NotFound func(http.ResponseWriter, *http.Request) // MethodNotAllowed is called the route does not support used HTTP method. // If not defined, the request is replied with the 405 (method not allowed) HTTP code error. MethodNotAllowed func(http.ResponseWriter, *http.Request, Params, []string) // Panic is called if handler panics instead of returning. // If not defined, panics propagate. Panic func(w http.ResponseWriter, req *http.Request, err interface{}) // EncodeQuery allows customization of how query strings are encoded // when reversing a route in Reverse and ReverseAPI methods. EncodeQuery func(qs url.Values) string // contains filtered or unexported fields }
Router calls handlers for HTTP requests based on URL path and HTTP method.
The goal of the router is to match routes in the same way as Vue Router. In addition, it supports also API handlers matched on HTTP method. API handlers share the same route name but have their path automatically prefixed with /api.
func (*Router) Handle ¶
Handle registers the route handler with route name at path and with HTTP method.
Path can contain parameters which start with ":". E.g., "/post/:id" is a path with one parameter "id". Those parameters are parsed from the request URL and passed to handlers.
Routes are matched in the order in which they are registered.
Non-API handlers can use only GET HTTP method, which is used also for HEAD HTTP method automatically.
Route is identified with the name and can have only one path associated with it, but it can have different handlers for different HTTP methods and can have both API and non-API handlers. Path for API handlers is automatically prefixed with /api, so you must not prefix it yourself.
func (*Router) Reverse ¶
Reverse constructs the path and query string portion of an URL based on the route name, Params, and query string values.
func (*Router) ReverseAPI ¶
ReverseAPI constructs the path and query string portion of an URL for API calls based on the route name, Params, and query string values.
func (*Router) ServeHTTP ¶
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)
ServeHTTP matches the route for the given request based on URL path, extracts Params from the path, and calls route's handler for the HTTP method.
If no route matches URL path, NotFound is called, if defined, or the request is replied with the 404 (not found) HTTP code error.
If the route does not support used HTTP method, MethodNotAllowed is called, if defined, or the request is replied with the 405 (method not allowed) HTTP code error.
type Server ¶
type Server[SiteT hasSite] struct { // Logger to be used by the server. Logger zerolog.Logger `kong:"-" yaml:"-"` // Run in development mode and proxy unknown requests. Development bool `help:"Run in development mode and proxy unknown requests." short:"d" yaml:"development"` // Base URL to proxy to in development mode. ProxyTo string `` /* 147-byte string literal not displayed */ // TLS configuration. TLS TLS `embed:"" prefix:"tls." yaml:"tls"` // Used primarily for testing. Addr string `json:"-" kong:"-" yaml:"-"` // contains filtered or unexported fields }
Server listens to HTTP/1.1 and HTTP2 requests on TLS enabled port 8080 and serves requests using the provided handler. Server is production ready and can be exposed directly on open Internet.
Certificates for TLS can be provided as files (which are daily reread to allow updating them) or can be automatically obtained (and updated) using Let's Encrypt (when running accessible from the Internet).
func (*Server[SiteT]) InDevelopment ¶
InDevelopment returns ProxyTo base URL if Development is true. Otherwise it returns an empty string.
func (*Server[SiteT]) Init ¶
Init determines the set of sites based on TLS configuration and sites provided, returning possibly updated and expanded set of sites.
If sites parameter is empty, sites are determined from domain names found in TLS certificates. If sites are provided and TLS certificates are not, their domains are used to obtain the necessary certificate from Let's Encrypt.
Key in sites map must match site's domain.
func (*Server[SiteT]) ListenAddr ¶
ListenAddr returns the address on which the server is listening.
Available only after the server runs. It blocks until the server runs if called before. If server fails to start before the address is obtained, it unblocks and returns an empty string.
type Service ¶
type Service[SiteT hasSite] struct { // General logger for the service. Logger zerolog.Logger // Canonical log line logger for the service which logs one log entry per // request. It is automatically populated with data about the request. CanonicalLogger zerolog.Logger // WithContext is a function which adds to the context a logger. // It is then accessible using zerolog.Ctx(ctx). // The first function is called when the request is handled and allows // any cleanup necessary. The second function is called on panic. // If WithContext is not set, Logger is used instead. WithContext func(context.Context) (context.Context, func(), func()) // StaticFiles to be served by the service. All paths are anchored at / when served. // HTML files (those with ".html" extension) are rendered using html/template // with site struct as data. Other files are served as-is. StaticFiles fs.ReadFileFS // Routes to be handled by the service and mapped to its Handler methods. Routes []Route // Sites configured for the service. Key in the map must match site's domain. // This should generally be set to sites returned from Server.Init method. Sites map[string]SiteT // Middleware is a chain of additional middleware to append before the router. Middleware []func(http.Handler) http.Handler // SiteContextPath is the path at which site context (JSON of site struct) // should be added to static files. SiteContextPath string // MetadataHeaderPrefix is an optional prefix to the Metadata response header. MetadataHeaderPrefix string // Development is a base URL to proxy to during development, if set. // This should generally be set to result of Server.InDevelopment method. // If set, StaticFiles are not served by the service so that they can be proxied instead. Development string // IsImmutableFile should return true if the static file is immutable and // should have such caching headers. Static files are those which do not change // during a runtime of the program. Immutable files are those which are never changed. IsImmutableFile func(path string) bool // SkipServingFile should return true if the static file should not be automatically // registered with the router to be served. It can still be served using ServeStaticFile. SkipServingFile func(path string) bool // contains filtered or unexported fields }
Service defines the application logic for your service.
You should embed the Service struct inside your service struct on which you define handlers as methods with Handler signature. Handlers together with StaticFiles, Routes and Sites define how should the service handle HTTP requests.
func (*Service[SiteT]) AddMetadata ¶
func (s *Service[SiteT]) AddMetadata(w http.ResponseWriter, req *http.Request, metadata map[string]interface{}) ([]byte, errors.E)
AddMetadata adds header with metadata to the response.
Metadata is encoded based on RFC 8941. Header name is "Metadata" with optional MetadataHeaderPrefix.
func (*Service[SiteT]) BadRequest ¶
func (s *Service[SiteT]) BadRequest(w http.ResponseWriter, req *http.Request)
BadRequest replies to the request with the 400 (bad request) HTTP code and the corresponding error message.
It does not otherwise end the request; the caller should ensure no further writes are done to w.
func (*Service[SiteT]) BadRequestWithError ¶
func (s *Service[SiteT]) BadRequestWithError(w http.ResponseWriter, req *http.Request, err errors.E)
BadRequestWithError replies to the request with the 400 (bad request) HTTP code and the corresponding error message. Error err is logged to the canonical log line.
As a special case, if err is context.Canceled or context.DeadlineExceeded it instead replies with the 408 (request timeout) HTTP code, the corresponding error message, and logs to the canonical log line that the context has been canceled or that deadline exceeded, respectively.
It does not otherwise end the request; the caller should ensure no further writes are done to w.
func (*Service[SiteT]) InternalServerError ¶
func (s *Service[SiteT]) InternalServerError(w http.ResponseWriter, req *http.Request)
InternalServerError replies to the request with the 500 (internal server error) HTTP code and the corresponding error message.
It does not otherwise end the request; the caller should ensure no further writes are done to w.
func (*Service[SiteT]) InternalServerErrorWithError ¶
func (s *Service[SiteT]) InternalServerErrorWithError(w http.ResponseWriter, req *http.Request, err errors.E)
InternalServerErrorWithError replies to the request with the 500 (internal server error) HTTP code and the corresponding error message. Error err is logged to the canonical log line.
As a special case, if err is context.Canceled or context.DeadlineExceeded it instead replies with the 408 (request timeout) HTTP code, the corresponding error message, and logs to the canonical log line that the context has been canceled or that deadline exceeded, respectively.
It does not otherwise end the request; the caller should ensure no further writes are done to w.
func (*Service[SiteT]) MethodNotAllowed ¶
MethodNotAllowed replies to the request with the 405 (method not allowed) HTTP code and the corresponding error message. It adds Allow response header based on the list of allowed methods in allow.
It does not otherwise end the request; the caller should ensure no further writes are done to w.
func (*Service[SiteT]) NotFound ¶
func (s *Service[SiteT]) NotFound(w http.ResponseWriter, req *http.Request)
NotFound replies to the request with the 404 (not found) HTTP code and the corresponding error message.
It does not otherwise end the request; the caller should ensure no further writes are done to w.
func (*Service[SiteT]) Proxy ¶
func (s *Service[SiteT]) Proxy(w http.ResponseWriter, req *http.Request)
Proxy proxies request to the development backend (e.g., Vite).
func (*Service[SiteT]) RedirectToMainSite ¶ added in v0.4.0
RedirectToMainSite is a middleware which redirects all requests to the site with mainDomain if they are made for another site on non-main domain.
func (*Service[SiteT]) ReverseAPI ¶
Reverse calls router's ReverseAPI.
func (*Service[SiteT]) RouteWith ¶
RouteWith registers static files and handlers with the router based on Routes and service Handler methods and returns a http.Handler to be used with the Server.
You should generally pass your service struct with embedded Service struct as service parameter so that handler methods can be detected. Non-API handler methods should have the same name as the route. While API handler methods should have the name matching the route name with HTTP method name as suffix (e.g., "CommentPost" for route with name "Comment" and POST HTTP method).
func (*Service[SiteT]) ServeStaticFile ¶
ServeStaticFile replies to the request by serving the file at path from service's static files.
It does not otherwise end the request; the caller should ensure no further writes are done to w.
func (*Service[SiteT]) TemporaryRedirectGetMethod ¶ added in v0.2.0
func (s *Service[SiteT]) TemporaryRedirectGetMethod(w http.ResponseWriter, req *http.Request, location string)
TemporaryRedirectGetMethod redirects the client to a new URL with the 303 (see other) HTTP code which makes the client do the request to a new location with the GET method.
func (*Service[SiteT]) TemporaryRedirectSameMethod ¶ added in v0.2.0
func (s *Service[SiteT]) TemporaryRedirectSameMethod(w http.ResponseWriter, req *http.Request, location string)
TemporaryRedirectSameMethod redirects the client to a new URL with the 307 (temporary redirect) HTTP code which makes the client redo the request to a new location with the same method and body.
func (*Service[SiteT]) WithError ¶ added in v0.5.0
WithError logs err to the canonical log line.
As a special case, if err is context.Canceled or context.DeadlineExceeded it logs to the canonical log line that the context has been canceled or that deadline exceeded, respectively.
func (*Service[SiteT]) WriteJSON ¶
func (s *Service[SiteT]) WriteJSON(w http.ResponseWriter, req *http.Request, data interface{}, metadata map[string]interface{})
WriteJSON replies to the request by writing data as JSON.
Optional metadata is added as the response header.
Besides other types, data can be of type []byte and json.RawMessage in which case it is expected that it already contains a well-formed JSON and is written as-is.
It does not otherwise end the request; the caller should ensure no further writes are done to w.
type Site ¶
type Site struct { Domain string `json:"domain" yaml:"domain"` // Certificate file path for the site. It should be valid for the domain. // Used when Let's Encrypt is not configured. CertFile string `json:"-" yaml:"cert,omitempty"` // Key file path. Used when Let's Encrypt is not configured. KeyFile string `json:"-" yaml:"key,omitempty"` // contains filtered or unexported fields }
Site describes the site at a domain.
A service can have multiple sites which share static files and handlers, but have different configuration and rendered HTML files. Core such configuration is site's domain, but you can provide your own site struct and embed Site to add additional configuration. Your site struct is then used when rendering HTML files and as site context to the frontend at SiteContextPath URL path.
Certificate and key file paths are not exposed in site context JSON.
type TLS ¶
type TLS struct { // Default certificate for TLS, when not using Let's Encrypt. CertFile string `` /* 164-byte string literal not displayed */ // Default certificate's private key, when not using Let's Encrypt. KeyFile string `` /* 168-byte string literal not displayed */ // Contact e-mail to use with Let's Encrypt. Email string `group:"Let's Encrypt:" help:"Contact e-mail to use with Let's Encrypt." short:"E" yaml:"email"` // Let's Encrypt's cache directory. Cache string `` /* 174-byte string literal not displayed */ // Used primarily for testing. ACMEDirectory string `json:"-" kong:"-" yaml:"-"` ACMEDirectoryRootCAs string `json:"-" kong:"-" yaml:"-"` }
TLS configuration used by the server.