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 } } }
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") } }
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 } } }
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) } }() } }
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 } } }() } }
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) } } }
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") } }
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) } }
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") } } }
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) } } }