Example #1
0
func TestOriginalRequestResponse(t *testing.T) {
	s := startTestServer(nil, 0, func(r *http.Request) {
		if th, ok := r.Header["X-Test-Header-Preserved"]; !ok || th[0] != "test value" {
			t.Error("wrong request header")
		}
	})

	defer s.Close()

	u, _ := url.ParseRequestURI("https://www.example.org/hello")
	r := &http.Request{
		URL:    u,
		Method: "GET",
		Header: http.Header{"X-Test-Header": []string{"test value"}}}
	w := httptest.NewRecorder()

	fr := builtin.MakeRegistry()
	fr.Register(&preserveOriginalSpec{})

	doc := fmt.Sprintf(`hello: Path("/hello") -> preserveOriginal() -> "%s"`, s.URL)
	tp, err := newTestProxyWithFilters(fr, doc, PreserveOriginal)
	if err != nil {
		t.Error(err)
		return
	}

	defer tp.close()

	tp.proxy.ServeHTTP(w, r)

	if th, ok := w.Header()["X-Test-Response-Header-Preserved"]; !ok || th[0] != "response header value" {
		t.Error("wrong response header", ok)
	}
}
func Example() {
	// create registry
	registry := builtin.MakeRegistry()

	// create and register the filter specification
	spec := &customSpec{name: "customFilter"}
	registry.Register(spec)

	// create simple data client, with route entries referencing 'customFilter',
	// and clipping part of the request path:
	dataClient, err := testdataclient.NewDoc(`

		ui: Path("/ui/*page") ->
			customFilter("ui request") ->
			modPath("^/[^/]*", "") ->
			"https://ui.example.org";

		api: Path("/api/*resource") ->
			customFilter("api request") ->
			modPath("^/[^/]*", "") ->
			"https://api.example.org"`)

	if err != nil {
		log.Fatal(err)
	}

	// create http.Handler:
	proxy.New(
		routing.New(routing.Options{
			FilterRegistry: registry,
			DataClients:    []routing.DataClient{dataClient}}),
		proxy.OptionsNone)
}
Example #3
0
// Run skipper.
func Run(o Options) error {
	// create authentication for Innkeeper
	auth := createInnkeeperAuthentication(o)

	// create data client
	dataClients, err := createDataClients(o, auth)
	if err != nil {
		return err
	}

	if len(dataClients) == 0 {
		log.Println("warning: no route source specified")
	}

	// create a filter registry with the available filter specs registered,
	// and register the custom filters
	registry := builtin.MakeRegistry()
	for _, f := range o.CustomFilters {
		registry.Register(f)
	}

	// create routing
	// create the proxy instance
	var mo routing.MatchingOptions
	if o.IgnoreTrailingSlash {
		mo = routing.IgnoreTrailingSlash
	}

	// ensure a non-zero poll timeout
	if o.SourcePollTimeout <= 0 {
		o.SourcePollTimeout = defaultSourcePollTimeout
	}

	// check for dev mode, and set update buffer of the routes
	updateBuffer := defaultRoutingUpdateBuffer
	if o.DevMode {
		updateBuffer = 0
	}

	// create a routing engine
	routing := routing.New(routing.Options{
		registry,
		mo,
		o.SourcePollTimeout,
		dataClients,
		updateBuffer})

	// create the proxy
	proxy := proxy.New(routing, o.ProxyOptions, o.PriorityRoutes...)

	// start the http server
	log.Printf("listening on %v\n", o.Address)
	return http.ListenAndServe(o.Address, proxy)
}
Example #4
0
func Example() {
	// create etcd data client:
	dataClient := etcd.New([]string{"https://etcd.example.org"}, "/skipper")

	// create http.Handler:
	proxy.New(
		routing.New(routing.Options{
			FilterRegistry: builtin.MakeRegistry(),
			DataClients:    []routing.DataClient{dataClient}}),
		proxy.OptionsNone)
}
func Example() {
	// create etcd data client:
	dataClient, err := etcd.New(etcd.Options{[]string{"https://etcd.example.org"}, "/skipper", 0})
	if err != nil {
		log.Fatal(err)
	}

	// create http.Handler:
	proxy.New(
		routing.New(routing.Options{
			FilterRegistry: builtin.MakeRegistry(),
			DataClients:    []routing.DataClient{dataClient}}),
		proxy.OptionsNone)
}
Example #6
0
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 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!
}
Example #7
0
func Example() {
	// create etcd data client:
	dataClient, err := etcd.New(etcd.Options{[]string{"https://etcd.example.org"}, "/skipper", 0, false})
	if err != nil {
		log.Fatal(err)
	}

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

	// create http.Handler:
	p := proxy.New(rt, proxy.OptionsNone)
	defer p.Close()
}
func ExampleFilter() {
	// create a test filter and add to the registry:
	fr := builtin.MakeRegistry()
	fr.Register(&filtertest.Filter{FilterName: "testFilter"})

	// create a data client, with a predefined route referencing the filter:
	dc, err := testdataclient.NewDoc(`Path("/some/path/:param") -> testFilter(3.14, "Hello, world!") -> "https://www.example.org"`)
	if err != nil {
		log.Fatal(err)
	}

	// create an http.Handler:
	proxy.New(
		routing.New(routing.Options{
			DataClients:    []routing.DataClient{dc},
			FilterRegistry: fr}),
		proxy.OptionsNone)
}
Example #9
0
func ExamplePriorityRoute() {
	// 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)
}
Example #10
0
// to run this test, set `-args listener` for the test command
func TestHTTPSServer(t *testing.T) {
	// TODO: figure why sometimes cannot connect
	if !testListener() {
		t.Skip()
	}

	a, err := findAddress()
	if err != nil {
		t.Fatal(err)
	}

	o := Options{
		Address:     a,
		CertPathTLS: "fixtures/test.crt",
		KeyPathTLS:  "fixtures/test.key",
	}

	rt := routing.New(routing.Options{
		FilterRegistry: builtin.MakeRegistry(),
		DataClients:    []routing.DataClient{}})
	defer rt.Close()

	proxy := proxy.New(rt, proxy.OptionsNone)
	defer proxy.Close()
	go listenAndServe(proxy, &o)

	r, err := waitConnGet("https://" + o.Address)
	if r != nil {
		defer r.Body.Close()
	}
	if err != nil {
		t.Fatalf("Cannot connect to the local server for testing: %s ", err.Error())
	}
	if r.StatusCode != 404 {
		t.Fatalf("Status code should be 404, instead got: %d\n", r.StatusCode)
	}
	_, err = ioutil.ReadAll(r.Body)
	if err != nil {
		t.Fatalf("Failed to stream response body: %v", err)
	}
}
Example #11
0
func TestOriginalRequestResponse(t *testing.T) {
	s := startTestServer(nil, 0, func(r *http.Request) {
		if th, ok := r.Header["X-Test-Header-Preserved"]; !ok || th[0] != "test value" {
			t.Error("wrong request header")
		}
	})

	defer s.Close()

	u, _ := url.ParseRequestURI("https://www.example.org/hello")
	r := &http.Request{
		URL:    u,
		Method: "GET",
		Header: http.Header{"X-Test-Header": []string{"test value"}}}
	w := httptest.NewRecorder()

	doc := fmt.Sprintf(`hello: Path("/hello") -> preserveOriginal() -> "%s"`, s.URL)
	dc, err := testdataclient.NewDoc(doc)
	if err != nil {
		t.Error(err)
	}

	fr := builtin.MakeRegistry()
	fr.Register(&preserveOriginalSpec{})
	p := New(routing.New(routing.Options{
		fr,
		routing.MatchingOptionsNone,
		sourcePollTimeout,
		[]routing.DataClient{dc},
		nil,
		0}), OptionsPreserveOriginal)

	delay()

	p.ServeHTTP(w, r)

	if th, ok := w.Header()["X-Test-Response-Header-Preserved"]; !ok || th[0] != "response header value" {
		t.Error("wrong response header", ok)
	}
}
Example #12
0
func TestWithWrongKeyPathFails(t *testing.T) {
	a, err := findAddress()
	if err != nil {
		t.Fatal(err)
	}

	o := Options{Address: a,
		CertPathTLS: "fixtures/test.crt",
		KeyPathTLS:  "fixtures/notFound.key",
	}

	rt := routing.New(routing.Options{
		FilterRegistry: builtin.MakeRegistry(),
		DataClients:    []routing.DataClient{}})
	defer rt.Close()

	proxy := proxy.New(rt, proxy.OptionsNone)
	defer proxy.Close()
	err = listenAndServe(proxy, &o)
	if err == nil {
		t.Fatal(err)
	}
}
Example #13
0
func Example() {
	// create a data client with a predefined route:
	dataClient, err := testdataclient.NewDoc(
		`Path("/some/path/to/:id") -> requestHeader("X-From", "skipper") -> "https://www.example.org"`)
	if err != nil {
		log.Fatal(err)
	}

	// create a router:
	r := routing.New(routing.Options{
		FilterRegistry:  builtin.MakeRegistry(),
		MatchingOptions: routing.IgnoreTrailingSlash,
		DataClients:     []routing.DataClient{dataClient}})

	// let the route data get propagated in the background:
	time.Sleep(36 * time.Millisecond)

	// create a request:
	req, err := http.NewRequest("GET", "https://www.example.com/some/path/to/Hello,+world!", nil)
	if err != nil {
		log.Fatal(err)
	}

	// match the request with the router:
	route, params := r.Route(req)
	if route == nil {
		log.Fatal("failed to route")
	}

	// verify the matched route and the path params:
	fmt.Println(route.Backend)
	fmt.Println(params["id"])

	// Output:
	// https://www.example.org
	// Hello, world!
}
Example #14
0
func newTestRoutingWithPredicates(cps []routing.PredicateSpec, dc ...routing.DataClient) (*testRouting, error) {
	return newTestRoutingWithFiltersPredicates(builtin.MakeRegistry(), cps, dc...)
}
Example #15
0
func newTestProxy(doc string, flags Flags, pr ...PriorityRoute) (*testProxy, error) {
	return newTestProxyWithFilters(builtin.MakeRegistry(), doc, flags, pr...)
}
Example #16
0
// Run skipper.
func Run(o Options) error {
	// init log
	err := initLog(o)
	if err != nil {
		return err
	}

	// init metrics
	metrics.Init(metrics.Options{
		Listener:             o.MetricsListener,
		Prefix:               o.MetricsPrefix,
		EnableDebugGcMetrics: o.EnableDebugGcMetrics,
		EnableRuntimeMetrics: o.EnableRuntimeMetrics,
	})

	// create authentication for Innkeeper
	auth := innkeeper.CreateInnkeeperAuthentication(innkeeper.AuthOptions{
		InnkeeperAuthToken:  o.InnkeeperAuthToken,
		OAuthCredentialsDir: o.OAuthCredentialsDir,
		OAuthUrl:            o.OAuthUrl,
		OAuthScope:          o.OAuthScope})

	// create data client
	dataClients, err := createDataClients(o, auth)
	if err != nil {
		return err
	}

	if len(dataClients) == 0 {
		log.Warning("no route source specified")
	}

	// create a filter registry with the available filter specs registered,
	// and register the custom filters
	registry := builtin.MakeRegistry()
	for _, f := range o.CustomFilters {
		registry.Register(f)
	}

	// create routing
	// create the proxy instance
	var mo routing.MatchingOptions
	if o.IgnoreTrailingSlash {
		mo = routing.IgnoreTrailingSlash
	}

	// ensure a non-zero poll timeout
	if o.SourcePollTimeout <= 0 {
		o.SourcePollTimeout = defaultSourcePollTimeout
	}

	// check for dev mode, and set update buffer of the routes
	updateBuffer := defaultRoutingUpdateBuffer
	if o.DevMode {
		updateBuffer = 0
	}

	// create a routing engine
	routing := routing.New(routing.Options{
		registry,
		mo,
		o.SourcePollTimeout,
		dataClients,
		o.CustomPredicates,
		updateBuffer})

	// create the proxy
	proxy := proxy.New(routing, o.ProxyOptions, o.PriorityRoutes...)

	// create the access log handler
	loggingHandler := logging.NewHandler(proxy)

	// start the http server
	log.Infof("proxy listener on %v", o.Address)
	return http.ListenAndServe(o.Address, loggingHandler)
}
Example #17
0
func newTestRouting(dc ...routing.DataClient) (*testRouting, error) {
	return newTestRoutingWithFiltersPredicates(builtin.MakeRegistry(), nil, dc...)
}
Example #18
0
// Run skipper.
func Run(o Options) error {
	// init log
	err := initLog(o)
	if err != nil {
		return err
	}

	// init metrics
	metrics.Init(metrics.Options{
		Listener:                o.MetricsListener,
		Prefix:                  o.MetricsPrefix,
		EnableDebugGcMetrics:    o.EnableDebugGcMetrics,
		EnableRuntimeMetrics:    o.EnableRuntimeMetrics,
		EnableServeRouteMetrics: o.EnableServeRouteMetrics,
		EnableServeHostMetrics:  o.EnableServeHostMetrics,
	})

	// create authentication for Innkeeper
	auth := innkeeper.CreateInnkeeperAuthentication(innkeeper.AuthOptions{
		InnkeeperAuthToken:  o.InnkeeperAuthToken,
		OAuthCredentialsDir: o.OAuthCredentialsDir,
		OAuthUrl:            o.OAuthUrl,
		OAuthScope:          o.OAuthScope})

	// create data clients
	dataClients, err := createDataClients(o, auth)
	if err != nil {
		return err
	}

	// append custom data clients
	dataClients = append(dataClients, o.CustomDataClients...)

	if len(dataClients) == 0 {
		log.Warning("no route source specified")
	}

	// create a filter registry with the available filter specs registered,
	// and register the custom filters
	registry := builtin.MakeRegistry()
	for _, f := range o.CustomFilters {
		registry.Register(f)
	}

	// create routing
	// create the proxy instance
	var mo routing.MatchingOptions
	if o.IgnoreTrailingSlash {
		mo = routing.IgnoreTrailingSlash
	}

	// ensure a non-zero poll timeout
	if o.SourcePollTimeout <= 0 {
		o.SourcePollTimeout = defaultSourcePollTimeout
	}

	// check for dev mode, and set update buffer of the routes
	updateBuffer := defaultRoutingUpdateBuffer
	if o.DevMode {
		updateBuffer = 0
	}

	// include bundeled custom predicates
	o.CustomPredicates = append(o.CustomPredicates,
		source.New(),
		interval.NewBetween(),
		interval.NewBefore(),
		interval.NewAfter(),
		cookie.New(),
		query.New())

	// create a routing engine
	routing := routing.New(routing.Options{
		FilterRegistry:  registry,
		MatchingOptions: mo,
		PollTimeout:     o.SourcePollTimeout,
		DataClients:     dataClients,
		Predicates:      o.CustomPredicates,
		UpdateBuffer:    updateBuffer})
	defer routing.Close()

	proxyFlags := proxy.Flags(o.ProxyOptions) | o.ProxyFlags
	proxyParams := proxy.Params{
		Routing:                routing,
		Flags:                  proxyFlags,
		PriorityRoutes:         o.PriorityRoutes,
		IdleConnectionsPerHost: o.IdleConnectionsPerHost,
		CloseIdleConnsPeriod:   o.CloseIdleConnsPeriod,
		FlushInterval:          o.BackendFlushInterval,
		ExperimentalUpgrade:    o.ExperimentalUpgrade}

	if o.DebugListener != "" {
		do := proxyParams
		do.Flags |= proxy.Debug
		dbg := proxy.WithParams(do)
		log.Infof("debug listener on %v", o.DebugListener)
		go func() { http.ListenAndServe(o.DebugListener, dbg) }()
	}

	// create the proxy
	proxy := proxy.WithParams(proxyParams)
	defer proxy.Close()

	return listenAndServe(proxy, &o)
}
Example #19
0
func TestHostHeader(t *testing.T) {
	// start a test backend that returns the received host header
	backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("X-Received-Host", r.Host)
	}))

	// take the generated host part of the backend
	bu, err := url.Parse(backend.URL)
	if err != nil {
		t.Error("failed to parse test backend url")
		return
	}
	backendHost := bu.Host

	for _, ti := range []struct {
		msg          string
		options      Options
		routeFmt     string
		incomingHost string
		expectedHost string
	}{{
		"no proxy preserve",
		OptionsNone,
		`route: Any() -> "%s"`,
		"www.example.org",
		backendHost,
	}, {
		"no proxy preserve, route preserve not",
		OptionsNone,
		`route: Any() -> preserveHost("false") -> "%s"`,
		"www.example.org",
		backendHost,
	}, {
		"no proxy preserve, route preserve",
		OptionsNone,
		`route: Any() -> preserveHost("true") -> "%s"`,
		"www.example.org",
		"www.example.org",
	}, {
		"no proxy preserve, route preserve not, explicit host last",
		OptionsNone,
		`route: Any() -> preserveHost("false") -> requestHeader("Host", "custom.example.org") -> "%s"`,
		"www.example.org",
		"custom.example.org",
	}, {
		"no proxy preserve, route preserve, explicit host last",
		OptionsNone,
		`route: Any() -> preserveHost("true") -> requestHeader("Host", "custom.example.org") -> "%s"`,
		"www.example.org",
		"custom.example.org",
	}, {
		"no proxy preserve, route preserve not, explicit host first",
		OptionsNone,
		`route: Any() -> requestHeader("Host", "custom.example.org") -> preserveHost("false") -> "%s"`,
		"www.example.org",
		"custom.example.org",
	}, {
		"no proxy preserve, route preserve, explicit host last",
		OptionsNone,
		`route: Any() -> requestHeader("Host", "custom.example.org") -> preserveHost("true") -> "%s"`,
		"www.example.org",
		"custom.example.org",
	}, {
		"proxy preserve",
		OptionsProxyPreserveHost,
		`route: Any() -> "%s"`,
		"www.example.org",
		"www.example.org",
	}, {
		"proxy preserve, route preserve not",
		OptionsProxyPreserveHost,
		`route: Any() -> preserveHost("false") -> "%s"`,
		"www.example.org",
		backendHost,
	}, {
		"proxy preserve, route preserve",
		OptionsProxyPreserveHost,
		`route: Any() -> preserveHost("true") -> "%s"`,
		"www.example.org",
		"www.example.org",
	}, {
		"proxy preserve, route preserve not, explicit host last",
		OptionsProxyPreserveHost,
		`route: Any() -> preserveHost("false") -> requestHeader("Host", "custom.example.org") -> "%s"`,
		"www.example.org",
		"custom.example.org",
	}, {
		"proxy preserve, route preserve, explicit host last",
		OptionsProxyPreserveHost,
		`route: Any() -> preserveHost("true") -> requestHeader("Host", "custom.example.org") -> "%s"`,
		"www.example.org",
		"custom.example.org",
	}, {
		"proxy preserve, route preserve not, explicit host first",
		OptionsProxyPreserveHost,
		`route: Any() -> requestHeader("Host", "custom.example.org") -> preserveHost("false") -> "%s"`,
		"www.example.org",
		"custom.example.org",
	}, {
		"proxy preserve, route preserve, explicit host last",
		OptionsProxyPreserveHost,
		`route: Any() -> requestHeader("Host", "custom.example.org") -> preserveHost("true") -> "%s"`,
		"www.example.org",
		"custom.example.org",
	}} {
		// replace the host in the route format
		f := ti.routeFmt + `;healthcheck: Path("/healthcheck") -> "%s"`
		route := fmt.Sprintf(f, backend.URL, backend.URL)

		// create a dataclient with the route
		dc, err := testdataclient.NewDoc(route)
		if err != nil {
			t.Error(ti.msg, "failed to parse route")
			continue
		}

		// start a proxy server
		r := routing.New(routing.Options{
			FilterRegistry:  builtin.MakeRegistry(),
			MatchingOptions: routing.MatchingOptionsNone,
			PollTimeout:     42 * time.Microsecond,
			DataClients:     []routing.DataClient{dc}})
		ps := httptest.NewServer(New(r, ti.options))

		// wait for the routing table was activated
		healthcheckDone := make(chan struct{})
		go func() {
			for {
				rs, _ := http.Get(ps.URL + "/healthcheck")
				if rs != nil &&
					rs.StatusCode >= http.StatusOK &&
					rs.StatusCode < http.StatusMultipleChoices {
					healthcheckDone <- struct{}{}
					return
				}
			}
		}()
		timeouted := false
		select {
		case <-time.After(999 * time.Millisecond):
			timeouted = true
		case <-healthcheckDone:
		}
		if timeouted {
			t.Error(ti.msg, "startup timeout")
			ps.Close()
			continue
		}

		req, err := http.NewRequest("GET", ps.URL, nil)
		if err != nil {
			t.Error(ti.msg, err)
			ps.Close()
			continue
		}

		req.Host = ti.incomingHost
		rsp, err := (&http.Client{}).Do(req)
		if err != nil {
			t.Error(ti.msg, "failed to make request")
			ps.Close()
			continue
		}

		if rsp.Header.Get("X-Received-Host") != ti.expectedHost {
			t.Error(ti.msg, "wrong host", rsp.Header.Get("X-Received-Host"), ti.expectedHost)
		}

		ps.Close()
	}
}