Documentation ¶
Overview ¶
Package middleware provides helpful functions that implement some common functionalities in http servers. A middleware is a function that takes in a http.Handler as one of its arguments and returns a http.Handler
The middlewares All, Get, Post, Head, Put & Delete wrap other internal middleware. The effect of this is that the aforementioned middleware, in addition to their specialised functionality, will:
- Add logID for traceability.
- Add the "real" client IP address to the request context.
- Add client TLS fingerprint to the request context.
- Recover from panics in the wrappedHandler.
- Log http requests and responses.
- Rate limit requests by IP address.
- Shed load based on http response latencies.
- Handle automatic procurement/renewal of ACME tls certificates.
- Redirect http requests to https.
- Add some important HTTP security headers and assign them sensible default values.
- Implement Cross-Origin Resource Sharing support(CORS).
- Provide protection against Cross Site Request Forgeries(CSRF).
- Attempt to provide protection against form re-submission when a user reloads an already submitted web form.
- Implement http sessions.
Example (GetCspNonce) ¶
package main import ( "context" "fmt" "net/http" "os" "github.com/komuw/ong/log" "github.com/komuw/ong/middleware" ) func loginHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { cspNonce := middleware.GetCspNonce(r.Context()) _ = cspNonce _, _ = fmt.Fprint(w, "welcome to your favorite website.") } } func main() { l := log.New(os.Stdout, 100)(context.Background()) handler := middleware.Get( loginHandler(), middleware.WithOpts("example.com", 443, "secretKey", middleware.DirectIpStrategy, l), ) _ = handler // use handler }
Output:
Example (GetCsrfToken) ¶
package main import ( "context" "fmt" "net/http" "os" "github.com/komuw/ong/log" "github.com/komuw/ong/middleware" ) func welcomeHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { csrfToken := middleware.GetCsrfToken(r.Context()) _ = csrfToken _, _ = fmt.Fprint(w, "welcome.") } } func main() { l := log.New(os.Stdout, 100)(context.Background()) handler := middleware.Get( welcomeHandler(), middleware.WithOpts("example.com", 443, "secretKey", middleware.DirectIpStrategy, l), ) _ = handler // use handler }
Output:
Index ¶
- Constants
- func All(wrappedHandler http.Handler, o Opts) http.HandlerFunc
- func BasicAuth(wrappedHandler http.Handler, user, passwd string) http.HandlerFunc
- func ClientFingerPrint(r *http.Request) string
- func ClientIP(r *http.Request) string
- func Delete(wrappedHandler http.Handler, o Opts) http.HandlerFunc
- func Get(wrappedHandler http.Handler, o Opts) http.HandlerFunc
- func GetCspNonce(c context.Context) string
- func GetCsrfToken(c context.Context) string
- func Head(wrappedHandler http.Handler, o Opts) http.HandlerFunc
- func Post(wrappedHandler http.Handler, o Opts) http.HandlerFunc
- func Put(wrappedHandler http.Handler, o Opts) http.HandlerFunc
- type ClientIPstrategy
- type Opts
Examples ¶
Constants ¶
const ( // DirectIpStrategy derives the client IP from [http.Request.RemoteAddr]. // It should be used if the server accepts direct connections, rather than through a proxy. // // See the warning in [ClientIP] DirectIpStrategy = ClientIPstrategy("DirectIpStrategy") // LeftIpStrategy derives the client IP from the leftmost valid & non-private IP address in the `X-Fowarded-For` or `Forwarded` header. // // See the warning in [ClientIP] LeftIpStrategy = ClientIPstrategy("LeftIpStrategy") // RightIpStrategy derives the client IP from the rightmost valid & non-private IP address in the `X-Fowarded-For` or `Forwarded` header. // // See the warning in [ClientIP] RightIpStrategy = ClientIPstrategy("RightIpStrategy") // ProxyStrategy derives the client IP from the [PROXY protocol v1]. // This should be used when your application is behind a TCP proxy that uses the v1 PROXY protocol. // // See the warning in [ClientIP] // // [PROXY protocol v1]: https://www.haproxy.org/download/2.8/doc/proxy-protocol.txt ProxyStrategy = ClientIPstrategy("ProxyStrategy") )
const ( // CsrfTokenFormName is the name of the html form name attribute for csrf token. CsrfTokenFormName = "csrftoken" // named after what django uses. // CsrfHeader is the name of the http header that Ong uses to store csrf token. CsrfHeader = "X-Csrf-Token" // named after what fiber uses. )
Variables ¶
This section is empty.
Functions ¶
func All ¶
func All(wrappedHandler http.Handler, o Opts) http.HandlerFunc
All is a middleware that allows all http methods.
See the package documentation for the additional functionality provided by this middleware.
Example ¶
package main import ( "context" "io" "net/http" "os" "github.com/komuw/ong/log" "github.com/komuw/ong/middleware" ) func main() { l := log.New(os.Stdout, 100)(context.Background()) opts := middleware.WithOpts("example.com", 443, "secretKey", middleware.DirectIpStrategy, l) myHandler := http.HandlerFunc( func(w http.ResponseWriter, _ *http.Request) { _, _ = io.WriteString(w, "Hello from a HandleFunc \n") }, ) handler := middleware.All(myHandler, opts) mux := http.NewServeMux() mux.Handle("/", handler) }
Output:
func BasicAuth ¶
func BasicAuth(wrappedHandler http.Handler, user, passwd string) http.HandlerFunc
BasicAuth is a middleware that protects wrappedHandler using basic authentication.
func ClientFingerPrint ¶ added in v0.0.44
ClientFingerPrint returns the TLS fingerprint of the client. It is provided on a best-effort basis. If a fingerprint is not found, it returns a string that has the substring "NotFound" in it. There are different formats/algorithms of fingerprinting, this library(by design) does not subscribe to a particular format or algorithm.
func ClientIP ¶ added in v0.0.26
ClientIP returns the "real" client IP address. This will be based on the ClientIPstrategy that you chose.
Warning: This should be used with caution. Clients CAN easily spoof IP addresses. Fetching the "real" client is done in a best-effort basis and can be grossly inaccurate & precarious. You should especially heed this warning if you intend to use the IP addresses for security related activities. Proceed at your own risk.
func Delete ¶
func Delete(wrappedHandler http.Handler, o Opts) http.HandlerFunc
Delete is a middleware that only allows http DELETE requests and http OPTIONS requests.
See the package documentation for the additional functionality provided by this middleware.
func Get ¶
func Get(wrappedHandler http.Handler, o Opts) http.HandlerFunc
Get is a middleware that only allows http GET requests and http OPTIONS requests.
See the package documentation for the additional functionality provided by this middleware.
Example ¶
package main import ( "context" "fmt" "net/http" "os" "github.com/komuw/ong/log" "github.com/komuw/ong/middleware" ) func loginHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { cspNonce := middleware.GetCspNonce(r.Context()) _ = cspNonce _, _ = fmt.Fprint(w, "welcome to your favorite website.") } } func main() { l := log.New(os.Stdout, 100)(context.Background()) opts := middleware.WithOpts("example.com", 443, "secretKey", middleware.DirectIpStrategy, l) handler := middleware.Get(loginHandler(), opts) _ = handler // use handler }
Output:
func GetCspNonce ¶
GetCspNonce returns the Content-Security-Policy nonce that was set for the http request in question.
func GetCsrfToken ¶
GetCsrfToken returns the csrf token that was set for the http request in question.
func Head ¶
func Head(wrappedHandler http.Handler, o Opts) http.HandlerFunc
Head is a middleware that only allows http HEAD requests and http OPTIONS requests.
See the package documentation for the additional functionality provided by this middleware.
Types ¶
type ClientIPstrategy ¶ added in v0.0.26
type ClientIPstrategy string
ClientIPstrategy is a middleware option that describes the strategy to use when fetching the client's IP address.
func SingleIpStrategy ¶ added in v0.0.26
func SingleIpStrategy(headerName string) ClientIPstrategy
SingleIpStrategy derives the client IP from http header headerName.
headerName MUST NOT be either `X-Forwarded-For` or `Forwarded`. It can be something like `CF-Connecting-IP`, `Fastly-Client-IP`, `Fly-Client-IP`, etc; depending on your usecase.
See the warning in ClientIP
type Opts ¶
type Opts struct {
// contains filtered or unexported fields
}
Opts are the various parameters(optionals) that can be used to configure middlewares.
Use either New or WithOpts to get a valid Opts.
func New ¶ added in v0.0.14
func New( domain string, httpsPort uint16, allowedOrigins []string, allowedMethods []string, allowedHeaders []string, secretKey string, strategy ClientIPstrategy, l *slog.Logger, ) Opts
New returns a new Opts.
domain is the domain name of your website. It can be an exact domain, subdomain or wildcard. New panics if domain is invalid.
httpsPort is the tls port where http requests will be redirected to.
allowedOrigins, allowedMethods, & allowedHeaders are used by the CORS middleware. If allowedOrigins is nil, all origins are allowed. You can also use * to allow all. If allowedMethods is nil, "GET", "POST", "HEAD" are allowed. Use * to allow all. If allowedHeaders is nil, "Origin", "Accept", "Content-Type", "X-Requested-With" are allowed. Use * to allow all.
secretKey is used for securing signed data. It should be unique & kept secret. If it becomes compromised, generate a new one and restart your application using the new one.
strategy is the algorithm to use when fetching the client's IP address; see ClientIPstrategy. It is important to choose your strategy carefully, see the warning in ClientIP.
func WithOpts ¶
func WithOpts( domain string, httpsPort uint16, secretKey string, strategy ClientIPstrategy, l *slog.Logger, ) Opts
WithOpts returns a new Opts that has sensible defaults. See New for extra documentation.
Example ¶
package main import ( "os" "github.com/komuw/ong/middleware" "golang.org/x/exp/slog" ) func main() { l := slog.New(slog.NewTextHandler(os.Stdout, nil)) opts := middleware.WithOpts( "example.com", 443, "secretKey", // assuming your application is deployed behind cloudflare. middleware.SingleIpStrategy("CF-Connecting-IP"), l, ) _ = opts }
Output: