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 := session.FromContext(nil) martian.SetContext(req, ctx) defer martian.RemoveContext(req) 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.Context(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) } }
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, err := session.FromContext(nil) if err != nil { t.Fatalf("session.FromContext(): got %v, want no error", err) } martian.SetContext(req, ctx) defer martian.RemoveContext(req) 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.Context(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 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, err := session.FromContext(nil) if err != nil { t.Fatalf("session.FromContext(): got %v, want no error", err) } martian.SetContext(req, ctx) defer martian.RemoveContext(req) 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) } }
// ModifyResponse runs resmod.ModifyResponse and modifies the response to // include the correct status code and headers if auth error is present. // // If an error is returned from resmod.ModifyResponse it is returned. func (m *Modifier) ModifyResponse(res *http.Response) error { ctx := martian.NewContext(res.Request) actx := auth.FromContext(ctx) err := m.resmod.ModifyResponse(res) if actx.Error() != nil { res.StatusCode = http.StatusProxyAuthRequired res.Header.Set("Proxy-Authenticate", "Basic") } return err }
// ModifyResponse runs resmod.ModifyResponse. // // If an error is returned from resmod.ModifyResponse it is returned. func (m *Modifier) ModifyResponse(res *http.Response) error { ctx := martian.NewContext(res.Request) actx := auth.FromContext(ctx) err := m.resmod.ModifyResponse(res) if actx.Error() != nil { res.StatusCode = 403 res.Status = http.StatusText(403) } return err }
// ModifyRequest sets the auth ID in the context from the request iff it has // not already been set and runs reqmod.ModifyRequest. If the underlying // modifier has indicated via auth error that no valid auth credentials // have been found we set ctx.SkipRoundTrip. func (m *Modifier) ModifyRequest(req *http.Request) error { ctx := martian.NewContext(req) actx := auth.FromContext(ctx) actx.SetID(id(req.Header)) err := m.reqmod.ModifyRequest(req) if actx.Error() != nil { ctx.SkipRoundTrip() } return err }
// ModifyRequest sets the auth ID in the context from the request iff it has // not already been set and runs reqmod.ModifyRequest. If the underlying // modifier has indicated via auth error that no valid auth credentials // have been found we set ctx.SkipRoundTrip. func (m *Modifier) ModifyRequest(req *http.Request) error { ctx := martian.NewContext(req) actx := auth.FromContext(ctx) ip, _, err := net.SplitHostPort(req.RemoteAddr) if err != nil { ip = req.RemoteAddr } actx.SetID(ip) err = m.reqmod.ModifyRequest(req) if actx.Error() != nil { ctx.SkipRoundTrip() } return err }
func TestProxyAuthInvalidCredentials(t *testing.T) { m := NewModifier() autherr := errors.New("auth error") tm := martiantest.NewModifier() tm.RequestFunc(func(req *http.Request) { ctx := martian.Context(req) actx := auth.FromContext(ctx) actx.SetError(autherr) }) tm.ResponseFunc(func(res *http.Response) { ctx := martian.Context(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, err := session.FromContext(nil) if err != nil { t.Fatalf("session.FromContext(): got %v, want no error", err) } martian.SetContext(req, ctx) defer martian.RemoveContext(req) 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) } }