middleware

package
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Jun 15, 2021 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package middleware provides flexibility at the HTTP request/response process. For this purpose, the Adapter pattern is used, which consist in wrapping handlers. An adapter may run code before and/or after the handler it is wrapping.

func MyAdapter(h http.Handler) http.Handler {
	nh := func(w http.ResponseWriter, r *http.Request) {
		// Code that run before
		h.ServeHTTP(w, r) // Breaks the flow if not used
		// Code that run after
	}

	return http.HandlerFunc(nh)
}

When multiple adapters are used, the result is a sequence of wrappers and its execution flow depends in the order that adapters were given.

Adapt(h, f1, f2, f3)

1. f1 before code

2. f2 before code

3. f3 before code

4. h

5. f3 after code

6. f2 after code

7. f1 after code

Some adapters my require to change the behavior of the ResponseWriter. Since the underlying type of a ResponseWriter from the stdlib implements more than this interface (which is a bad design decision), simply wrapping it with a custom type will hide other interface implementations. This leads to several side effects during request processing and makes some middleware completely unusable.

To solve this, AdaptResponseWriter must be used. Supported interfaces are:

* http.Flusher

* io.ReaderFrom

Unsupported interfaces are:

* http.CloseNotifier: this was deprecated in favor of Request.Context.

* http.Hijacker: I don't like Websockets (or the way they are implemented) and hijacking HTTP is a hacky workaround (also, ResponseWriters for HTTP versions higher than 1.1 don't implement this interface).

* http.Pusher: web browsers are deprecating HTTP/2 Server Push.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Adapt

func Adapt(h http.Handler, a ...Adapter) http.Handler

Adapt wraps a http.Handler into a list of Adapters. Takes a http.Handler and a list of Adapters, which will be wrapped from right to left (and ran left to right).

Example
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"

	"go.ntrrg.dev/ntgo/net/http/middleware"
)

func main() {
	h := middleware.Adapt(
		http.FileServer(http.Dir(".")),
		middleware.Cache("max-age=3600, s-max-age=3600"),
		middleware.Gzip(-1),
	)

	// http.Handle("/", h)

	w := httptest.NewRecorder()
	r := httptest.NewRequest(http.MethodGet, "/", nil)
	r.Header.Set("Accept-Encoding", "gzip")
	h.ServeHTTP(w, r)

	res := w.Result()
	defer res.Body.Close()

	fmt.Printf("Status: %v\n", res.Status)
	fmt.Printf("Cache-Control: %+v\n", res.Header.Get("Cache-Control"))
	fmt.Printf("Content-Encoding: %v", res.Header.Get("Content-Encoding"))
}
Output:

Status: 200 OK
Cache-Control: max-age=3600, s-max-age=3600
Content-Encoding: gzip

func AdaptFunc

func AdaptFunc(
	h func(w http.ResponseWriter, r *http.Request),
	a ...Adapter,
) http.Handler

AdaptFunc works as Adapt but for http.HandlerFunc.

Example
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"

	"go.ntrrg.dev/ntgo/net/http/middleware"
)

func main() {
	h := middleware.AdaptFunc(
		func(w http.ResponseWriter, r *http.Request) {},
		middleware.JSONResponse(),
		middleware.Cache("max-age=3600, s-max-age=3600"),
	)

	// http.Handle("/", h)

	w := httptest.NewRecorder()
	r := httptest.NewRequest(http.MethodGet, "/", nil)
	h.ServeHTTP(w, r)

	res := w.Result()
	defer res.Body.Close()

	fmt.Printf("Status: %v\n", res.Status)
	fmt.Printf("Cache-Control: %+v\n", res.Header.Get("Cache-Control"))
	fmt.Printf("Content-Type: %v", res.Header.Get("Content-Type"))
}
Output:

Status: 200 OK
Cache-Control: max-age=3600, s-max-age=3600
Content-Type: application/json; charset=utf-8

func AdaptResponseWriter added in v0.7.0

func AdaptResponseWriter(
	w http.ResponseWriter,
	m ResponseWriterMethods,
) http.ResponseWriter

AdaptResponseWriter wraps a ResponseWriter with custom methods. This allows to overwrite only specific methods without compromising other interface implementations.

This technique is based on https://github.com/felixge/httpsnoop.

func IsAdaptedResponseWriter added in v0.7.0

func IsAdaptedResponseWriter(w http.ResponseWriter) bool

IsAdaptedResponseWriter reports if the given http.ResponseWriter were adapted previously.

Types

type Adapter

type Adapter func(http.Handler) http.Handler

Adapter is a wrapper for http.Handler. Takes a http.Handler as argument and creates a new one that may run code before and/or after calling the given handler.

func AddHeader

func AddHeader(key, value string) Adapter

AddHeader creates/appends a HTTP header before calling the http.Handler.

func Cache

func Cache(directives string) Adapter

Cache sets HTTP cache headers for GET requests.

Supported directives:

* public/private: whether the cached response is for any or a specific user.

* max-age=TIME: cache life time in seconds. The maximum value is 1 year.

* s-max-age=TIME: same as max-age, but this one has effect in proxies.

* must-revalidate: force expired cached response revalidation, even in special circumstances (like slow connections, were cached responses are used even after they had expired).

* proxy-revalidate: same as must-revalidate, but this one has effect in proxies.

* no-cache: disables cache.

* no-store: disables cache, even in proxies.

func DelHeader

func DelHeader(key string) Adapter

DelHeader removes a HTTP header before calling the http.Handler.

func Gzip

func Gzip(level int) Adapter

Gzip compresses the response body. The compression level is given as an integer value according to the compress/flate package.

func JSONRequest

func JSONRequest() Adapter

JSONRequest checks that request has the appropriate HTTP method and the appropriate 'Content-Type' header. Responds with http.StatusMethodNotAllowed if the used method is not one of POST, PUT or PATCH. Responds with http.StatusUnsupportedMediaType if the 'Content-Type' header is not valid.

func JSONResponse

func JSONResponse() Adapter

JSONResponse prepares the response to be a JSON response.

func Replace

func Replace(old, n string) Adapter

Replace replaces old by new from the request URL.

func SetHeader

func SetHeader(key, value string) Adapter

SetHeader creates/replaces a HTTP header before calling the http.Handler.

func StripPrefix

func StripPrefix(prefix string) Adapter

StripPrefix strips the given prefix from the request URL.

type ResponseWriteAdapter added in v0.7.0

type ResponseWriteAdapter interface {
	// Unwrap allows access to the underlying http.ResponseWriter.
	Unwrap() http.ResponseWriter
}

A ResponseWriteAdapter represents an adapted http.ResponseWriter.

type ResponseWriterMethods added in v0.7.0

type ResponseWriterMethods struct {
	// http.ResponseWriter
	Header      func() http.Header
	Write       func([]byte) (int, error)
	WriteHeader func(int)

	// http.Flusher
	Flush func()

	// io.ReaderFrom
	ReadFrom func(io.Reader) (int64, error)
}

ResponseWriterMethods is a set of methods used to wrap a http.ReponseWriter.

Jump to

Keyboard shortcuts

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