Go API Documentation

github.com/caddyserver/caddy/v2/modules/caddyhttp/push

No package summary is available.

Package

Files: 3. Third party imports: 2. Imports from organisation: 0. Tests: 0. Benchmarks: 0.

Constants

const (
	comma		= ","
	semicolon	= ";"
	equal		= "="
)
// pushHeader is a header field that gets added to push requests
// in order to avoid recursive/infinite pushes.
const pushHeader = "Caddy-Push"
// pushedLink is the key for the variable on the request
// context that we use to remember whether we have already
// pushed resources from Link headers yet; otherwise, if
// multiple push handlers are invoked, it would repeat the
// pushing of Link headers.
const pushedLink = "http.handlers.push.pushed_link"

Vars

var (
	_	caddy.Provisioner		= (*Handler)(nil)
	_	caddyhttp.MiddlewareHandler	= (*Handler)(nil)
	_	http.ResponseWriter		= (*linkPusher)(nil)
	_	http.Pusher			= (*linkPusher)(nil)
)

safeHeaders is a list of header fields that are safe to copy to push requests implicitly. It is assumed that requests for certain kinds of content would fail without these fields present.

var safeHeaders = []string{
	"Accept-Encoding",
	"Accept-Language",
	"Accept",
	"Cache-Control",
	"User-Agent",
}

Types

Handler

Handler is a middleware for HTTP/2 server push. Note that HTTP/2 server push has been deprecated by some clients and its use is discouraged unless you can accurately predict which resources actually need to be pushed to the client; it can be difficult to know what the client already has cached. Pushing unnecessary resources results in worse performance. Consider using HTTP 103 Early Hints instead.

This handler supports pushing from Link headers; in other words, if the eventual response has Link headers, this handler will push the resources indicated by those headers, even without specifying any resources in its config.

type Handler struct {
	// The resources to push.
	Resources	[]Resource	`json:"resources,omitempty"`

	// Headers to modify for the push requests.
	Headers	*HeaderConfig	`json:"headers,omitempty"`

	logger	*zap.Logger
}

HeaderConfig

HeaderConfig configures headers for synthetic push requests.

type HeaderConfig struct {
	headers.HeaderOps
}

Resource

Resource represents a request for a resource to push.

type Resource struct {
	// Method is the request method, which must be GET or HEAD.
	// Default is GET.
	Method	string	`json:"method,omitempty"`

	// Target is the path to the resource being pushed.
	Target	string	`json:"target,omitempty"`
}

linkPusher

linkPusher is a http.ResponseWriter that intercepts the WriteHeader() call to ensure that any resources described by Link response headers get pushed before the response is allowed to be written.

type linkPusher struct {
	*caddyhttp.ResponseWriterWrapper
	handler	Handler
	pusher	http.Pusher
	header	http.Header
	request	*http.Request
}

linkResource

linkResource contains the results of a parsed Link header.

type linkResource struct {
	uri	string
	params	map[string]string
}

Functions

func (*Handler) Provision

Provision sets up h.

func (h *Handler) Provision(ctx caddy.Context) error {
	h.logger = ctx.Logger()
	if h.Headers != nil {
		err := h.Headers.Provision(ctx)
		if err != nil {
			return fmt.Errorf("provisioning header operations: %v", err)
		}
	}
	return nil
}

Cognitive complexity: 4, Cyclomatic complexity: 3

Uses: fmt.Errorf.

func (Handler) CaddyModule

CaddyModule returns the Caddy module information.

func (Handler) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID:	"http.handlers.push",
		New:	func() caddy.Module { return new(Handler) },
	}
}

Cognitive complexity: 2, Cyclomatic complexity: 1

func (Handler) ServeHTTP

func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
	pusher, ok := w.(http.Pusher)
	if !ok {
		return next.ServeHTTP(w, r)
	}

	// short-circuit recursive pushes
	if _, ok := r.Header[pushHeader]; ok {
		return next.ServeHTTP(w, r)
	}

	repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
	server := r.Context().Value(caddyhttp.ServerCtxKey).(*caddyhttp.Server)
	shouldLogCredentials := server.Logs != nil && server.Logs.ShouldLogCredentials

	// create header for push requests
	hdr := h.initializePushHeaders(r, repl)

	// push first!
	for _, resource := range h.Resources {
		if c := h.logger.Check(zapcore.DebugLevel, "pushing resource"); c != nil {
			c.Write(
				zap.String("uri", r.RequestURI),
				zap.String("push_method", resource.Method),
				zap.String("push_target", resource.Target),
				zap.Object("push_headers", caddyhttp.LoggableHTTPHeader{
					Header:			hdr,
					ShouldLogCredentials:	shouldLogCredentials,
				}),
			)
		}
		err := pusher.Push(repl.ReplaceAll(resource.Target, "."), &http.PushOptions{
			Method:	resource.Method,
			Header:	hdr,
		})
		if err != nil {
			// usually this means either that push is not
			// supported or concurrent streams are full
			break
		}
	}

	// wrap the response writer so that we can initiate push of any resources
	// described in Link header fields before the response is written
	lp := linkPusher{
		ResponseWriterWrapper:	&caddyhttp.ResponseWriterWrapper{ResponseWriter: w},
		handler:		h,
		pusher:			pusher,
		header:			hdr,
		request:		r,
	}

	// serve only after pushing!
	if err := next.ServeHTTP(lp, r); err != nil {
		return err
	}

	return nil
}

Cognitive complexity: 17, Cyclomatic complexity: 8

Uses: caddyhttp.LoggableHTTPHeader, caddyhttp.ResponseWriterWrapper, caddyhttp.Server, caddyhttp.ServerCtxKey, http.PushOptions, http.Pusher, zap.Object, zap.String, zapcore.DebugLevel.

func (linkPusher) WriteHeader

func (lp linkPusher) WriteHeader(statusCode int) {
	if links, ok := lp.ResponseWriter.Header()["Link"]; ok {
		// only initiate these pushes if it hasn't been done yet
		if val := caddyhttp.GetVar(lp.request.Context(), pushedLink); val == nil {
			if c := lp.handler.logger.Check(zapcore.DebugLevel, "pushing Link resources"); c != nil {
				c.Write(zap.Strings("linked", links))
			}
			caddyhttp.SetVar(lp.request.Context(), pushedLink, true)
			lp.handler.servePreloadLinks(lp.pusher, lp.header, links)
		}
	}
	lp.ResponseWriter.WriteHeader(statusCode)
}

Cognitive complexity: 6, Cyclomatic complexity: 4

Uses: caddyhttp.GetVar, caddyhttp.SetVar, zap.Strings, zapcore.DebugLevel.

Private functions

func init

init ()
References: httpcaddyfile.RegisterHandlerDirective.

func isRemoteResource

isRemoteResource returns true if resource starts with a scheme or is a protocol-relative URI.

isRemoteResource (resource string) bool
References: strings.HasPrefix.

func parseCaddyfile

parseCaddyfile sets up the push handler. Syntax:

push [<matcher>] [<resource>] {
    [GET|HEAD] <resource>
    headers {
        [+]<field> [<value|regexp> [<replacement>]]
        -<field>
    }
}

A single resource can be specified inline without opening a block for the most common/simple case. Or, a block can be opened and multiple resources can be specified, one per line, optionally preceded by the method. The headers subdirective can be used to customize the headers that are set on each (synthetic) push request, using the same syntax as the 'header' directive for request headers. Placeholders are accepted in resource and header field name and value and replacement tokens.

parseCaddyfile (h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
References: headers.CaddyfileHeaderOp.

func parseLinkHeader

parseLinkHeader is responsible for parsing Link header and returning list of found resources.

Accepted formats are:

Link: <resource>; as=script
Link: <resource>; as=script,<resource>; as=style
Link: <resource>;<resource2>

where <resource> begins with a forward slash (/).

parseLinkHeader (header string) []linkResource
References: strings.Cut, strings.Index, strings.Split, strings.TrimSpace.

func initializePushHeaders

initializePushHeaders (r *http.Request, repl *caddy.Replacer) http.Header
References: http.Header.

servePreloadLinks parses Link headers from upstream and pushes resources described by them. If a resource has the "nopush" attribute or describes an external entity (meaning, the resource URI includes a scheme), it will not be pushed.

servePreloadLinks (pusher http.Pusher, hdr http.Header, resources []string)
References: http.PushOptions.


Tests

Files: 1. Third party imports: 0. Imports from organisation: 0. Tests: 1. Benchmarks: 0.

Test functions

TestParseLinkHeader

References: reflect.DeepEqual.