package proxy import ( "bytes" "github.com/owncloud/ocis-proxy/pkg/config" "io" "io/ioutil" "log" "net/http" "net/http/httptest" "net/url" "testing" ) func TestProxyIntegration(t *testing.T) { var tests = []testCase{ // Simple prefix route test("simple_prefix", withPolicy("reva", withRoutes{{ Type: config.PrefixRoute, Endpoint: "/api", Backend: "http://api.example.com"}, })).withRequest("GET", "https://example.com/api", nil). expectProxyTo("http://api.example.com/api"), // Complex prefix route, different method test("complex_prefix_post", withPolicy("reva", withRoutes{{ Type: config.PrefixRoute, Endpoint: "/api", Backend: "http://api.example.com/service1/"}, })).withRequest("POST", "https://example.com/api", nil). expectProxyTo("http://api.example.com/service1/api"), // Query route test("query_route", withPolicy("reva", withRoutes{{ Type: config.QueryRoute, Endpoint: "/api?format=json", Backend: "http://backend/"}, })).withRequest("GET", "https://example.com/api?format=json", nil). expectProxyTo("http://backend/api?format=json"), // Regex route test("regex_route", withPolicy("reva", withRoutes{{ Type: config.RegexRoute, Endpoint: `\/user\/(\d+)`, Backend: "http://backend/"}, })).withRequest("POST", "https://example.com/user/1234", nil). expectProxyTo("http://backend/user/1234"), // Multiple prefix routes 1 test("multiple_prefix", withPolicy("reva", withRoutes{ { Type: config.PrefixRoute, Endpoint: "/api", Backend: "http://api.example.com", }, { Type: config.PrefixRoute, Endpoint: "/payment", Backend: "http://payment.example.com", }, })).withRequest("GET", "https://example.com/payment", nil). expectProxyTo("http://payment.example.com/payment"), // Multiple prefix routes 2 test("multiple_prefix", withPolicy("reva", withRoutes{ { Type: config.PrefixRoute, Endpoint: "/api", Backend: "http://api.example.com", }, { Type: config.PrefixRoute, Endpoint: "/payment", Backend: "http://payment.example.com", }, })).withRequest("GET", "https://example.com/api", nil). expectProxyTo("http://api.example.com/api"), // Mixed route types test("mixed_types", withPolicy("reva", withRoutes{ { Type: config.PrefixRoute, Endpoint: "/api", Backend: "http://api.example.com", }, { Type: config.RegexRoute, Endpoint: `\/user\/(\d+)`, Backend: "http://users.example.com", ApacheVHost: false, }, })).withRequest("GET", "https://example.com/api", nil). expectProxyTo("http://api.example.com/api"), // Mixed route types test("mixed_types", withPolicy("reva", withRoutes{ { Type: config.PrefixRoute, Endpoint: "/api", Backend: "http://api.example.com", }, { Type: config.RegexRoute, Endpoint: `\/user\/(\d+)`, Backend: "http://users.example.com", ApacheVHost: false, }, })).withRequest("GET", "https://example.com/user/1234", nil). expectProxyTo("http://users.example.com/user/1234"), } for k := range tests { t.Run(tests[k].id, func(t *testing.T) { t.Parallel() tc := tests[k] rp := newTestProxy(testConfig(tc.conf), func(req *http.Request) *http.Response { if got, want := req.URL.String(), tc.expect.String(); got != want { t.Errorf("Proxied url should be %v got %v", want, got) } if got, want := req.Method, tc.input.Method; got != want { t.Errorf("Proxied request method should be %v got %v", want, got) } if got, want := req.Proto, tc.input.Proto; got != want { t.Errorf("Proxied request proto should be %v got %v", want, got) } return &http.Response{ StatusCode: 200, Body: ioutil.NopCloser(bytes.NewBufferString(`OK`)), Header: make(http.Header), } }) rr := httptest.NewRecorder() rp.ServeHTTP(rr, tc.input) if rr.Result().StatusCode != 200 { t.Errorf("Expected status 200 from proxy-response got %v", rr.Result().StatusCode) } resultBody, err := ioutil.ReadAll(rr.Result().Body) if err != nil { t.Fatal("Error reading result body") } bodyString := string(resultBody) if bodyString != `OK` { t.Errorf("Result body of proxied response should be OK, got %v", bodyString) } }) } } func newTestProxy(cfg *config.Config, fn RoundTripFunc) *MultiHostReverseProxy { rp := NewMultiHostReverseProxy(Config(cfg)) rp.Transport = fn return rp } type RoundTripFunc func(req *http.Request) *http.Response // RoundTrip . func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { return f(req), nil } type withRoutes []config.Route type testCase struct { id string input *http.Request expect *url.URL conf []config.Policy } func test(id string, policies ...config.Policy) *testCase { tc := &testCase{ id: id, } for k := range policies { tc.conf = append(tc.conf, policies[k]) } return tc } func withPolicy(name string, r withRoutes) config.Policy { return config.Policy{Name: name, Routes: r} } func (tc *testCase) withRequest(method string, target string, body io.Reader) *testCase { tc.input = httptest.NewRequest(method, target, body) return tc } func (tc *testCase) expectProxyTo(strURL string) testCase { pu, err := url.Parse(strURL) if err != nil { log.Fatalf("Error parsing %v", strURL) } tc.expect = pu return *tc } func testConfig(policy []config.Policy) *config.Config { return &config.Config{ File: "", Log: config.Log{}, Debug: config.Debug{}, HTTP: config.HTTP{}, Tracing: config.Tracing{}, Asset: config.Asset{}, Policies: policy, OIDC: nil, PolicySelector: nil, } }