proxy

package
v0.0.0-...-91c223c Latest Latest
Warning

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

Go to latest
Published: Jun 2, 2016 License: Apache-2.0 Imports: 14 Imported by: 0

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, but it may also mean to handle the request internally if it is a 'shunt' route.

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 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.

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.

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 a proxy instance, and start an http server:
proxy := proxy.New(routing.New(routing.Options{
	FilterRegistry: filterRegistry,
	DataClients:    []routing.DataClient{dataClient}}), proxy.OptionsNone)
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

This section is empty.

Variables

This section is empty.

Functions

func New

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

Creates a proxy. It expects a routing instance that is used to match the incoming requests to routes. If the 'insecure' parameter is true, the proxy skips the TLS verification for the requests made to the route backends. It accepts an optional list of priority routes to be used for matching before the general lookup tree.

Types

type Options

type Options uint
const (
	OptionsNone Options = 0

	// Flag indicating to ignore the verification of the TLS
	// certificates of the backend services.
	OptionsInsecure Options = 1 << iota

	// Flag indicating whether filters require the preserved original
	// metadata of the request and the response.
	OptionsPreserveOriginal

	// Flag indicating 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.
	OptionsProxyPreserveHost

	OptionsDebug
)

func (Options) Debug

func (o Options) Debug() bool

func (Options) Insecure

func (o Options) Insecure() bool

func (Options) PreserveOriginal

func (o Options) PreserveOriginal() bool

func (Options) ProxyPreserveHost

func (o Options) ProxyPreserveHost() bool

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 (
	"fmt"
	"github.com/zalando/skipper/eskip"
	"github.com/zalando/skipper/filters"
	"github.com/zalando/skipper/filters/builtin"
	"github.com/zalando/skipper/proxy"
	"github.com/zalando/skipper/routing"
	"github.com/zalando/skipper/routing/testdataclient"
	"io"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
)

// custom filter type:
type setEchoHeader struct{}

func (s *setEchoHeader) Name() string                                         { return "setEchoHeader" }
func (s *setEchoHeader) CreateFilter(_ []interface{}) (filters.Filter, error) { return s, nil }
func (f *setEchoHeader) Response(_ filters.FilterContext)                     {}

// the filter copies the path parameter 'echo' to the 'X-Echo' header
func (f *setEchoHeader) Request(ctx filters.FilterContext) {
	ctx.Request().Header.Set("X-Echo", ctx.PathParam("echo"))
}

func DisabledExample() {
	// 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 a proxy instance, and start an http server:
	proxy := proxy.New(routing.New(routing.Options{
		FilterRegistry: filterRegistry,
		DataClients:    []routing.DataClient{dataClient}}), proxy.OptionsNone)
	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:

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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