func TestOptionRequestPostDataLogging(t *testing.T) { logger := NewLogger() logger.SetOption(PostDataLoggingForContentTypes("application/x-www-form-urlencoded")) body := strings.NewReader("first=true&second=false") req, err := http.NewRequest("POST", "http://example.com", body) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") _, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() if err := logger.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } log := logger.Export().Log if got, want := string(log.Entries[0].Request.PostData.Params[0].Value), "true"; got != want { t.Fatalf("log.Entries[0].Request.PostData.Params[0].Value: got %s, want %s", got, want) } logger = NewLogger() logger.SetOption(SkipPostDataLoggingForContentTypes("application/x-www-form-urlencoded")) body = strings.NewReader("first=true&second=false") req, err = http.NewRequest("POST", "http://example.com", body) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") _, remove, err = martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() if err := logger.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } log = logger.Export().Log if got, want := len(log.Entries[0].Request.PostData.Params), 0; got != want { t.Fatalf("len(log.Entries[0].Request.PostData.Params): got %v, want %v", got, want) } }
func TestRoundTrip(t *testing.T) { m := NewRoundTrip() req, err := http.NewRequest("GET", "http://example.com", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } ctx, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() if ctx.SkippingRoundTrip() { t.Fatal("ctx.SkippingRoundTrip(): got true, want false") } if err := m.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } if !ctx.SkippingRoundTrip() { t.Fatal("ctx.SkippingRoundTrip(): got false, want true") } }
func TestReset(t *testing.T) { logger := NewLogger("martian", "2.0.0") req, err := http.NewRequest("GET", "http://example.com", nil) if err != nil { t.Fatalf("NewRequest(): got %v, want no error", err) } _, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() if err := logger.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } log := logger.Export().Log if got, want := len(log.Entries), 1; got != want { t.Fatalf("len(log.Entries): got %d, want %d", got, want) } logger.Reset() log = logger.Export().Log if got, want := len(log.Entries), 0; got != want { t.Errorf("len(log.Entries): got %d, want %d", got, want) } }
func TestExportIgnoresOrphanedResponse(t *testing.T) { logger := NewLogger("martian", "2.0.0") req, err := http.NewRequest("GET", "http://example.com", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } _, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() if err := logger.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } // Reset before the response comes back. logger.Reset() res := proxyutil.NewResponse(200, nil, req) if err := logger.ModifyResponse(res); err != nil { t.Fatalf("ModifyResponse(): got %v, want no error", err) } log := logger.Export().Log if got, want := len(log.Entries), 0; got != want { t.Errorf("len(log.Entries): got %d, want %d", got, want) } }
func TestExportSortsEntries(t *testing.T) { logger := NewLogger("martian", "2.0.0") count := 10 for i := 0; i < count; i++ { req, err := http.NewRequest("GET", "http://example.com", nil) if err != nil { t.Fatalf("NewRequest(): got %v, want no error", err) } _, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() if err := logger.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } } log := logger.Export().Log for i := 0; i < count-1; i++ { first := log.Entries[i] second := log.Entries[i+1] if got, want := first.StartedDateTime, second.StartedDateTime; got.After(want) { t.Errorf("entry.StartedDateTime: got %s, want to be before %s", got, want) } } }
func TestNoModifiers(t *testing.T) { m := NewModifier() m.SetRequestModifier(nil) m.SetResponseModifier(nil) req, err := http.NewRequest("GET", "http://example.com", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } _, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() if err := m.ModifyRequest(req); err != nil { t.Errorf("ModifyRequest(): got %v, want no error", err) } res := proxyutil.NewResponse(200, nil, req) if err := m.ModifyResponse(res); err != nil { t.Errorf("ModifyResponse(): got %v, want no error", err) } }
func TestProxyAuth(t *testing.T) { m := NewModifier() tm := martiantest.NewModifier() m.SetRequestModifier(tm) m.SetResponseModifier(tm) req, err := http.NewRequest("GET", "http://example.com", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } req.Header.Set("Proxy-Authorization", "Basic "+encode("user:pass")) ctx, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() if err := m.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } actx := auth.FromContext(ctx) if got, want := actx.ID(), "user:pass"; got != want { t.Fatalf("actx.ID(): got %q, want %q", got, want) } if err := m.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } if !tm.RequestModified() { t.Error("tm.RequestModified(): got false, want true") } res := proxyutil.NewResponse(200, nil, req) if err := m.ModifyResponse(res); err != nil { t.Fatalf("ModifyResponse(): got %v, want no error", err) } if err := m.ModifyResponse(res); err != nil { t.Fatalf("ModifyResponse(): got %v, want no error", err) } if !tm.ResponseModified() { t.Error("tm.ResponseModified(): got false, want true") } if got, want := res.StatusCode, 200; got != want { t.Errorf("res.StatusCode: got %d, want %d", got, want) } if got, want := res.Header.Get("Proxy-Authenticate"), ""; got != want { t.Errorf("res.Header.Get(%q): got %q, want %q", "Proxy-Authenticate", got, want) } }
func TestModifyResponse(t *testing.T) { m := NewModifier() m.SetResponseModifier(nil) req, err := http.NewRequest("GET", "http://example.com", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } ctx, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() res := proxyutil.NewResponse(200, nil, req) if err := m.ModifyResponse(res); err != nil { t.Fatalf("ModifyResponse(): got %v, want no error", err) } // Modifier with error. tm := martiantest.NewModifier() reserr := errors.New("response error") tm.ResponseError(reserr) m.SetResponseModifier(tm) if err := m.ModifyResponse(res); err != reserr { t.Fatalf("ModifyResponse(): got %v, want %v", err, reserr) } // Modifier with auth error. tm.ResponseError(nil) autherr := errors.New("auth error") tm.ResponseFunc(func(res *http.Response) { ctx := martian.NewContext(res.Request) actx := auth.FromContext(ctx) actx.SetError(autherr) }) actx := auth.FromContext(ctx) actx.SetID("bad-auth") if err := m.ModifyResponse(res); err != nil { t.Fatalf("ModifyResponse(): got %v, want no error", err) } if got, want := res.StatusCode, 403; got != want { t.Errorf("res.StatusCode: got %d, want %d", got, want) } if got, want := actx.Error(), autherr; got != want { t.Errorf("actx.Error(): got %v, want %v", got, want) } }
func TestModifyResponse(t *testing.T) { f := NewFilter() tm := martiantest.NewModifier() f.SetResponseModifier("id", tm) req, err := http.NewRequest("GET", "http://example.com", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } res := proxyutil.NewResponse(200, nil, req) // No ID, auth required. f.SetAuthRequired(true) ctx, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() if err := f.ModifyResponse(res); err != nil { t.Fatalf("ModifyResponse(): got %v, want no error", err) } actx := FromContext(ctx) if actx.Error() == nil { t.Error("actx.Error(): got nil, want error") } if tm.ResponseModified() { t.Error("tm.RequestModified(): got true, want false") } // No ID, no auth required. f.SetAuthRequired(false) actx.SetError(nil) if err := f.ModifyResponse(res); err != nil { t.Fatalf("ModifyResponse(): got %v, want no error", err) } if tm.ResponseModified() { t.Error("tm.ResponseModified(): got true, want false") } // Valid ID. actx.SetID("id") if err := f.ModifyResponse(res); err != nil { t.Fatalf("ModifyResponse(): got %v, want no error", err) } if !tm.ResponseModified() { t.Error("tm.ResponseModified(): got false, want true") } }
func TestNewStack(t *testing.T) { stack, fg := NewStack("martian") tm := martiantest.NewModifier() fg.AddRequestModifier(tm) fg.AddResponseModifier(tm) req, err := http.NewRequest("GET", "http://example.com", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } _, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() // Hop-by-hop header to be removed. req.Header.Set("Hop-By-Hop", "true") req.Header.Set("Connection", "Hop-By-Hop") req.RemoteAddr = "10.0.0.1:5000" if err := stack.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } if got, want := req.Header.Get("Hop-By-Hop"), ""; got != want { t.Errorf("req.Header.Get(%q): got %q, want %q", "Hop-By-Hop", got, want) } if got, want := req.Header.Get("X-Forwarded-For"), "10.0.0.1"; got != want { t.Errorf("req.Header.Get(%q): got %q, want %q", "X-Forwarded-For", got, want) } if got, want := req.Header.Get("Via"), "1.1 martian"; got != want { t.Errorf("req.Header.Get(%q): got %q, want %q", "Via", got, want) } res := proxyutil.NewResponse(200, nil, req) // Hop-by-hop header to be removed. res.Header.Set("Hop-By-Hop", "true") res.Header.Set("Connection", "Hop-By-Hop") if err := stack.ModifyResponse(res); err != nil { t.Fatalf("ModifyResponse(): got %v, want no error", err) } if got, want := res.Header.Get("Hop-By-Hop"), ""; got != want { t.Errorf("res.Header.Get(%q): got %q, want %q", "Hop-By-Hop", got, want) } }
func TestViaModifier(t *testing.T) { m := NewViaModifier("martian") req, err := http.NewRequest("GET", "/", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } res := proxyutil.NewResponse(200, nil, req) ctx, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() if err := m.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } if got, want := req.Header.Get("Via"), "1.1 martian"; got != want { t.Errorf("req.Header.Get(%q): got %q, want %q", "Via", got, want) } if err := m.ModifyResponse(res); err != nil { t.Fatalf("ModifyResponse(): got %v, want no error", err) } req.Header.Set("Via", "1.0\talpha\t(martian)") if err := m.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } if got, want := req.Header.Get("Via"), "1.0\talpha\t(martian), 1.1 martian"; got != want { t.Errorf("req.Header.Get(%q): got %q, want %q", "Via", got, want) } req.Header.Set("Via", "1.0\talpha\t(martian), 1.1 martian, 1.1 beta") if err := m.ModifyRequest(req); err == nil { t.Fatal("ModifyRequest(): got nil, want request loop error") } if !ctx.SkippingRoundTrip() { t.Errorf("ctx.SkippingRoundTrip(): got false, want true") } if err := m.ModifyResponse(res); err == nil { t.Fatal("ModifyResponse(): got nil, want request loop error") } if got, want := res.StatusCode, 400; got != want { t.Errorf("res.StatusCode: got %d, want %d", got, want) } if got, want := res.Status, http.StatusText(400); got != want { t.Errorf("res.Status: got %q, want %q", got, want) } }
func TestModifyRequestBodyURLEncoded(t *testing.T) { logger := NewLogger("martian", "2.0.0") body := strings.NewReader("first=true&second=false") req, err := http.NewRequest("POST", "http://example.com", body) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") _, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() if err := logger.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } log := logger.Export().Log if got, want := len(log.Entries), 1; got != want { t.Errorf("len(log.Entries): got %v, want %v", got, want) } pd := log.Entries[0].Request.PostData if got, want := pd.MimeType, "application/x-www-form-urlencoded"; got != want { t.Errorf("PostData.MimeType: got %v, want %v", got, want) } if got, want := len(pd.Params), 2; got != want { t.Fatalf("len(PostData.Params): got %d, want %d", got, want) } for _, p := range pd.Params { var want string switch p.Name { case "first": want = "true" case "second": want = "false" default: t.Errorf("PostData.Params: got %q, want to not be present", p.Name) continue } if got := p.Value; got != want { t.Errorf("PostData.Params[%q]: got %q, want %q", p.Name, got, want) } } }
func TestHARExportsTime(t *testing.T) { logger := NewLogger("martian", "2.0.0") req, err := http.NewRequest("GET", "http://example.com", nil) if err != nil { t.Fatalf("NewRequest(): got %v, want no error", err) } _, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() if err := logger.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } // Simulate fast network round trip. time.Sleep(10 * time.Millisecond) res := proxyutil.NewResponse(200, nil, req) if err := logger.ModifyResponse(res); err != nil { t.Fatalf("ModifyResponse(): got %v, want no error", err) } log := logger.Export().Log if got, want := len(log.Entries), 1; got != want { t.Fatalf("len(log.Entries): got %v, want %v", got, want) } entry := log.Entries[0] min, max := int64(10), int64(100) if got := entry.Time; got < min || got > max { t.Errorf("entry.Time: got %dms, want between %dms and %vms", got, min, max) } }
func TestModifyRequestBodyArbitraryContentType(t *testing.T) { logger := NewLogger("martian", "2.0.0") body := "arbitrary binary data" req, err := http.NewRequest("POST", "http://www.example.com", strings.NewReader(body)) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } _, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() if err := logger.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } log := logger.Export().Log if got, want := len(log.Entries), 1; got != want { t.Errorf("len(log.Entries): got %d, want %d", got, want) } pd := log.Entries[0].Request.PostData if got, want := pd.MimeType, ""; got != want { t.Errorf("PostData.MimeType: got %q, want %q", got, want) } if got, want := len(pd.Params), 0; got != want { t.Errorf("len(PostData.Params): got %d, want %d", got, want) } if got, want := pd.Text, body; got != want { t.Errorf("PostData.Text: got %q, want %q", got, want) } }
func TestModifyRequestBodyMultipart(t *testing.T) { logger := NewLogger("martian", "2.0.0") body := new(bytes.Buffer) mpw := multipart.NewWriter(body) mpw.SetBoundary("boundary") if err := mpw.WriteField("key", "value"); err != nil { t.Errorf("mpw.WriteField(): got %v, want no error", err) } w, err := mpw.CreateFormFile("file", "test.txt") if _, err = w.Write([]byte("file contents")); err != nil { t.Fatalf("Write(): got %v, want no error", err) } mpw.Close() req, err := http.NewRequest("POST", "http://example.com", body) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } req.Header.Set("Content-Type", mpw.FormDataContentType()) _, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() if err := logger.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } log := logger.Export().Log if got, want := len(log.Entries), 1; got != want { t.Fatalf("len(log.Entries): got %d, want %d", got, want) } pd := log.Entries[0].Request.PostData if got, want := pd.MimeType, "multipart/form-data"; got != want { t.Errorf("PostData.MimeType: got %q, want %q", got, want) } if got, want := len(pd.Params), 2; got != want { t.Errorf("PostData.Params: got %d, want %d", got, want) } for _, p := range pd.Params { var want Param switch p.Name { case "key": want = Param{ Filename: "", ContentType: "", Value: "value", } case "file": want = Param{ Filename: "test.txt", ContentType: "application/octet-stream", Value: "file contents", } default: t.Errorf("pd.Params: got %q, want not to be present", p.Name) continue } if got, want := p.Filename, want.Filename; got != want { t.Errorf("p.Filename: got %q, want %q", got, want) } if got, want := p.ContentType, want.ContentType; got != want { t.Errorf("p.ContentType: got %q, want %q", got, want) } if got, want := p.Value, want.Value; got != want { t.Errorf("p.Value: got %q, want %q", got, want) } } }
func TestModifyRequest(t *testing.T) { req, err := http.NewRequest("GET", "http://example.com/path?query=true", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } req.Header.Add("Request-Header", "first") req.Header.Add("Request-Header", "second") cookie := &http.Cookie{ Name: "request", Value: "cookie", } req.AddCookie(cookie) _, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() logger := NewLogger("martian", "2.0.0") if err := logger.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } log := logger.Export().Log if got, want := log.Version, "1.2"; got != want { t.Errorf("log.Version: got %q, want %q", got, want) } if got, want := log.Creator.Name, "martian"; got != want { t.Errorf("log.Creator.Name: got %q, want %q", got, want) } if got, want := log.Creator.Version, "2.0.0"; got != want { t.Errorf("log.Creator.Version: got %q, want %q", got, want) } if got, want := len(log.Entries), 1; got != want { t.Fatalf("len(log.Entries): got %d, want %d", got, want) } entry := log.Entries[0] if got, want := time.Since(entry.StartedDateTime), time.Second; got > want { t.Errorf("entry.StartedDateTime: got %s, want less than %s", got, want) } hreq := entry.Request if got, want := hreq.Method, "GET"; got != want { t.Errorf("hreq.Method: got %q, want %q", got, want) } if got, want := hreq.URL, "http://example.com/path?query=true"; got != want { t.Errorf("hreq.URL: got %q, want %q", got, want) } if got, want := hreq.HTTPVersion, "HTTP/1.1"; got != want { t.Errorf("hreq.HTTPVersion: got %q, want %q", got, want) } if got, want := hreq.BodySize, int64(0); got != want { t.Errorf("hreq.BodySize: got %d, want %d", got, want) } if got, want := hreq.HeadersSize, int64(-1); got != want { t.Errorf("hreq.HeadersSize: got %d, want %d", got, want) } if got, want := len(hreq.QueryString), 1; got != want { t.Fatalf("len(hreq.QueryString): got %d, want %q", got, want) } qs := hreq.QueryString[0] if got, want := qs.Name, "query"; got != want { t.Errorf("qs.Name: got %q, want %q", got, want) } if got, want := qs.Value, "true"; got != want { t.Errorf("qs.Value: got %q, want %q", got, want) } if got, want := len(hreq.Headers), 2; got != want { t.Fatalf("len(hreq.Headers): got %d, want %d", got, want) } for _, h := range hreq.Headers { var want string switch h.Name { case "Request-Header": want = "first, second" case "Cookie": want = cookie.String() default: t.Errorf("hreq.Headers: got %q, want header to not be present", h.Name) continue } if got := h.Value; got != want { t.Errorf("hreq.Headers[%q]: got %q, want %q", h.Name, got, want) } } if got, want := len(hreq.Cookies), 1; got != want { t.Fatalf("len(hreq.Cookies): got %d, want %d", got, want) } hcookie := hreq.Cookies[0] if got, want := hcookie.Name, "request"; got != want { t.Errorf("hcookie.Name: got %q, want %q", got, want) } if got, want := hcookie.Value, "cookie"; got != want { t.Errorf("hcookie.Value: got %q, want %q", got, want) } }
func TestModifyResponse(t *testing.T) { req, err := http.NewRequest("GET", "http://example.com", nil) if err != nil { t.Fatalf("NewRequest(): got %v, want no error", err) } _, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() res := proxyutil.NewResponse(301, strings.NewReader("response body"), req) res.Header.Add("Response-Header", "first") res.Header.Add("Response-Header", "second") res.Header.Set("Location", "google.com") expires := time.Now() cookie := &http.Cookie{ Name: "response", Value: "cookie", Path: "/", Domain: "example.com", Expires: expires, Secure: true, HttpOnly: true, } res.Header.Set("Set-Cookie", cookie.String()) logger := NewLogger("martian", "2.0.0") if err := logger.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } if err := logger.ModifyResponse(res); err != nil { t.Fatalf("ModifyResponse(): got %v, want no error", err) } log := logger.Export().Log if got, want := len(log.Entries), 1; got != want { t.Fatalf("len(log.Entries): got %d, want %d", got, want) } hres := log.Entries[0].Response if got, want := hres.Status, 301; got != want { t.Errorf("hres.Status: got %d, want %d", got, want) } if got, want := hres.StatusText, "Moved Permanently"; got != want { t.Errorf("hres.StatusText: got %q, want %q", got, want) } if got, want := hres.HTTPVersion, "HTTP/1.1"; got != want { t.Errorf("hres.HTTPVersion: got %q, want %q", got, want) } if got, want := hres.Content.Text, []byte("response body"); !bytes.Equal(got, want) { t.Errorf("hres.Content.Text: got %q, want %q", got, want) } if got, want := len(hres.Headers), 3; got != want { t.Fatalf("len(hreq.Headers): got %d, want %d", got, want) } for _, h := range hres.Headers { var want string switch h.Name { case "Response-Header": want = "first, second" case "Location": want = "google.com" case "Set-Cookie": want = cookie.String() default: t.Errorf("hres.Headers: got %q, want header to not be present", h.Name) continue } if got := h.Value; got != want { t.Errorf("hres.Headers[%q]: got %q, want %q", h.Name, got, want) } } if got, want := len(hres.Cookies), 1; got != want { t.Fatalf("len(hres.Cookies): got %d, want %d", got, want) } hcookie := hres.Cookies[0] if got, want := hcookie.Name, "response"; got != want { t.Errorf("hcookie.Name: got %q, want %q", got, want) } if got, want := hcookie.Value, "cookie"; got != want { t.Errorf("hcookie.Value: got %q, want %q", got, want) } if got, want := hcookie.Path, "/"; got != want { t.Errorf("hcookie.Path: got %q, want %q", got, want) } if got, want := hcookie.Domain, "example.com"; got != want { t.Errorf("hcookie.Domain: got %q, want %q", got, want) } if got, want := hcookie.Expires, expires; got.Equal(want) { t.Errorf("hcookie.Expires: got %s, want %s", got, want) } if !hcookie.HTTPOnly { t.Error("hcookie.HTTPOnly: got false, want true") } if !hcookie.Secure { t.Error("hcookie.Secure: got false, want true") } }
func TestExportHandlerServeHTTP(t *testing.T) { logger := NewLogger() req, err := http.NewRequest("GET", "http://example.com", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } _, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() if err := logger.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } res := proxyutil.NewResponse(200, nil, req) if err := logger.ModifyResponse(res); err != nil { t.Fatalf("ModifyResponse(): got %v, want no error", err) } h := NewExportHandler(logger) req, err = http.NewRequest("GET", "/", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } rw := httptest.NewRecorder() h.ServeHTTP(rw, req) if got, want := rw.Code, http.StatusOK; got != want { t.Errorf("rw.Code: got %d, want %d", got, want) } hl := &HAR{} if err := json.Unmarshal(rw.Body.Bytes(), hl); err != nil { t.Fatalf("json.Unmarshal(): got %v, want no error", err) } if got, want := len(hl.Log.Entries), 1; got != want { t.Fatalf("len(hl.Log.Entries): got %v, want %v", got, want) } entry := hl.Log.Entries[0] if got, want := entry.Request.URL, "http://example.com"; got != want { t.Errorf("Request.URL: got %q, want %q", got, want) } if got, want := entry.Response.Status, 200; got != want { t.Errorf("Response.Status: got %d, want %d", got, want) } rh := NewResetHandler(logger) req, err = http.NewRequest("DELETE", "/", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } rw = httptest.NewRecorder() rh.ServeHTTP(rw, req) req, err = http.NewRequest("GET", "/", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } rw = httptest.NewRecorder() h.ServeHTTP(rw, req) if got, want := rw.Code, http.StatusOK; got != want { t.Errorf("rw.Code: got %d, want %d", got, want) } hl = &HAR{} if err := json.Unmarshal(rw.Body.Bytes(), hl); err != nil { t.Fatalf("json.Unmarshal(): got %v, want no error", err) } if got, want := len(hl.Log.Entries), 0; got != want { t.Errorf("len(Log.Entries): got %v, want %v", got, want) } }
func TestProxyAuthInvalidCredentials(t *testing.T) { m := NewModifier() autherr := errors.New("auth error") tm := martiantest.NewModifier() tm.RequestFunc(func(req *http.Request) { ctx := martian.NewContext(req) actx := auth.FromContext(ctx) actx.SetError(autherr) }) tm.ResponseFunc(func(res *http.Response) { ctx := martian.NewContext(res.Request) actx := auth.FromContext(ctx) actx.SetError(autherr) }) m.SetRequestModifier(tm) m.SetResponseModifier(tm) req, err := http.NewRequest("GET", "http://example.com", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } req.Header.Set("Proxy-Authorization", "Basic "+encode("user:pass")) ctx, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() if err := m.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } if !tm.RequestModified() { t.Error("tm.RequestModified(): got false, want true") } actx := auth.FromContext(ctx) if actx.Error() != autherr { t.Fatalf("auth.Error(): got %v, want %v", actx.Error(), autherr) } actx.SetError(nil) res := proxyutil.NewResponse(200, nil, req) if err := m.ModifyResponse(res); err != nil { t.Fatalf("ModifyResponse(): got %v, want no error", err) } if !tm.ResponseModified() { t.Error("tm.ResponseModified(): got false, want true") } if actx.Error() != autherr { t.Fatalf("auth.Error(): got %v, want %v", actx.Error(), autherr) } if got, want := res.StatusCode, http.StatusProxyAuthRequired; got != want { t.Errorf("res.StatusCode: got %d, want %d", got, want) } if got, want := res.Header.Get("Proxy-Authenticate"), "Basic"; got != want { t.Errorf("res.Header.Get(%q): got %q, want %q", "Proxy-Authenticate", got, want) } }
func TestOptionResponseBodyLogging(t *testing.T) { req, err := http.NewRequest("GET", "http://example.com", nil) if err != nil { t.Fatalf("NewRequest(): got %v, want no error", err) } _, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() bdr := strings.NewReader("{\"response\": \"body\"}") res := proxyutil.NewResponse(200, bdr, req) res.ContentLength = int64(bdr.Len()) res.Header.Set("Content-Type", "application/json") logger := NewLogger() if err := logger.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } if err := logger.ModifyResponse(res); err != nil { t.Fatalf("ModifyResponse(): got %v, want no error", err) } log := logger.Export().Log if got, want := len(log.Entries), 1; got != want { t.Fatalf("len(log.Entries): got %d, want %d", got, want) } if got, want := string(log.Entries[0].Response.Content.Text), "{\"response\": \"body\"}"; got != want { t.Fatalf("log.Entries[0].Response.Content.Text: got %s, want %s", got, want) } logger = NewLogger() logger.SetOption(BodyLogging(false)) if err := logger.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } if err := logger.ModifyResponse(res); err != nil { t.Fatalf("ModifyResponse(): got %v, want no error", err) } log = logger.Export().Log if got, want := len(log.Entries), 1; got != want { t.Fatalf("len(log.Entries): got %d, want %d", got, want) } if got, want := string(log.Entries[0].Response.Content.Text), ""; got != want { t.Fatalf("log.Entries[0].Response.Content: got %s, want %s", got, want) } logger = NewLogger() logger.SetOption(BodyLoggingForContentTypes("application/json")) if err := logger.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } if err := logger.ModifyResponse(res); err != nil { t.Fatalf("ModifyResponse(): got %v, want no error", err) } log = logger.Export().Log if got, want := string(log.Entries[0].Response.Content.Text), "{\"response\": \"body\"}"; got != want { t.Fatalf("log.Entries[0].Response.Content: got %s, want %s", got, want) } logger = NewLogger() logger.SetOption(SkipBodyLoggingForContentTypes("application/json")) if err := logger.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } if err := logger.ModifyResponse(res); err != nil { t.Fatalf("ModifyResponse(): got %v, want no error", err) } log = logger.Export().Log if got, want := string(log.Entries[0].Response.Content.Text), ""; got != want { t.Fatalf("log.Entries[0].Response.Content: got %v, want %v", got, want) } }
func TestModifyRequest(t *testing.T) { m := NewModifier() m.SetRequestModifier(nil) req, err := http.NewRequest("CONNECT", "https://www.example.com", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } ctx, remove, err := martian.TestContext(req) if err != nil { t.Fatalf("martian.TestContext(): got %v, want no error", err) } defer remove() if err := m.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } actx := auth.FromContext(ctx) if got, want := actx.ID(), ""; got != want { t.Errorf("actx.ID(): got %q, want %q", got, want) } // IP with port and modifier with error. tm := martiantest.NewModifier() reqerr := errors.New("request error") tm.RequestError(reqerr) req.RemoteAddr = "1.1.1.1:8111" m.SetRequestModifier(tm) if err := m.ModifyRequest(req); err != reqerr { t.Fatalf("ModifyConnectRequest(): got %v, want %v", err, reqerr) } if got, want := actx.ID(), "1.1.1.1"; got != want { t.Errorf("actx.ID(): got %q, want %q", got, want) } // IP without port and modifier with auth error. req.RemoteAddr = "4.4.4.4" autherr := errors.New("auth error") tm.RequestError(nil) tm.RequestFunc(func(req *http.Request) { ctx := martian.NewContext(req) actx := auth.FromContext(ctx) actx.SetError(autherr) }) if err := m.ModifyRequest(req); err != nil { t.Fatalf("ModifyRequest(): got %v, want no error", err) } if got, want := actx.ID(), ""; got != want { t.Errorf("actx.ID(): got %q, want %q", got, want) } }