Ejemplo n.º 1
0
func TestStreaming(t *testing.T) {
	const expectedParts = 3

	payload := []byte("some data to stream")
	s := startTestServer(payload, expectedParts, voidCheck)
	defer s.Close()

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

	p := New(routing.New(routing.Options{
		nil,
		routing.MatchingOptionsNone,
		sourcePollTimeout,
		[]routing.DataClient{dc},
		0}), OptionsNone)

	delay()

	u, _ := url.ParseRequestURI("https://www.example.org/hello")
	r := &http.Request{
		URL:    u,
		Method: "GET"}
	w := httptest.NewRecorder()

	parts := 0
	total := 0
	done := make(chan int)
	go p.ServeHTTP(w, r)
	go func() {
		for {
			buf := w.Body.Bytes()

			if len(buf) == 0 {
				time.Sleep(streamingDelay)
				continue
			}

			parts++
			total += len(buf)

			if total >= len(payload) {
				close(done)
				return
			}
		}
	}()

	select {
	case <-done:
		if parts <= expectedParts {
			t.Error("streaming failed", parts)
		}
	case <-time.After(150 * time.Millisecond):
		t.Error("streaming timeout")
	}
}
Ejemplo n.º 2
0
func TestProcessesRequestWithShuntBackend(t *testing.T) {
	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 := make(filters.Registry)
	fr.Register(builtin.NewResponseHeader())

	doc := `hello: Path("/hello") -> responseHeader("X-Test-Response-Header", "response header value") -> <shunt>`
	dc, err := testdataclient.NewDoc(doc)
	if err != nil {
		t.Error(err)
	}

	p := New(routing.New(routing.Options{
		fr,
		routing.MatchingOptionsNone,
		sourcePollTimeout,
		[]routing.DataClient{dc},
		0}), OptionsNone)

	delay()

	p.ServeHTTP(w, r)

	if h, ok := w.Header()["X-Test-Response-Header"]; !ok || h[0] != "response header value" {
		t.Error("wrong response header")
	}
}
Ejemplo n.º 3
0
func ExampleClient_FailNext() {
	// create a data client:
	dc, err := testdataclient.NewDoc(`Path("/some/path") -> "https://www.example.org"`)
	if err != nil || dc == nil {
		log.Fatal(err, dc == nil)
	}

	// set the the next two requests to fail:
	dc.FailNext()
	dc.FailNext()

	// wait for the third request to succeed:
	_, err = dc.LoadAll()
	fmt.Println(err)

	_, err = dc.LoadAll()
	fmt.Println(err)

	routes, err := dc.LoadAll()
	if err != nil || len(routes) != 1 {
		log.Fatal(err, len(routes))
	}

	fmt.Println(routes[0].Backend)

	// Output:
	// failed to get routes
	// failed to get routes
	// https://www.example.org
}
Ejemplo n.º 4
0
// TestNonMatchedStaticRoute for bug #116: non-matched static route supress wild-carded route
func TestNonMatchedStaticRoute(t *testing.T) {
	dc, err := testdataclient.NewDoc(`
		a: Path("/foo/*_") -> "https://foo.org";
		b: Path("/foo/bar") && CustomPredicate("custom1") -> "https://bar.org";
		z: * -> "https://catch.all"`)
	if err != nil {
		t.Error(err)
		return
	}

	cps := []routing.PredicateSpec{&predicate{}}

	tr, err := newTestRoutingWithPredicates(cps, dc)
	if err != nil {
		t.Error(err)
		return
	}

	defer tr.close()

	req, err := http.NewRequest("GET", "https://www.example.com/foo/bar", nil)
	if err != nil {
		t.Error(err)
		return
	}

	if r, err := tr.checkRequest(req); r == nil || err != nil {
		t.Error(err)
	} else {
		if r.Backend != "https://foo.org" {
			t.Error("non-matched static route supress wild-carded route")
		}
	}
}
Ejemplo n.º 5
0
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)
}
Ejemplo n.º 6
0
func TestProcessesPredicates(t *testing.T) {
	dc, err := testdataclient.NewDoc(`
        route1: CustomPredicate("custom1") -> "https://route1.example.org";
        route2: CustomPredicate("custom2") -> "https://route2.example.org";
        catchAll: * -> "https://route.example.org"`)
	if err != nil {
		t.Error(err)
		return
	}

	cps := []routing.PredicateSpec{&predicate{}, &predicate{}}
	rt := routing.New(routing.Options{
		DataClients: []routing.DataClient{dc},
		PollTimeout: pollTimeout,
		Predicates:  cps})

	req, err := http.NewRequest("GET", "https://www.example.com", nil)
	if err != nil {
		t.Error(err)
		return
	}

	req.Header.Set(predicateHeader, "custom1")
	select {
	case r := <-waitRoute(rt, req):
		if r.Backend != "https://route1.example.org" {
			t.Error("custom predicate matching failed, route1")
			return
		}
	case <-time.After(3 * pollTimeout):
		t.Error("test timeout")
	}

	req.Header.Set(predicateHeader, "custom2")
	select {
	case r := <-waitRoute(rt, req):
		if r.Backend != "https://route2.example.org" {
			t.Error("custom predicate matching failed, route2")
			return
		}
	case <-time.After(3 * pollTimeout):
		t.Error("test timeout")
	}

	req.Header.Del(predicateHeader)
	select {
	case r := <-waitRoute(rt, req):
		if r.Backend != "https://route.example.org" {
			t.Error("custom predicate matching failed, catch-all")
			return
		}
	case <-time.After(3 * pollTimeout):
		t.Error("test timeout")
	}
}
Ejemplo n.º 7
0
func TestNoMultipleTreePredicates(t *testing.T) {
	for _, ti := range []struct {
		routes string
		err    bool
	}{{
		`Path("/foo") && Path("/bar") -> <shunt>`,
		true,
	}, {
		`Path("/foo") && PathSubtree("/bar") -> <shunt>`,
		true,
	}, {
		`PathSubtree("/foo") && PathSubtree("/bar") -> <shunt>`,
		true,
	}, {
		`Path("/foo") -> <shunt>`,
		false,
	}, {
		`PathSubtree("/foo") -> <shunt>`,
		false,
	}} {
		func() {
			dc, err := testdataclient.NewDoc(ti.routes)
			if err != nil {
				if !ti.err {
					t.Error(ti.routes, err)
				}

				return
			}

			defs, err := dc.LoadAll()
			if err != nil {
				if !ti.err {
					t.Error(ti.routes, err)
				}

				return
			}

			erred := false
			pr := make(map[string]PredicateSpec)
			fr := make(filters.Registry)
			for _, d := range defs {
				if _, err := processRouteDef(pr, fr, d); err != nil {
					erred = true
					break
				}
			}

			if erred != ti.err {
				t.Error("unexpected error result", erred, ti.err)
			}
		}()
	}
}
Ejemplo n.º 8
0
func TestRoute(t *testing.T) {
	payload1 := []byte("host one")
	s1 := startTestServer(payload1, 0, voidCheck)
	defer s1.Close()

	payload2 := []byte("host two")
	s2 := startTestServer(payload2, 0, voidCheck)
	defer s2.Close()

	doc := fmt.Sprintf(`
		route1: Path("/host-one/*any") -> "%s";
		route2: Path("/host-two/*any") -> "%s"
	`, s1.URL, s2.URL)
	dc, err := testdataclient.NewDoc(doc)
	if err != nil {
		t.Error(err)
	}

	p := New(routing.New(routing.Options{
		nil,
		routing.MatchingOptionsNone,
		sourcePollTimeout,
		[]routing.DataClient{dc},
		nil,
		0}), OptionsNone)

	delay()

	var (
		r *http.Request
		w *httptest.ResponseRecorder
		u *url.URL
	)

	u, _ = url.ParseRequestURI("https://www.example.org/host-one/some/path")
	r = &http.Request{
		URL:    u,
		Method: "GET"}
	w = httptest.NewRecorder()
	p.ServeHTTP(w, r)
	if w.Code != http.StatusOK || !bytes.Equal(w.Body.Bytes(), payload1) {
		t.Error("wrong routing 1")
	}

	u, _ = url.ParseRequestURI("https://www.example.org/host-two/some/path")
	r = &http.Request{
		URL:    u,
		Method: "GET"}
	w = httptest.NewRecorder()
	p.ServeHTTP(w, r)
	if w.Code != http.StatusOK || !bytes.Equal(w.Body.Bytes(), payload2) {
		t.Error("wrong routing 2")
	}
}
Ejemplo n.º 9
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!
}
Ejemplo n.º 10
0
func TestAppliesFilters(t *testing.T) {
	payload := []byte("Hello World!")

	s := startTestServer(payload, 0, func(r *http.Request) {
		if h, ok := r.Header["X-Test-Request-Header"]; !ok ||
			h[0] != "request header value" {
			t.Error("request header is missing")
		}
	})
	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 := make(filters.Registry)
	fr.Register(builtin.NewRequestHeader())
	fr.Register(builtin.NewResponseHeader())

	doc := fmt.Sprintf(`hello:
		Path("/hello") ->
		requestHeader("X-Test-Request-Header", "request header value") ->
		responseHeader("X-Test-Response-Header", "response header value") ->
		"%s"`, s.URL)
	dc, err := testdataclient.NewDoc(doc)
	if err != nil {
		t.Error(err)
	}

	p := New(routing.New(routing.Options{
		fr,
		routing.MatchingOptionsNone,
		sourcePollTimeout,
		[]routing.DataClient{dc},
		nil,
		0}), OptionsNone)

	delay()

	p.ServeHTTP(w, r)

	if h, ok := w.Header()["X-Test-Response-Header"]; !ok || h[0] != "response header value" {
		t.Error("missing response header")
	}
}
Ejemplo n.º 11
0
func TestFlusherImplementation(t *testing.T) {
	h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello, "))
		time.Sleep(15 * time.Millisecond)
		w.Write([]byte("world!"))
	})

	ts := httptest.NewServer(h)
	defer ts.Close()

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

	p := New(routing.New(routing.Options{
		nil,
		routing.MatchingOptionsNone,
		sourcePollTimeout,
		[]routing.DataClient{dc},
		nil,
		0}), OptionsNone)

	delay()

	a := fmt.Sprintf(":%d", 1<<16-rand.Intn(1<<15))
	ps := &http.Server{Addr: a, Handler: p}
	go ps.ListenAndServe()

	// let the server start listening
	time.Sleep(15 * time.Millisecond)

	rsp, err := http.Get("http://127.0.0.1" + a)
	if err != nil {
		t.Error(err)
		return
	}
	defer rsp.Body.Close()
	b, err := ioutil.ReadAll(rsp.Body)
	if err != nil {
		t.Error(err)
		return
	}
	if string(b) != "Hello, world!" {
		t.Error("failed to receive response")
	}
}
Ejemplo n.º 12
0
func TestProcessesRequestWithPriorityRouteOverStandard(t *testing.T) {
	s0 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("X-Test-Header", "priority-value")
	}))

	s1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("X-Test-Header", "normal-value")
	}))

	req, err := http.NewRequest(
		"GET",
		"https://example.org/hello/world",
		nil)
	if err != nil {
		t.Error(err)
	}

	u, err := url.Parse(s0.URL)
	if err != nil {
		t.Error(err)
	}

	prt := &priorityRoute{&routing.Route{Scheme: u.Scheme, Host: u.Host}, nil, func(r *http.Request) bool {
		return r == req
	}}

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

	p := New(routing.New(routing.Options{
		nil,
		routing.MatchingOptionsNone,
		sourcePollTimeout,
		[]routing.DataClient{dc},
		nil,
		0}), OptionsNone, prt)

	delay()

	w := httptest.NewRecorder()
	p.ServeHTTP(w, req)
	if w.Header().Get("X-Test-Header") != "priority-value" {
		t.Error("failed match priority route")
	}
}
Ejemplo n.º 13
0
func TestProcessesPredicates(t *testing.T) {
	dc, err := testdataclient.NewDoc(`
        route1: CustomPredicate("custom1") -> "https://route1.example.org";
        route2: CustomPredicate("custom2") -> "https://route2.example.org";
        catchAll: * -> "https://route.example.org"`)
	if err != nil {
		t.Error(err)
		return
	}

	cps := []routing.PredicateSpec{&predicate{}, &predicate{}}

	tr, err := newTestRoutingWithPredicates(cps, dc)
	if err != nil {
		t.Error(err)
		return
	}

	defer tr.close()

	req, err := http.NewRequest("GET", "https://www.example.com", nil)
	if err != nil {
		t.Error(err)
		return
	}

	req.Header.Set(predicateHeader, "custom1")
	if r, err := tr.checkRequest(req); r == nil || err != nil {
		t.Error(err)
	} else {
		if r.Backend != "https://route1.example.org" {
			t.Error("custom predicate matching failed, route1")
			return
		}
	}

	req.Header.Del(predicateHeader)
	if r, err := tr.checkRequest(req); r == nil || err != nil {
		t.Error(err)
	} else {
		if r.Backend != "https://route.example.org" {
			t.Error("custom predicate matching failed, catch-all")
			return
		}
	}
}
Ejemplo n.º 14
0
func TestPostRoundtrip(t *testing.T) {
	s := startTestServer(nil, 0, func(r *http.Request) {
		if r.Method != "POST" {
			t.Error("wrong request method", r.Method)
		}

		if th, ok := r.Header["X-Test-Header"]; !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: "POST",
		Header: http.Header{"X-Test-Header": []string{"test value"}}}
	w := httptest.NewRecorder()

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

	p := New(routing.New(routing.Options{
		nil,
		routing.MatchingOptionsNone,
		sourcePollTimeout,
		[]routing.DataClient{dc},
		nil,
		0}), OptionsNone)

	delay()

	p.ServeHTTP(w, r)

	if w.Code != http.StatusOK {
		t.Error("wrong status", w.Code)
	}

	if w.Body.Len() != 0 {
		t.Error("wrong content", string(w.Body.Bytes()))
	}
}
Ejemplo n.º 15
0
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)
}
Ejemplo n.º 16
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)
}
Ejemplo n.º 17
0
func newTestProxyWithFilters(fr filters.Registry, doc string, flags Flags, pr ...PriorityRoute) (*testProxy, error) {
	dc, err := testdataclient.NewDoc(doc)
	if err != nil {
		return nil, err
	}

	tl := loggingtest.New()
	rt := routing.New(routing.Options{
		FilterRegistry: fr,
		PollTimeout:    sourcePollTimeout,
		DataClients:    []routing.DataClient{dc},
		Log:            tl})
	p := WithParams(Params{Routing: rt, Flags: flags, PriorityRoutes: pr})

	if err := tl.WaitFor("route settings applied", time.Second); err != nil {
		return nil, err
	}

	return &testProxy{tl, rt, p}, nil
}
Ejemplo n.º 18
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)
	}
}
Ejemplo n.º 19
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!
}
Ejemplo n.º 20
0
func TestBreakFilterChain(t *testing.T) {
	s := startTestServer([]byte("Hello World!"), 0, func(r *http.Request) {
		t.Error("This should never be called")
	})
	defer s.Close()

	fr := make(filters.Registry)
	fr.Register(builtin.NewRequestHeader())
	resp1 := &http.Response{
		Header:     make(http.Header),
		Body:       ioutil.NopCloser(new(bytes.Buffer)),
		StatusCode: http.StatusUnauthorized,
		Status:     "Impossible body",
	}
	fr.Register(&breaker{resp1})
	fr.Register(builtin.NewResponseHeader())

	doc := fmt.Sprintf(`breakerDemo:
		Path("/breaker") ->
		requestHeader("X-Expected", "request header") ->
		responseHeader("X-Expected", "response header") ->
		breaker() ->
		requestHeader("X-Unexpected", "foo") ->
		responseHeader("X-Unexpected", "bar") ->
		"%s"`, s.URL)
	dc, err := testdataclient.NewDoc(doc)
	if err != nil {
		t.Error(err)
	}

	p := New(routing.New(routing.Options{
		fr,
		routing.MatchingOptionsNone,
		sourcePollTimeout,
		[]routing.DataClient{dc},
		nil,
		0}), OptionsNone)

	delay()

	r, _ := http.NewRequest("GET", "https://www.example.org/breaker", nil)
	w := httptest.NewRecorder()
	p.ServeHTTP(w, r)

	if _, has := r.Header["X-Expected"]; !has {
		t.Error("Request is missing the expected header (added during filter chain winding)")
	}

	if _, has := w.Header()["X-Expected"]; !has {
		t.Error("Response is missing the expected header (added during filter chain unwinding)")
	}

	if _, has := r.Header["X-Unexpected"]; has {
		t.Error("Request has an unexpected header from a filter after the breaker in the chain")
	}

	if _, has := w.Header()["X-Unexpected"]; has {
		t.Error("Response has an unexpected header from a filter after the breaker in the chain")
	}

	if w.Code != http.StatusUnauthorized && w.Body.String() != "Impossible body" {
		t.Errorf("Wrong status code/body. Expected 401 - Impossible body but got %d - %s", w.Code, w.Body.String())
	}
}
Ejemplo n.º 21
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()
	}
}
Ejemplo n.º 22
0
func ExampleNewDoc() {
	dc, err := testdataclient.NewDoc(`Path("/some/path") -> "https://www.example.org"`)
	if err != nil || dc == nil {
		log.Fatal(err, dc == nil)
	}
}
Ejemplo n.º 23
0
func TestGetRoundtrip(t *testing.T) {
	payload := []byte("Hello World!")

	s := startTestServer(payload, 0, func(r *http.Request) {
		if r.Method != "GET" {
			t.Error("wrong request method")
		}

		if th, ok := r.Header["X-Test-Header"]; !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") -> "%s"`, s.URL)
	dc, err := testdataclient.NewDoc(doc)
	if err != nil {
		t.Error(err)
	}

	p := New(routing.New(routing.Options{
		nil,
		routing.MatchingOptionsNone,
		sourcePollTimeout,
		[]routing.DataClient{dc},
		0}), OptionsNone)

	delay()

	p.ServeHTTP(w, r)

	if w.Code != http.StatusOK {
		t.Error("wrong status", w.Code)
	}

	if ct, ok := w.Header()["Content-Type"]; !ok || ct[0] != "text/plain" {
		t.Error("wrong content type")
	}

	if cl, ok := w.Header()["Content-Length"]; !ok || cl[0] != strconv.Itoa(len(payload)) {
		t.Error("wrong content length")
	}

	if xpb, ok := w.Header()["X-Powered-By"]; !ok || xpb[0] != "Skipper" {
		t.Error("Wrong X-Powered-By header value")
	}

	if xpb, ok := w.Header()["Server"]; !ok || xpb[0] != "Skipper" {
		t.Error("Wrong Server header value")
	}

	if !bytes.Equal(w.Body.Bytes(), payload) {
		t.Error("wrong content", string(w.Body.Bytes()))
	}
}