proxy

package
v0.16.100 Latest Latest
Warning

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

Go to latest
Published: Jun 12, 2023 License: Apache-2.0, MIT Imports: 43 Imported by: 14

Documentation

Overview

Package proxy implements an HTTP reverse proxy based on continuously updated skipper routing rules.

The proxy matches each incoming request to the lookup tree for the first matching route, and handles it accordingly to the rules defined in it. This typically means augmenting the request with the filters and forwarding it to the route endpoint. It may also mean to handle the request internally if it is a 'shunt' route or to continue routing further in case of 'loopback'.

Proxy Mechanism

1. route matching:

The incoming request is matched to the current routing tree, implemented in skipper/routing. The result may be a route, which will be used for forwarding or handling the request, or nil, in which case the proxy responds with a configured http status code (defaults to 404).

2. upstream request augmentation:

In case of a matched route, the request handling method of all filters in the route will be executed in the order they are defined. The filters share a context object, that provides the in-memory representation of the incoming request, the outgoing response writer, the path parameters derived from the actual request path (see skipper/routing) and a free-form state bag. The filters may modify the request or pass data to each other using the state bag.

Filters can break the filter chain, serving their own response object. This will prevent the request from reaching the route endpoint. The filters that are defined in the route after the one that broke the chain will never handle the request.

3.a upstream request:

The incoming and augmented request is mapped to an outgoing request and executed, addressing the endpoint defined by the current route.

If a filter chain was broken by some filter this step is skipped.

3.b shunt:

In case the route is a 'shunt', an empty response is created with default 404 status.

3.c loopback:

In case of the `loopback` route no upstream request is made. Routing will happen again with an augmented request. This can happen several times in a row until maxLoopbacks limit is reached.

3.d dynamic:

In case of a `dynamic` route, the final target must be defined in a filter.

4. downstream response augmentation:

The response handling method of all the filters processed in step 2 will be executed in reverse order. The filter context is the same instance as the one in step 2. It will include the response object from step 3, or the one provided by the filter that broke the chain.

If the route is a shunt route, one of the filters needs to handle the request latest in this phase. It should set the status and response headers and write the response body, if any, to the writer in the filter context.

5. response:

In case none of the filters handled the request, the response properties, including the status and the headers, are mapped to the outgoing response writer, and the response body is streamed to it, with continuous flushing.

Routing Rules

The route matching is implemented in the skipper/routing package. The routing rules are not static, but they can be continuously updated by new definitions originated in one or more data sources.

The only exceptions are the priority routes, that have not originated from the external data sources, and are tested against the requests before the general routing tree.

Handling the Host header

The default behavior regarding the 'Host' header of the proxy requests is that the proxy ignores the value set in the incoming request. This can be changed setting the `OptionsProxyPreserveHost` init flag.

In either case, it is possible to override this behavior individually for each route with the `preserveHost` filter. If the filter argument is "true", the request host is used, if it is "false", the backend host is used, regardless of the global setting.

If a filter sets the 'Host' header to a value other than the value in the incoming request or the backend host, this custom value will be used instead of the global setting or the route specific override.

To control the value of the outgoing 'Host' header, the `OutgoingHost()` and `SetOutgoingHost()` methods of the `FilterContext` need to be used instead of the `Request.Header` map.

Circuit Breakers

When configured, skipper can use circuit breakers for the backend requests. It asks the registry for a matching circuit breaker for every request, and if there is any, checks if it is closed before sending out the backend request. On connection errors and responses with a status code higher than 499, it reports a failure to the current breaker, otherwise it reports a success.

For details, see: https://godoc.org/github.com/zalando/skipper/circuit.

Proxy Example

The below example demonstrates creating a routing proxy as a standard http.Handler interface:

// create a target backend server. It will return the value of the 'X-Echo' request header
// as the response body:
targetServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte(r.Header.Get("X-Echo")))
}))

defer targetServer.Close()

// create a filter registry, and register the custom filter:
filterRegistry := builtin.MakeRegistry()
filterRegistry.Register(&setEchoHeader{})

// create a data client with a predefined route, referencing the filter and a path condition
// containing a wildcard called 'echo':
routeDoc := fmt.Sprintf(`Path("/return/:echo") -> setEchoHeader() -> "%s"`, targetServer.URL)
dataClient, err := testdataclient.NewDoc(routeDoc)
if err != nil {
	log.Fatal(err)
}

// create routing object:
rt := routing.New(routing.Options{
	FilterRegistry: filterRegistry,
	DataClients:    []routing.DataClient{dataClient}})
defer rt.Close()

// create a proxy instance, and start an http server:
proxy := proxy.New(rt, proxy.OptionsNone)
defer proxy.Close()

router := httptest.NewServer(proxy)
defer router.Close()

// make a request to the proxy:
rsp, err := http.Get(fmt.Sprintf("%s/return/Hello,+world!", router.URL))
if err != nil {
	log.Fatal(err)
}

defer rsp.Body.Close()

// print out the response:
if _, err := io.Copy(os.Stdout, rsp.Body); err != nil {
	log.Fatal(err)
}

// Output:
// Hello, world!

Index

Examples

Constants

View Source
const (

	// Number of loops allowed by default.
	DefaultMaxLoopbacks = 9

	// The default value set for http.Transport.MaxIdleConnsPerHost.
	DefaultIdleConnsPerHost = 64

	// The default period at which the idle connections are forcibly
	// closed.
	DefaultCloseIdleConnsPeriod = 20 * time.Second

	// DefaultResponseHeaderTimeout, the default response header timeout
	DefaultResponseHeaderTimeout = 60 * time.Second

	// DefaultExpectContinueTimeout, the default timeout to expect
	// a response for a 100 Continue request
	DefaultExpectContinueTimeout = 30 * time.Second
)
View Source
const (
	OptionsNone              = Options(FlagsNone)
	OptionsInsecure          = Options(Insecure)
	OptionsPreserveOriginal  = Options(PreserveOriginal)
	OptionsPreserveHost      = Options(PreserveHost)
	OptionsDebug             = Options(Debug)
	OptionsHopHeadersRemoval = Options(HopHeadersRemoval)
)
View Source
const (
	ClientRequestStateTag = "client.request"
	ComponentTag          = "component"
	ErrorTag              = "error"
	BlockTag              = "blocked"
	FlowIDTag             = "flow_id"
	HostnameTag           = "hostname"
	HTTPHostTag           = "http.host"
	HTTPMethodTag         = "http.method"
	HTTPRemoteIPTag       = "http.remote_ip"
	HTTPPathTag           = "http.path"
	HTTPUrlTag            = "http.url"
	HTTPStatusCodeTag     = "http.status_code"
	SkipperRouteIDTag     = "skipper.route_id"
	SpanKindTag           = "span.kind"

	ClientRequestCanceled = "canceled"
	SpanKindClient        = "client"
	SpanKindServer        = "server"

	EndEvent           = "end"
	StartEvent         = "start"
	StreamHeadersEvent = "stream_Headers"
	StreamBodyEvent    = "streamBody.byte"
	StreamBodyError    = "streamBody error"
)

Variables

View Source
var (
	ErrBlocked = errors.New("blocked string match found in body")
)

Functions

This section is empty.

Types

type Flags

type Flags uint

Flags control the behavior of the proxy.

const (
	FlagsNone Flags = 0

	// Insecure causes the proxy to ignore the verification of
	// the TLS certificates of the backend services.
	Insecure Flags = 1 << iota

	// PreserveOriginal indicates that filters require the
	// preserved original metadata of the request and the response.
	PreserveOriginal

	// PreserveHost indicates whether the outgoing request to the
	// backend should use by default the 'Host' header of the incoming
	// request, or the host part of the backend address, in case filters
	// don't change it.
	PreserveHost

	// Debug indicates that the current proxy instance will be used as a
	// debug proxy. Debug proxies don't forward the request to the
	// route backends, but they execute all filters, and return a
	// JSON document with the changes the filters make to the request
	// and with the approximate changes they would make to the
	// response.
	Debug

	// HopHeadersRemoval indicates whether the Hop Headers should be removed
	// in compliance with RFC 2616
	HopHeadersRemoval

	// PatchPath instructs the proxy to patch the parsed request path
	// if the reserved characters according to RFC 2616 and RFC 3986
	// were unescaped by the parser.
	PatchPath
)

func (Flags) Debug

func (f Flags) Debug() bool

When set, the proxy runs in debug mode.

func (Flags) HopHeadersRemoval added in v0.9.164

func (f Flags) HopHeadersRemoval() bool

When set, the proxy will remove the Hop Headers

func (Flags) Insecure

func (f Flags) Insecure() bool

When set, the proxy will skip the TLS verification on outgoing requests.

func (Flags) PreserveHost

func (f Flags) PreserveHost() bool

When set, the proxy will set the, by default, the Host header value of the outgoing requests to the one of the incoming request.

func (Flags) PreserveOriginal

func (f Flags) PreserveOriginal() bool

When set, the filters will receive an unmodified clone of the original incoming request and response.

type LockedBuffer added in v0.15.27

type LockedBuffer struct {
	// contains filtered or unexported fields
}

func NewLockedBuffer added in v0.15.27

func NewLockedBuffer() *LockedBuffer

func (*LockedBuffer) String added in v0.15.27

func (b *LockedBuffer) String() string

func (*LockedBuffer) Write added in v0.15.27

func (b *LockedBuffer) Write(p []byte) (int, error)

type OpenTracingParams added in v0.10.234

type OpenTracingParams struct {
	// Tracer holds the tracer enabled for this proxy instance
	Tracer ot.Tracer

	// InitialSpan can override the default initial, pre-routing, span name.
	// Default: "ingress".
	InitialSpan string

	// DisableFilterSpans disables creation of spans representing request and response filters.
	// Default: false
	DisableFilterSpans bool

	// LogFilterEvents enables the behavior to mark start and completion times of filters
	// on the span representing request/response filters being processed.
	// Default: false
	LogFilterEvents bool

	// LogStreamEvents enables the logs that marks the times when response headers & payload are streamed to
	// the client
	// Default: false
	LogStreamEvents bool

	// ExcludeTags controls what tags are disabled. Any tag that is listed here will be ignored.
	ExcludeTags []string
}

type Options

type Options Flags

Options are deprecated alias for Flags.

type Params

type Params struct {
	// The proxy expects a routing instance that is used to match
	// the incoming requests to routes.
	Routing *routing.Routing

	// Control flags. See the Flags values.
	Flags Flags

	// And optional list of priority routes to be used for matching
	// before the general lookup tree.
	PriorityRoutes []PriorityRoute

	// Enable the experimental upgrade protocol feature
	ExperimentalUpgrade bool

	// ExperimentalUpgradeAudit enables audit log of both the request line
	// and the response messages during web socket upgrades.
	ExperimentalUpgradeAudit bool

	// When set, no access log is printed.
	AccessLogDisabled bool

	// DualStack sets if the proxy TCP connections to the backend should be dual stack
	DualStack bool

	// DefaultHTTPStatus is the HTTP status used when no routes are found
	// for a request.
	DefaultHTTPStatus int

	// MaxLoopbacks sets the maximum number of allowed loops. If 0
	// the default (9) is applied. To disable looping, set it to
	// -1. Note, that disabling looping by this option, may result
	// wrong routing depending on the current configuration.
	MaxLoopbacks int

	// Same as net/http.Transport.MaxIdleConnsPerHost, but the default
	// is 64. This value supports scenarios with relatively few remote
	// hosts. When the routing table contains different hosts in the
	// range of hundreds, it is recommended to set this options to a
	// lower value.
	IdleConnectionsPerHost int

	// MaxIdleConns limits the number of idle connections to all backends, 0 means no limit
	MaxIdleConns int

	// DisableHTTPKeepalives forces backend to always create a new connection
	DisableHTTPKeepalives bool

	// CircuitBreakers provides a registry that skipper can use to
	// find the matching circuit breaker for backend requests. If not
	// set, no circuit breakers are used.
	CircuitBreakers *circuit.Registry

	// RateLimiters provides a registry that skipper can use to
	// find the matching ratelimiter for backend requests. If not
	// set, no ratelimits are used.
	RateLimiters *ratelimit.Registry

	// LoadBalancer to report unhealthy or dead backends to
	LoadBalancer *loadbalancer.LB

	// Defines the time period of how often the idle connections are
	// forcibly closed. The default is 12 seconds. When set to less than
	// 0, the proxy doesn't force closing the idle connections.
	CloseIdleConnsPeriod time.Duration

	// The Flush interval for copying upgraded connections
	FlushInterval time.Duration

	// Timeout sets the TCP client connection timeout for proxy http connections to the backend
	Timeout time.Duration

	// ResponseHeaderTimeout sets the HTTP response timeout for
	// proxy http connections to the backend.
	ResponseHeaderTimeout time.Duration

	// ExpectContinueTimeout sets the HTTP timeout to expect a
	// response for status Code 100 for proxy http connections to
	// the backend.
	ExpectContinueTimeout time.Duration

	// KeepAlive sets the TCP keepalive for proxy http connections to the backend
	KeepAlive time.Duration

	// TLSHandshakeTimeout sets the TLS handshake timeout for proxy connections to the backend
	TLSHandshakeTimeout time.Duration

	// Client TLS to connect to Backends
	ClientTLS *tls.Config

	// OpenTracing contains parameters related to OpenTracing instrumentation. For default values
	// check OpenTracingParams
	OpenTracing *OpenTracingParams

	// CustomHttpRoundTripperWrap provides ability to wrap http.RoundTripper created by skipper.
	// http.RoundTripper is used for making outgoing requests (backends)
	// It allows to add additional logic (for example tracing) by providing a wrapper function
	// which accepts original skipper http.RoundTripper as an argument and returns a wrapped roundtripper
	CustomHttpRoundTripperWrap func(http.RoundTripper) http.RoundTripper
}

Proxy initialization options.

type PriorityRoute

type PriorityRoute interface {
	// If the request is matched, returns a route, otherwise nil.
	// Additionally it may return a parameter map used by the filters
	// in the route.
	Match(*http.Request) (*routing.Route, map[string]string)
}

Priority routes are custom route implementations that are matched against each request before the routes in the general lookup tree.

Example
package main

import (
	"log"
	"net/http"

	"github.com/zalando/skipper/eskip"
	"github.com/zalando/skipper/filters/builtin"
	"github.com/zalando/skipper/proxy"
	"github.com/zalando/skipper/routing"
	"github.com/zalando/skipper/routing/testdataclient"
)

type priorityRoute struct{}

func (p *priorityRoute) Match(request *http.Request) (*routing.Route, map[string]string) {
	if request.URL.Path != "/disabled-page" {
		return nil, nil
	}

	return &routing.Route{Route: eskip.Route{Shunt: true}}, nil
}

func main() {
	// create a routing doc forwarding all requests,
	// and load it in a data client:
	routeDoc := `* -> "https://www.example.org"`
	dataClient, err := testdataclient.NewDoc(routeDoc)
	if err != nil {
		log.Fatal(err)
	}

	// create a priority route making exceptions:
	pr := &priorityRoute{}

	// create an http.Handler:
	proxy.New(
		routing.New(routing.Options{
			FilterRegistry: builtin.MakeRegistry(),
			DataClients:    []routing.DataClient{dataClient}}),
		proxy.OptionsNone,
		pr)
}
Output:

type Proxy

type Proxy struct {
	// contains filtered or unexported fields
}

Proxy instances implement Skipper proxying functionality. For initializing, see the WithParams the constructor and Params.

func New

func New(r *routing.Routing, options Options, pr ...PriorityRoute) *Proxy

New returns an initialized Proxy. Deprecated, see WithParams and Params instead.

func WithParams

func WithParams(p Params) *Proxy

WithParams returns an initialized Proxy.

func (*Proxy) Close

func (p *Proxy) Close() error

Close causes the proxy to stop closing idle connections and, currently, has no other effect. It's primary purpose is to support testing.

func (*Proxy) ServeHTTP

func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request)

http.Handler implementation

Directories

Path Synopsis
Generate a self-signed X.509 certificate for a TLS server.
Generate a self-signed X.509 certificate for a TLS server.

Jump to

Keyboard shortcuts

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