Exemple #1
0
func TestSameFileMultipleTimes(t *testing.T) {
	const n = 6

	if err := ioutil.WriteFile("/tmp/static-test", []byte("test content"), os.ModePerm); err != nil {
		t.Error(err)
		return
	}

	fr := make(filters.Registry)
	fr.Register(NewStatic())
	pr := proxytest.New(fr, &eskip.Route{
		Filters: []*eskip.Filter{{Name: StaticName, Args: []interface{}{"/static", "/tmp"}}},
		Shunt:   true})
	defer pr.Close()

	for i := 0; i < n; i++ {
		rsp, err := http.Get(pr.URL + "/static/static-test")
		if err != nil {
			t.Error(err)
			return
		}

		defer rsp.Body.Close()
		_, err = ioutil.ReadAll(rsp.Body)
		if err != nil {
			t.Error(err)
			return
		}
	}
}
Exemple #2
0
func TestTeeEndToEndBody(t *testing.T) {
	shadowHandler := &MyHandler{name: "shadow"}
	shadowServer := httptest.NewServer(shadowHandler)
	shadowUrl := shadowServer.URL
	defer shadowServer.Close()

	originalHandler := &MyHandler{name: "original"}
	originalServer := httptest.NewServer(originalHandler)
	originalUrl := originalServer.URL
	defer originalServer.Close()

	routeStr := fmt.Sprintf(`route1: * -> tee("%v") -> "%v";`, shadowUrl, originalUrl)

	route, _ := eskip.Parse(routeStr)
	registry := make(filters.Registry)
	registry.Register(NewTee())
	p := proxytest.New(registry, route...)
	defer p.Close()

	testingStr := "TESTEST"
	req, _ := http.NewRequest("GET", p.URL, strings.NewReader(testingStr))
	req.Host = "www.example.org"
	req.Header.Set("X-Test", "true")
	req.Close = true
	rsp, _ := (&http.Client{}).Do(req)
	rsp.Body.Close()
	if shadowHandler.body != testingStr && originalHandler.body != testingStr {
		t.Error("Bodies are not equal")
	}
}
Exemple #3
0
func TestStatus(t *testing.T) {
	for _, ti := range []struct {
		msg          string
		args         []interface{}
		expectedCode int
	}{{
		msg:          "no arguments",
		args:         nil,
		expectedCode: http.StatusNotFound,
	}, {
		msg:          "too many arguments",
		args:         []interface{}{float64(http.StatusTeapot), "something else"},
		expectedCode: http.StatusNotFound,
	}, {
		msg:          "invalid code argument",
		args:         []interface{}{"418"},
		expectedCode: http.StatusNotFound,
	}, {
		msg:          "set status",
		args:         []interface{}{float64(http.StatusTeapot)},
		expectedCode: http.StatusTeapot,
	}} {
		fr := make(filters.Registry)
		fr.Register(NewStatus())
		pr := proxytest.New(fr, &eskip.Route{
			Filters: []*eskip.Filter{{Name: StatusName, Args: ti.args}},
			Shunt:   true})
		defer pr.Close()

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

		req.Close = true

		rsp, err := (&http.Client{}).Do(req)
		if err != nil {
			t.Error(ti.msg, err)
			continue
		}

		defer rsp.Body.Close()

		if rsp.StatusCode != ti.expectedCode {
			t.Error(ti.msg, "status code doesn't match", rsp.StatusCode, ti.expectedCode)
			continue
		}
	}
}
Exemple #4
0
func TestThrottle(t *testing.T) {
	if testing.Short() {
		t.Skip()
	}

	rc := &requestCheck{}
	r := filters.Registry{
		requestCheckName: rc,
		RandomName:       &random{}}

	testServer := proxytest.New(r, &eskip.Route{
		Filters: []*eskip.Filter{
			{Name: requestCheckName, Args: nil},
			{Name: RandomName, Args: []interface{}{float64(testDataLen)}}},
		Shunt: true})
	defer testServer.Close()

	proxyFilters := filters.Registry{
		LatencyName:          NewLatency(),
		BandwidthName:        NewBandwidth(),
		ChunksName:           NewChunks(),
		BackendLatencyName:   NewBackendLatency(),
		BackendBandwidthName: NewBackendBandwidth(),
		BackendChunksName:    NewBackendChunks()}

	for _, ti := range []struct {
		msg           string
		filters       []*eskip.Filter
		clientExpect  messageExp
		backendExpect messageExp
	}{{
		msg:          "zero latency",
		filters:      []*eskip.Filter{{Name: LatencyName, Args: []interface{}{float64(0)}}},
		clientExpect: messageExp{header: time.Millisecond},
	}, {
		msg:          "latency",
		filters:      []*eskip.Filter{{Name: LatencyName, Args: []interface{}{float64(smallDelay)}}},
		clientExpect: messageExp{header: smallDelay * time.Millisecond},
	}, {
		msg:          "high latency",
		filters:      []*eskip.Filter{{Name: LatencyName, Args: []interface{}{float64(highDelay)}}},
		clientExpect: messageExp{header: highDelay * time.Millisecond},
	}, {
		msg:           "zero backend latency",
		filters:       []*eskip.Filter{{Name: BackendLatencyName, Args: []interface{}{float64(0)}}},
		backendExpect: messageExp{header: time.Millisecond},
	}, {
		msg:           "backend latency",
		filters:       []*eskip.Filter{{Name: BackendLatencyName, Args: []interface{}{float64(smallDelay)}}},
		backendExpect: messageExp{header: smallDelay * time.Millisecond},
	}, {
		msg:           "high backend latency",
		filters:       []*eskip.Filter{{Name: BackendLatencyName, Args: []interface{}{float64(highDelay)}}},
		backendExpect: messageExp{header: highDelay * time.Millisecond},
	}, {
		msg:          "bandwidth",
		filters:      []*eskip.Filter{{Name: BandwidthName, Args: []interface{}{float64(12)}}},
		clientExpect: messageExp{kbps: 12},
	}, {
		msg:     "very high bandwidth",
		filters: []*eskip.Filter{{Name: BandwidthName, Args: []interface{}{float64(12000000000)}}},
	}, {
		msg: "bandwidth, adjust",
		filters: []*eskip.Filter{{
			Name: BandwidthName,
			Args: []interface{}{float64(12)},
		}, {
			Name: BandwidthName,
			Args: []interface{}{float64(36)},
		}},
		clientExpect: messageExp{kbps: 12},
	}, {
		msg:           "backend bandwidth",
		filters:       []*eskip.Filter{{Name: BackendBandwidthName, Args: []interface{}{float64(12)}}},
		backendExpect: messageExp{kbps: 12},
	}, {
		msg:     "backend, very high bandwidth",
		filters: []*eskip.Filter{{Name: BackendBandwidthName, Args: []interface{}{float64(12000000000)}}},
	}, {
		msg: "backend bandwidth, adjust",
		filters: []*eskip.Filter{{
			Name: BackendBandwidthName,
			Args: []interface{}{float64(36)},
		}, {
			Name: BackendBandwidthName,
			Args: []interface{}{float64(12)},
		}},
		backendExpect: messageExp{kbps: 12},
	}, {
		msg:          "single chunk",
		filters:      []*eskip.Filter{{Name: ChunksName, Args: []interface{}{float64(2 * testDataLen), float64(smallDelay)}}},
		clientExpect: messageExp{chunks: []testChunk{{2 * testDataLen, time.Duration(smallDelay) * time.Millisecond}}},
	}, {
		msg:          "single chunk, long delay",
		filters:      []*eskip.Filter{{Name: ChunksName, Args: []interface{}{float64(2 * testDataLen), float64(highDelay)}}},
		clientExpect: messageExp{chunks: []testChunk{{2 * testDataLen, time.Duration(highDelay) * time.Millisecond}}},
	}, {
		msg:     "multiple chunks",
		filters: []*eskip.Filter{{Name: ChunksName, Args: []interface{}{float64(testDataLen/4 + testDataLen/8), float64(smallDelay)}}},
		clientExpect: messageExp{chunks: []testChunk{{
			testDataLen/4 + testDataLen/8, time.Duration(smallDelay) * time.Millisecond,
		}, {
			testDataLen/4 + testDataLen/8, time.Duration(smallDelay) * time.Millisecond,
		}, {
			testDataLen / 4, time.Duration(smallDelay) * time.Millisecond,
		}}},
	}, {
		msg:     "multiple chunks, long delay",
		filters: []*eskip.Filter{{Name: ChunksName, Args: []interface{}{float64(testDataLen/4 + testDataLen/8), float64(highDelay)}}},
		clientExpect: messageExp{chunks: []testChunk{{
			testDataLen/4 + testDataLen/8, time.Duration(highDelay) * time.Millisecond,
		}, {
			testDataLen/4 + testDataLen/8, time.Duration(highDelay) * time.Millisecond,
		}, {
			testDataLen / 4, time.Duration(highDelay) * time.Millisecond,
		}}},
	}, {
		msg:           "single chunk, backend",
		filters:       []*eskip.Filter{{Name: BackendChunksName, Args: []interface{}{float64(2 * testDataLen), float64(smallDelay)}}},
		backendExpect: messageExp{chunks: []testChunk{{2 * testDataLen, time.Duration(smallDelay) * time.Millisecond}}},
	}, {
		msg:           "single chunk, long delay, backend",
		filters:       []*eskip.Filter{{Name: BackendChunksName, Args: []interface{}{float64(2 * testDataLen), float64(highDelay)}}},
		backendExpect: messageExp{chunks: []testChunk{{2 * testDataLen, time.Duration(highDelay) * time.Millisecond}}},
	}, {
		msg:     "multiple chunks, backend",
		filters: []*eskip.Filter{{Name: BackendChunksName, Args: []interface{}{float64(testDataLen/4 + testDataLen/8), float64(smallDelay)}}},
		backendExpect: messageExp{chunks: []testChunk{{
			testDataLen/4 + testDataLen/8, time.Duration(smallDelay) * time.Millisecond,
		}, {
			testDataLen/4 + testDataLen/8, time.Duration(smallDelay) * time.Millisecond,
		}, {
			testDataLen / 4, time.Duration(smallDelay) * time.Millisecond,
		}}},
	}, {
		msg:     "multiple chunks, long delay, backend",
		filters: []*eskip.Filter{{Name: BackendChunksName, Args: []interface{}{float64(testDataLen/4 + testDataLen/8), float64(highDelay)}}},
		backendExpect: messageExp{chunks: []testChunk{{
			testDataLen/4 + testDataLen/8, time.Duration(highDelay) * time.Millisecond,
		}, {
			testDataLen/4 + testDataLen/8, time.Duration(highDelay) * time.Millisecond,
		}, {
			testDataLen / 4, time.Duration(highDelay) * time.Millisecond,
		}}},
	}} {
		func() {
			var requestStart time.Time

			rc.check = func(req *http.Request) bool {
				if err := checkMessage(ti.backendExpect, requestStart, req.Body); err != nil {
					t.Error(ti.msg, err)
					return false
				}

				return true
			}

			p := proxytest.New(proxyFilters, &eskip.Route{
				Filters: ti.filters,
				Backend: testServer.URL})
			defer p.Close()

			req, err := http.NewRequest("GET", p.URL,
				&io.LimitedReader{rand.New(rand.NewSource(0)), testDataLen})
			if err != nil {
				t.Error(ti.msg, err)
				return
			}

			req.Close = true

			requestStart = time.Now()
			rsp, err := (&http.Client{}).Do(req)
			if err != nil {
				t.Error(ti.msg, err)
				return
			}

			defer rsp.Body.Close()

			if rsp.StatusCode != http.StatusOK {
				if rsp.StatusCode != http.StatusBadRequest {
					t.Error(ti.msg, "request failed")
				}

				return
			}

			if err := checkMessage(ti.clientExpect, requestStart, rsp.Body); err != nil {
				t.Error(ti.msg, err)
			}

		}()
	}
}
Exemple #5
0
func TestRandom(t *testing.T) {
	for _, ti := range []struct {
		msg string
		len int
	}{{
		"zero bytes",
		0,
	}, {
		"small",
		defaultChunkSize / 2,
	}, {
		"large",
		defaultChunkSize*2 + defaultChunkSize/2,
	}} {
		func() {
			p := proxytest.New(filters.Registry{RandomName: &random{}}, &eskip.Route{
				Filters: []*eskip.Filter{{Name: RandomName, Args: []interface{}{float64(ti.len)}}},
				Shunt:   true})
			defer p.Close()

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

			req.Close = true

			rsp, err := (&http.Client{}).Do(req)
			if err != nil {
				t.Error(ti.msg, err)
				return
			}

			defer rsp.Body.Close()

			if rsp.StatusCode != http.StatusOK {
				t.Error(ti.msg, "request failed")
				return
			}

			b, err := ioutil.ReadAll(rsp.Body)
			if err != nil {
				t.Error(ti.msg, err)
				return
			}

			if len(b) != ti.len {
				t.Error(ti.msg, "invalid content length", len(b), ti.len)
				return
			}

			randBytes := []byte(randomChars)
			for _, bi := range b {
				found := false
				for _, rbi := range randBytes {
					if rbi == bi {
						found = true
						break
					}
				}

				if !found {
					t.Error(ti.msg, "invalid character")
					return
				}
			}
		}()
	}
}
Exemple #6
0
func TestStatic(t *testing.T) {
	const testData = "Hello, world!"

	for _, ti := range []struct {
		msg             string
		args            []interface{}
		content         string
		removeFile      bool
		path            string
		expectedStatus  int
		expectedContent string
	}{{
		msg:            "invalid number of args",
		args:           nil,
		content:        testData,
		path:           "/static/static-test",
		expectedStatus: http.StatusNotFound,
	}, {
		msg:            "not string web root",
		args:           []interface{}{3.14, "/tmp"},
		content:        testData,
		path:           "/static/static-test",
		expectedStatus: http.StatusNotFound,
	}, {
		msg:            "not string fs root",
		args:           []interface{}{"/static", 3.14},
		content:        testData,
		path:           "/static/static-test",
		expectedStatus: http.StatusNotFound,
	}, {
		msg:            "web root cannot be clipped",
		args:           []interface{}{"/static", "/tmp"},
		content:        testData,
		path:           "/a",
		expectedStatus: http.StatusNotFound,
	}, {
		msg:            "not found",
		args:           []interface{}{"/static", "/tmp"},
		content:        testData,
		removeFile:     true,
		path:           "/static/static-test",
		expectedStatus: http.StatusNotFound,
	}, {
		msg:             "found",
		args:            []interface{}{"/static", "/tmp"},
		content:         testData,
		path:            "/static/static-test",
		expectedStatus:  http.StatusOK,
		expectedContent: testData,
	}, {
		msg:             "found, empty",
		args:            []interface{}{"/static", "/tmp"},
		content:         "",
		path:            "/static/static-test",
		expectedStatus:  http.StatusOK,
		expectedContent: "",
	}} {
		if ti.removeFile {
			if err := os.Remove("/tmp/static-test"); err != nil && !os.IsNotExist(err) {
				t.Error(ti.msg, err)
				continue
			}
		} else {
			if err := ioutil.WriteFile("/tmp/static-test", []byte(ti.content), os.ModePerm); err != nil {
				t.Error(ti.msg, err)
				continue
			}
		}

		fr := make(filters.Registry)
		fr.Register(NewStatic())
		pr := proxytest.New(fr, &eskip.Route{
			Filters: []*eskip.Filter{{Name: StaticName, Args: ti.args}},
			Shunt:   true})
		defer pr.Close()

		rsp, err := http.Get(pr.URL + ti.path)
		if err != nil {
			t.Error(ti.msg, err)
			continue
		}

		defer rsp.Body.Close()

		if rsp.StatusCode != ti.expectedStatus {
			t.Error(ti.msg, "status code doesn't match", rsp.StatusCode, ti.expectedStatus)
			continue
		}

		if rsp.StatusCode != http.StatusOK {
			continue
		}

		content, err := ioutil.ReadAll(rsp.Body)
		if err != nil {
			t.Error(ti.msg, err)
			continue
		}

		if string(content) != ti.expectedContent {
			t.Error(ti.msg, "content doesn't match", string(content), ti.expectedContent)
		}
	}
}
Exemple #7
0
func TestMultipleRanges(t *testing.T) {
	const fcontent = "test content"
	if err := ioutil.WriteFile("/tmp/static-test", []byte(fcontent), os.ModePerm); err != nil {
		t.Error(err)
		return
	}

	fr := make(filters.Registry)
	fr.Register(NewStatic())
	pr := proxytest.New(fr, &eskip.Route{
		Filters: []*eskip.Filter{{Name: StaticName, Args: []interface{}{"/static", "/tmp"}}},
		Shunt:   true})
	defer pr.Close()

	req, err := http.NewRequest("GET", pr.URL+"/static/static-test", nil)
	if err != nil {
		t.Error(err)
		return
	}

	req.Close = true
	req.Header.Set("Range", "bytes=1-3,5-8")

	rsp, err := http.DefaultClient.Do(req)
	if err != nil {
		t.Error(err)
		return
	}

	defer rsp.Body.Close()
	_, params, err := mime.ParseMediaType(rsp.Header.Get("Content-Type"))
	if err != nil {
		t.Error(err)
		return
	}

	mp := multipart.NewReader(rsp.Body, params["boundary"])
	parts := [][]int{{1, 4}, {5, 9}}
	for {
		p, err := mp.NextPart()
		if err != nil {
			if err != io.EOF {
				t.Error(err)
			}

			break
		}

		partContent, err := ioutil.ReadAll(p)
		if err != nil {
			t.Error(err)
			break
		}

		if string(partContent) != fcontent[parts[0][0]:parts[0][1]] {
			t.Error("failed to receive multiple ranges")
		}

		parts = parts[1:]
	}

	if len(parts) != 0 {
		t.Error("failed to receive all ranges")
	}
}
Exemple #8
0
func TestStreaming(t *testing.T) {
	if testing.Short() {
		t.Skip()
	}

	randReader := rand.New(rand.NewSource(0))
	writeRandomN := func(w io.Writer, n int64) error {
		nw, err := io.CopyN(w, randReader, n)
		if nw != n {
			return errors.New("failed to write random bytes")
		}

		return err
	}

	timeoutCall := func(to time.Duration, call func(c chan<- error)) error {
		c := make(chan error)
		go call(c)

		select {
		case err := <-c:
			return err
		case <-time.After(to):
			return errors.New("timeout")
		}
	}

	chunkDelay := 120 * time.Millisecond

	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Connection", "close")

		err := writeRandomN(w, 1<<14)
		if err != nil {
			t.Error(err)
		}

		if f, ok := w.(http.Flusher); ok {
			f.Flush()
		}

		time.Sleep(chunkDelay)
		err = writeRandomN(w, 1<<14)
		if err != nil {
			t.Error(err)
		}
	}))
	defer s.Close()

	p := proxytest.New(MakeRegistry(), &eskip.Route{
		Filters: []*eskip.Filter{{Name: CompressName}},
		Backend: s.URL})
	defer p.Close()

	var body io.ReadCloser
	if err := timeoutCall(chunkDelay/2, func(c chan<- error) {
		rsp, err := http.Get(p.URL)
		if err != nil {
			c <- err
			return
		}

		body = rsp.Body

		if rsp.StatusCode != http.StatusOK {
			c <- errors.New("failed to make request")
			return
		}

		const preread = 1 << 6
		n, err := body.Read(make([]byte, preread))
		if err != nil {
			c <- err
			return
		}

		if n != preread {
			c <- errors.New("failed to preread from the body")
			return
		}

		c <- nil
	}); err != nil {
		t.Error(err)
		return
	}

	defer body.Close()

	if err := timeoutCall(chunkDelay*3/2, func(c chan<- error) {
		_, err := ioutil.ReadAll(body)
		c <- err
	}); err != nil {
		t.Error(err)
	}
}
Exemple #9
0
func TestCompress(t *testing.T) {
	for _, ti := range []struct {
		msg            string
		responseHeader http.Header
		contentLength  int
		compressArgs   []interface{}
		acceptEncoding string
		expectedHeader http.Header
	}{{
		"response already encoded",
		http.Header{"Content-Encoding": []string{"x-custom"}},
		3 * 8192,
		nil,
		"gzip,deflate",
		http.Header{"Content-Encoding": []string{"x-custom"}},
	}, {
		"forgives identity in the response",
		http.Header{"Content-Encoding": []string{"identity"}},
		3 * 8192,
		nil,
		"gzip,deflate",
		http.Header{
			"Content-Encoding": []string{"gzip"},
			"Vary":             []string{"Accept-Encoding"}},
	}, {
		"encoding prohibited by cache control",
		http.Header{"Cache-Control": []string{"x-test,no-transform"}},
		3 * 8192,
		nil,
		"gzip,deflate",
		http.Header{"Cache-Control": []string{"x-test,no-transform"}},
	}, {
		"unsupported content type",
		http.Header{"Content-Type": []string{"x/custom"}},
		3 * 8192,
		nil,
		"gzip,deflate",
		http.Header{"Content-Type": []string{"x/custom"}},
	}, {
		"custom content type",
		http.Header{"Content-Type": []string{"x/custom"}},
		3 * 8192,
		[]interface{}{"x/custom"},
		"gzip,deflate",
		http.Header{
			"Content-Type":     []string{"x/custom"},
			"Content-Encoding": []string{"gzip"},
			"Vary":             []string{"Accept-Encoding"}},
	}, {
		"does not accept encoding",
		http.Header{},
		3 * 8192,
		nil,
		"",
		http.Header{},
	}, {
		"unknown encoding",
		http.Header{},
		3 * 8192,
		nil,
		"x-custom",
		http.Header{},
	}, {
		"gzip",
		http.Header{},
		3 * 8192,
		nil,
		"x-custom,gzip",
		http.Header{
			"Content-Encoding": []string{"gzip"},
			"Vary":             []string{"Accept-Encoding"}},
	}, {
		"gzip, no compression",
		http.Header{},
		3 * 8192,
		[]interface{}{float64(gzip.NoCompression)},
		"x-custom,gzip",
		http.Header{
			"Content-Encoding": []string{"gzip"},
			"Vary":             []string{"Accept-Encoding"}},
	}, {
		"gzip, best speed",
		http.Header{},
		3 * 8192,
		[]interface{}{float64(gzip.BestSpeed)},
		"x-custom,gzip",
		http.Header{
			"Content-Encoding": []string{"gzip"},
			"Vary":             []string{"Accept-Encoding"}},
	}, {
		"gzip, stdlib default",
		http.Header{},
		3 * 8192,
		[]interface{}{float64(gzip.DefaultCompression)},
		"x-custom,gzip",
		http.Header{
			"Content-Encoding": []string{"gzip"},
			"Vary":             []string{"Accept-Encoding"}},
	}, {
		"gzip, best compression",
		http.Header{},
		3 * 8192,
		[]interface{}{float64(gzip.BestCompression)},
		"x-custom,gzip",
		http.Header{
			"Content-Encoding": []string{"gzip"},
			"Vary":             []string{"Accept-Encoding"}},
	}, {
		"deflate",
		http.Header{},
		3 * 8192,
		nil,
		"x-custom,deflate",
		http.Header{
			"Content-Encoding": []string{"deflate"},
			"Vary":             []string{"Accept-Encoding"}},
	}, {
		"deflate, no compression",
		http.Header{},
		3 * 8192,
		[]interface{}{float64(flate.NoCompression)},
		"x-custom,deflate",
		http.Header{
			"Content-Encoding": []string{"deflate"},
			"Vary":             []string{"Accept-Encoding"}},
	}, {
		"deflate, best speed",
		http.Header{},
		3 * 8192,
		[]interface{}{float64(flate.BestSpeed)},
		"x-custom,deflate",
		http.Header{
			"Content-Encoding": []string{"deflate"},
			"Vary":             []string{"Accept-Encoding"}},
	}, {
		"deflate",
		http.Header{},
		3 * 8192,
		[]interface{}{float64(flate.DefaultCompression)},
		"x-custom,deflate",
		http.Header{
			"Content-Encoding": []string{"deflate"},
			"Vary":             []string{"Accept-Encoding"}},
	}, {
		"deflate",
		http.Header{},
		3 * 8192,
		[]interface{}{float64(flate.BestCompression)},
		"x-custom,deflate",
		http.Header{
			"Content-Encoding": []string{"deflate"},
			"Vary":             []string{"Accept-Encoding"}},
	}, {
		"weighted first",
		http.Header{},
		3 * 8192,
		nil,
		"x-custom; q=0.8, deflate; q=0.6, gzip; q=0.4",
		http.Header{
			"Content-Encoding": []string{"deflate"},
			"Vary":             []string{"Accept-Encoding"}},
	}, {
		"weighted last",
		http.Header{},
		3 * 8192,
		nil,
		"gzip; q=0.4, x-custom; q=0.8, deflate; q=0.6",
		http.Header{
			"Content-Encoding": []string{"deflate"},
			"Vary":             []string{"Accept-Encoding"}},
	}, {
		"drops content length",
		http.Header{"Content-Length": []string{strconv.Itoa(3 * 8192)}},
		3 * 8192,
		nil,
		"gzip,deflate",
		http.Header{
			"Content-Encoding": []string{"gzip"},
			"Vary":             []string{"Accept-Encoding"}},
	}, {
		"encodes large body",
		http.Header{},
		maxTestContent,
		nil,
		"gzip,deflate",
		http.Header{
			"Content-Encoding": []string{"gzip"},
			"Vary":             []string{"Accept-Encoding"}},
	}} {
		s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
			setHeaders(w.Header(), ti.responseHeader)
			count := 0
			for count < ti.contentLength {
				wl := writeLength
				if count+wl > len(testContent) {
					wl = len(testContent) - count
				}

				w.Write(testContent[count : count+wl])
				count += wl
				time.Sleep(writeDelay)
			}
		}))
		defer s.Close()

		p := proxytest.New(MakeRegistry(), &eskip.Route{
			Filters: []*eskip.Filter{{Name: CompressName, Args: ti.compressArgs}},
			Backend: s.URL})
		defer p.Close()

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

		req.Header.Set("Accept-Encoding", ti.acceptEncoding)

		rsp, err := http.DefaultTransport.RoundTrip(req)
		if err != nil {
			t.Error(ti.msg, err)
			continue
		}

		defer rsp.Body.Close()

		rsp.Header.Del("Server")
		rsp.Header.Del("X-Powered-By")
		rsp.Header.Del("Date")
		if rsp.Header.Get("Content-Type") == "application/octet-stream" {
			rsp.Header.Del("Content-Type")
		}

		if !compareHeaders(rsp.Header, ti.expectedHeader) {
			printHeader(t, ti.expectedHeader, ti.msg, "invalid header", "expected")
			printHeader(t, rsp.Header, ti.msg, "invalid header", "got")

			t.Error(ti.msg, "invalid header")
			continue
		}

		if ok, err := compareBody(rsp, ti.contentLength); err != nil {
			t.Error(ti.msg, err)
		} else if !ok {
			t.Error(ti.msg, "invalid content")
		}
	}
}
Exemple #10
0
func TestHeader(t *testing.T) {
	for _, ti := range []struct {
		msg            string
		filterName     string
		args           []interface{}
		host           string
		valid          bool
		requestHeader  http.Header
		responseHeader http.Header
		expectedHeader http.Header
	}{{
		msg:        "invalid number of args",
		filterName: "setRequestHeader",
		args:       []interface{}{"name", "value", "other value"},
		valid:      false,
	}, {
		msg:        "name not string",
		filterName: "setRequestHeader",
		args:       []interface{}{3, "value"},
		valid:      false,
	}, {
		msg:        "value not string",
		filterName: "setRequestHeader",
		args:       []interface{}{"name", 3},
		valid:      false,
	}, {
		msg:            "set request header when none",
		filterName:     "setRequestHeader",
		args:           []interface{}{"X-Test-Name", "value"},
		valid:          true,
		expectedHeader: http.Header{"X-Test-Request-Name": []string{"value"}},
	}, {
		msg:            "set request header when exists",
		filterName:     "setRequestHeader",
		args:           []interface{}{"X-Test-Name", "value"},
		valid:          true,
		requestHeader:  http.Header{"X-Test-Name": []string{"value0", "value1"}},
		expectedHeader: http.Header{"X-Test-Request-Name": []string{"value"}},
	}, {
		msg:            "append request header when none",
		filterName:     "appendRequestHeader",
		args:           []interface{}{"X-Test-Name", "value"},
		valid:          true,
		expectedHeader: http.Header{"X-Test-Request-Name": []string{"value"}},
	}, {
		msg:            "append request header when exists",
		filterName:     "appendRequestHeader",
		args:           []interface{}{"X-Test-Name", "value"},
		valid:          true,
		requestHeader:  http.Header{"X-Test-Name": []string{"value0", "value1"}},
		expectedHeader: http.Header{"X-Test-Request-Name": []string{"value0", "value1", "value"}},
	}, {
		msg:        "drop request header when none",
		filterName: "dropRequestHeader",
		args:       []interface{}{"X-Test-Name"},
		valid:      true,
	}, {
		msg:           "drop request header when exists",
		filterName:    "dropRequestHeader",
		args:          []interface{}{"X-Test-Name"},
		valid:         true,
		requestHeader: http.Header{"X-Test-Name": []string{"value0", "value1"}},
	}, {
		msg:            "set response header when none",
		filterName:     "setResponseHeader",
		args:           []interface{}{"X-Test-Name", "value"},
		valid:          true,
		expectedHeader: http.Header{"X-Test-Name": []string{"value"}},
	}, {
		msg:            "set response header when exists",
		filterName:     "setResponseHeader",
		args:           []interface{}{"X-Test-Name", "value"},
		valid:          true,
		responseHeader: http.Header{"X-Test-Name": []string{"value0", "value1"}},
		expectedHeader: http.Header{"X-Test-Name": []string{"value"}},
	}, {
		msg:            "append response header when none",
		filterName:     "appendResponseHeader",
		args:           []interface{}{"X-Test-Name", "value"},
		valid:          true,
		expectedHeader: http.Header{"X-Test-Name": []string{"value"}},
	}, {
		msg:            "append response header when exists",
		filterName:     "appendResponseHeader",
		args:           []interface{}{"X-Test-Name", "value"},
		valid:          true,
		responseHeader: http.Header{"X-Test-Name": []string{"value0", "value1"}},
		expectedHeader: http.Header{"X-Test-Name": []string{"value0", "value1", "value"}},
	}, {
		msg:        "drop response header when none",
		filterName: "dropResponseHeader",
		args:       []interface{}{"X-Test-Name"},
		valid:      true,
	}, {
		msg:            "drop response header when exists",
		filterName:     "dropResponseHeader",
		args:           []interface{}{"X-Test-Name"},
		valid:          true,
		responseHeader: http.Header{"X-Test-Name": []string{"value0", "value1"}},
	}, {
		msg:        "set outgoing host on set",
		filterName: "setRequestHeader",
		args:       []interface{}{"Host", "www.example.org"},
		valid:      true,
		host:       "www.example.org",
	}, {
		msg:        "append outgoing host on set",
		filterName: "appendRequestHeader",
		args:       []interface{}{"Host", "www.example.org"},
		valid:      true,
		host:       "www.example.org",
	}} {
		bs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			for n, vs := range r.Header {
				if strings.HasPrefix(n, "X-Test-") {
					w.Header()["X-Test-Request-"+n[7:]] = vs
				}
			}

			for n, vs := range ti.responseHeader {
				w.Header()[n] = vs
			}

			w.Header().Set("X-Request-Host", r.Host)
		}))
		defer bs.Close()

		fr := make(filters.Registry)
		fr.Register(NewSetRequestHeader())
		fr.Register(NewAppendRequestHeader())
		fr.Register(NewDropRequestHeader())
		fr.Register(NewSetResponseHeader())
		fr.Register(NewAppendResponseHeader())
		fr.Register(NewDropResponseHeader())
		pr := proxytest.New(fr, &eskip.Route{
			Filters: []*eskip.Filter{{Name: ti.filterName, Args: ti.args}},
			Backend: bs.URL})
		defer pr.Close()

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

		req.Close = true

		for n, vs := range ti.requestHeader {
			req.Header[n] = vs
		}

		rsp, err := http.DefaultClient.Do(req)
		if err != nil {
			t.Error(ti.msg, err)
			continue
		}

		if ti.valid && rsp.StatusCode != http.StatusOK ||
			!ti.valid && rsp.StatusCode != http.StatusNotFound {
			t.Error(ti.msg, "failed to validate arguments")
			continue
		}

		if ti.host != "" && ti.host != rsp.Header.Get("X-Request-Host") {
			t.Error(ti.msg, "failed to set outgoing request host")
		}

		if ti.valid {
			testHeaders(t, ti.msg, rsp.Header, ti.expectedHeader)
		}
	}
}