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) } }
// ModifyResponse sets the status code to 400 Bad Request if a loop was // detected in the request. func (m *viaModifier) ModifyResponse(res *http.Response) error { ctx := martian.Context(res.Request) if err, _ := ctx.Get(viaLoopKey); err != nil { res.StatusCode = 400 res.Status = http.StatusText(400) return err.(error) } return nil }
// 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.Context(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.Context(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 runs the RequestModifier for the associated auth ID. If no // modifier is found for auth ID then auth error is set. func (f *Filter) ModifyRequest(req *http.Request) error { ctx := martian.Context(req) actx := FromContext(ctx) if reqmod, ok := f.reqmods[actx.ID()]; ok { return reqmod.ModifyRequest(req) } if err := f.requireKnownAuth(actx.ID()); err != nil { actx.SetError(err) } return nil }
// 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.Context(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.Context(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 }
// ModifyRequest sets the Via header and provides loop-detection. If Via is // already present, it will be appended to the existing value. If a loop is // detected an error is added to the context and the request round trip is // skipped. // // http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-14#section-9.9 func (m *viaModifier) ModifyRequest(req *http.Request) error { via := fmt.Sprintf("%d.%d %s", req.ProtoMajor, req.ProtoMinor, m.requestedBy) if v := req.Header.Get("Via"); v != "" { if strings.Contains(v, m.requestedBy) { err := fmt.Errorf("via: detected request loop, header contains %s", m.requestedBy) ctx := martian.Context(req) ctx.Set(viaLoopKey, err) ctx.SkipRoundTrip() return err } via = fmt.Sprintf("%s, %s", v, via) } req.Header.Set("Via", via) return nil }
// ModifyResponse logs responses. func (l *Logger) ModifyResponse(res *http.Response) error { ctx := martian.Context(res.Request) id := ctx.ID() return l.LogResponse(id, res) }
// ModifyRequest logs requests. func (l *Logger) ModifyRequest(req *http.Request) error { ctx := martian.Context(req) id := ctx.ID() return l.LogRequest(id, req) }
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) } }