Example #1
0
func TestFilter(t *testing.T) {
	f := NewFilter()
	if reqmod := f.RequestModifier("id"); reqmod != nil {
		t.Fatalf("f.RequestModifier(%q): got reqmod, want no reqmod", "id")
	}
	if resmod := f.ResponseModifier("id"); resmod != nil {
		t.Fatalf("f.ResponseModifier(%q): got resmod, want no resmod", "id")
	}

	f.SetRequestModifier("id", martian.RequestModifierFunc(
		func(*martian.Context, *http.Request) error {
			return nil
		}))

	f.SetResponseModifier("id", martian.ResponseModifierFunc(
		func(*martian.Context, *http.Response) error {
			return nil
		}))

	if reqmod := f.RequestModifier("id"); reqmod == nil {
		t.Errorf("f.RequestModifier(%q): got no reqmod, want reqmod", "id")
	}
	if resmod := f.ResponseModifier("id"); resmod == nil {
		t.Errorf("f.ResponseModifier(%q): got no resmod, want resmod", "id")
	}

	f.SetRequestModifier("id", nil)
	f.SetResponseModifier("id", nil)
	if reqmod := f.RequestModifier("id"); reqmod != nil {
		t.Fatalf("f.RequestModifier(%q): got reqmod, want no reqmod", "id")
	}
	if resmod := f.ResponseModifier("id"); resmod != nil {
		t.Fatalf("f.ResponseModifier(%q): got resmod, want no resmod", "id")
	}
}
Example #2
0
func TestModifyRequest(t *testing.T) {
	m := NewModifier()
	ctx := martian.NewContext()

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

	if err := m.ModifyRequest(ctx, req); err != nil {
		t.Fatalf("ModifyRequest(): got %v, want no error", err)
	}
	if got, want := ctx.Auth.ID, "user:pass"; got != want {
		t.Fatalf("ctx.Auth.ID: got %q, want %q", got, want)
	}

	modifierRun := false
	m.SetRequestModifier(martian.RequestModifierFunc(
		func(*martian.Context, *http.Request) error {
			modifierRun = true
			return nil
		}))

	if err := m.ModifyRequest(ctx, req); err != nil {
		t.Fatalf("ModifyRequest(): got %v, want no error", err)
	}
	if !modifierRun {
		t.Error("modifierRun: got false, want true")
	}
}
Example #3
0
func TestModifyRequest(t *testing.T) {
	f := NewFilter()

	modifierRun := false
	f.SetRequestModifier("id", martian.RequestModifierFunc(
		func(*martian.Context, *http.Request) error {
			modifierRun = true
			return nil
		}))

	req, err := http.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatalf("NewRequest(): got %v, want no error", err)
	}
	ctx := martian.NewContext()

	// No ID, auth required.
	f.SetAuthRequired(true)

	if err := f.ModifyRequest(ctx, req); err != nil {
		t.Fatalf("ModifyRequest(): got %v, want no error", err)
	}
	if ctx.Auth.Error == nil {
		t.Error("ctx.Auth.Error: got nil, want error")
	}
	if modifierRun {
		t.Error("modifierRun: got true, want false")
	}

	// No ID, auth not required.
	f.SetAuthRequired(false)
	ctx.Auth.Error = nil

	if err := f.ModifyRequest(ctx, req); err != nil {
		t.Fatalf("ModifyRequest(): got %v, want no error", err)
	}
	if ctx.Auth.Error != nil {
		t.Errorf("ctx.Auth.Error: got %v, want no error", err)
	}
	if modifierRun {
		t.Error("modifierRun: got true, want false")
	}

	// Valid ID.
	ctx.Auth.ID = "id"
	ctx.Auth.Error = nil
	if err := f.ModifyRequest(ctx, req); err != nil {
		t.Fatalf("ModifyRequest(): got %v, want no error", err)
	}
	if ctx.Auth.Error != nil {
		t.Errorf("ctx.Auth.Error: got %v, want no error", ctx.Auth.Error)
	}
	if !modifierRun {
		t.Error("modifierRun: got false, want true")
	}
}
Example #4
0
func TestPriorityGroupModifyRequest(t *testing.T) {
	var priorities []int64

	pg := NewGroup()
	f := func(*martian.Context, *http.Request) error {
		priorities = append(priorities, 50)
		return nil
	}
	pg.AddRequestModifier(martian.RequestModifierFunc(f), 50)

	f = func(*martian.Context, *http.Request) error {
		priorities = append(priorities, 100)
		return nil
	}
	pg.AddRequestModifier(martian.RequestModifierFunc(f), 100)

	f = func(*martian.Context, *http.Request) error {
		priorities = append(priorities, 75)
		return nil
	}

	// Functions are not directly comparable, so we must wrap in a
	// type that is.
	m := &struct{ martian.RequestModifier }{martian.RequestModifierFunc(f)}
	if err := pg.RemoveRequestModifier(m); err != ErrModifierNotFound {
		t.Fatalf("RemoveRequestModifier(): got %v, want ErrModifierNotFound", err)
	}
	pg.AddRequestModifier(m, 75)
	if err := pg.RemoveRequestModifier(m); err != nil {
		t.Fatalf("RemoveRequestModifier(): got %v, want no error", err)
	}

	req, err := http.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatalf("http.NewRequest(): got %v, want no error", err)
	}
	if err := pg.ModifyRequest(martian.NewContext(), req); err != nil {
		t.Fatalf("ModifyRequest(): got %v, want no error", err)
	}
	if got, want := priorities, []int64{100, 50}; !reflect.DeepEqual(got, want) {
		t.Fatalf("reflect.DeepEqual(%v, %v): got false, want true", got, want)
	}
}
Example #5
0
func TestModifyResponseResetAuth(t *testing.T) {
	auth := NewModifier()

	auth.SetRequestModifier(martian.RequestModifierFunc(
		func(ctx *martian.Context, req *http.Request) error {
			if ctx.Auth.ID != "secret:pass" {
				ctx.Auth.Error = errors.New("invalid auth")
			}
			return nil
		}))

	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("wrong:pass"))

	ctx := martian.NewContext()
	// This will set ctx.Auth.Error since the ID isn't "secret:pass".
	if err := auth.ModifyRequest(ctx, req); err != nil {
		t.Fatalf("ModifyRequest(): got %v, want no error", err)
	}

	res := proxyutil.NewResponse(200, nil, req)
	if err := auth.ModifyResponse(ctx, res); err != nil {
		t.Fatalf("ModifyResponse(): got %v, want no error", err)
	}
	if got, want := res.StatusCode, 407; got != want {
		t.Errorf("res.StatusCode: got %d, want %d", got, want)
	}

	if got, want := ctx.Auth.ID, ""; got != want {
		t.Errorf("ctx.Auth.ID: got %q, want %q", got, want)
	}
	if err := ctx.Auth.Error; err != nil {
		t.Errorf("ctx.Auth.Error: got %v, want no error", err)
	}

	// This will be successful because the ID is "secret:pass".
	req.Header.Set("Proxy-Authorization", "Basic "+encode("secret:pass"))
	if err := auth.ModifyRequest(ctx, req); err != nil {
		t.Fatalf("ModifyRequest(): got %v, want no error", err)
	}

	// Reset the response.
	res = proxyutil.NewResponse(200, nil, req)
	if err := auth.ModifyResponse(ctx, res); err != nil {
		t.Fatalf("ModifyResponse(): got %v, want no error", err)
	}
	if got, want := res.StatusCode, 200; got != want {
		t.Errorf("res.StatusCode: got %d, want %d", got, want)
	}
}
Example #6
0
// NewViaModifier sets the Via header.
//
// If Via is already present, via is appended to the existing value.
//
// http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-14#section-9.9
func NewViaModifier(via string) martian.RequestModifier {
	return martian.RequestModifierFunc(
		func(ctx *martian.Context, req *http.Request) error {
			if v := req.Header.Get("Via"); v != "" {
				via = v + ", " + via
			}

			req.Header.Set("Via", via)

			return nil
		})
}
func TestFilterWithQueryStringNameAndValue(t *testing.T) {
	name, value := "name", "value"
	nameMatcher, err := regexp.Compile(name)
	if err != nil {
		t.Fatalf("regexp.Compile(%q): got %v, want no error", name, err)
	}
	valueMatcher, err := regexp.Compile(value)
	if err != nil {
		t.Fatalf("regexp.Compile(%q): got %v, want no error", value, err)
	}

	modifierRun := false
	f := func(*martian.Context, *http.Request) error {
		modifierRun = true
		return nil
	}
	filter, err := NewFilter(nameMatcher, valueMatcher)
	if err != nil {
		t.Fatalf("NewFilter(): got %v, want no error", err)
	}

	filter.SetRequestModifier(martian.RequestModifierFunc(f))

	v := url.Values{}
	v.Add("nomatch", "value")
	req, err := http.NewRequest("POST", "http://martian.local?name=value", strings.NewReader(v.Encode()))
	if err != nil {
		t.Fatalf("http.NewRequest(): got %v, want no error", err)
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	if err := filter.ModifyRequest(martian.NewContext(), req); err != nil {
		t.Errorf("ModifyRequest(): got %v, want no error", err)
	}
	if !modifierRun {
		t.Error("modifierRun: got false, want true")
	}

	v = url.Values{}
	req, err = http.NewRequest("POST", "http://martian.local", strings.NewReader(v.Encode()))
	if err != nil {
		t.Fatalf("http.NewRequest(): got %v, want no error", err)
	}

	modifierRun = false
	if err := filter.ModifyRequest(martian.NewContext(), req); err != nil {
		t.Errorf("ModifyRequest(): got %v, want no error", err)
	}
	if modifierRun {
		t.Error("modifierRun: got true, want false")
	}
}
Example #8
0
func TestModifyRequestHaltsOnError(t *testing.T) {
	mg := NewGroup()
	errHalt := errors.New("halt modifier chain")
	f := func(*martian.Context, *http.Request) error {
		return errHalt
	}
	mg.AddRequestModifier(martian.RequestModifierFunc(f))

	f = func(*martian.Context, *http.Request) error {
		t.Fatal("ModifyRequest(): got called, want skipped")
		return nil
	}
	mg.AddRequestModifier(martian.RequestModifierFunc(f))

	req, err := http.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatalf("http.NewRequest(): got %v, want no error", err)
	}
	if err := mg.ModifyRequest(martian.NewContext(), req); err != errHalt {
		t.Fatalf("mg.ModifyRequest(): got %v, want %v", err, errHalt)
	}
}
Example #9
0
func TestFromJSON(t *testing.T) {
	wasRun := false

	Register("parse.Key", func(b []byte) (*Result, error) {
		m := martian.RequestModifierFunc(
			func(*martian.Context, *http.Request) error {
				wasRun = true
				return nil
			})

		msg := &struct {
			Scope []ModifierType `json:"scope"`
		}{}

		if err := json.Unmarshal(b, msg); err != nil {
			return nil, err
		}

		return NewResult(m, msg.Scope)
	})

	rawMsg := []byte(`{
	  "parse.Key": {
      "scope":["request"]
    }
	}`)

	r, err := FromJSON(rawMsg)
	if err != nil {
		t.Fatalf("FromJSON(): got %v, want no error", err)
	}

	reqmod := r.RequestModifier()
	if reqmod == nil {
		t.Fatal("FromJSON(): got nil, want not nil")
	}

	resmod := r.ResponseModifier()
	if resmod != nil {
		t.Fatal("FromJSON(): got nil, want not nil")
	}

	err = reqmod.ModifyRequest(nil, nil)
	if err != nil {
		t.Fatalf("reqmod.ModifyRequest(): got %v, want no error", err)
	}
	if !wasRun {
		t.Error("FromJSON(): got false, want true")
	}

}
Example #10
0
func TestPriorityGroupModifyRequestHaltsOnError(t *testing.T) {
	pg := NewGroup()

	errHalt := errors.New("modifier chain halted")
	f := func(*martian.Context, *http.Request) error {
		return errHalt
	}
	pg.AddRequestModifier(martian.RequestModifierFunc(f), 100)

	f = func(*martian.Context, *http.Request) error {
		t.Fatal("ModifyRequest(): got called, want skipped")
		return nil
	}
	pg.AddRequestModifier(martian.RequestModifierFunc(f), 75)

	req, err := http.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatalf("http.NewRequest(): got %v, want no error", err)
	}
	if err := pg.ModifyRequest(martian.NewContext(), req); err != errHalt {
		t.Fatalf("ModifyRequest(): got %v, want errHalt", err)
	}
}
Example #11
0
// NewBadFramingModifier makes a best effort to fix inconsistencies in the
// request such as multiple Content-Lengths or the lack of Content-Length and
// improper Transfer-Encoding. If it is unable to determine a proper resolution
// it returns an error.
//
// http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-14#section-3.3
func NewBadFramingModifier() martian.RequestModifier {
	return martian.RequestModifierFunc(
		func(ctx *martian.Context, req *http.Request) error {
			cls := req.Header["Content-Length"]
			if len(cls) > 0 {
				var length string

				// Iterate over all Content-Length headers, splitting any we find with
				// commas, and check that all Content-Lengths are equal.
				for _, ls := range cls {
					for _, l := range strings.Split(ls, ",") {
						// First length, set it as the canonical Content-Length.
						if length == "" {
							length = strings.TrimSpace(l)
							continue
						}

						// Mismatched Content-Lengths.
						if length != strings.TrimSpace(l) {
							return fmt.Errorf(`bad request framing: multiple mismatched "Content-Length" headers: %v`, cls)
						}
					}
				}

				// All Content-Lengths are equal, remove extras and set it to the
				// canonical value.
				req.Header.Set("Content-Length", length)
			}

			tes := req.Header["Transfer-Encoding"]
			if len(tes) > 0 {
				// Extract the last Transfer-Encoding value, and split on commas.
				last := strings.Split(tes[len(tes)-1], ",")

				// Check that the last, potentially comma-delimited, value is
				// "chunked", else we have no way to determine when the request is
				// finished.
				if strings.TrimSpace(last[len(last)-1]) != "chunked" {
					return fmt.Errorf(`bad request framing: "Transfer-Encoding" header is present, but does not end in "chunked"`)
				}

				// Transfer-Encoding "chunked" takes precedence over
				// Content-Length.
				req.Header.Del("Content-Length")
			}

			return nil
		})
}
func TestFilterWithQueryParamNameAndNoValue(t *testing.T) {
	name := "name"
	nameMatcher, err := regexp.Compile(name)
	if err != nil {
		t.Fatalf("regexp.Compile(%q): got %v, want no error", name, err)
	}

	modifierRun := false
	f := func(*martian.Context, *http.Request) error {
		modifierRun = true
		return nil
	}
	filter, err := NewFilter(nameMatcher, nil)
	if err != nil {
		t.Fatalf("NewFilter(): got %v, want no error", err)
	}

	filter.SetRequestModifier(martian.RequestModifierFunc(f))

	req, err := http.NewRequest("GET", "http://martian.local?name", nil)
	if err != nil {
		t.Fatalf("http.NewRequest(): got %v, want no error", err)
	}

	if err := filter.ModifyRequest(martian.NewContext(), req); err != nil {
		t.Errorf("ModifyRequest(): got %v, want no error", err)
	}
	if !modifierRun {
		t.Error("modifierRun: got false, want true")
	}

	req, err = http.NewRequest("GET", "http://martian.local?test", nil)
	if err != nil {
		t.Fatalf("http.NewRequest(): got %v, want no error", err)
	}

	modifierRun = false
	if err := filter.ModifyRequest(martian.NewContext(), req); err != nil {
		t.Errorf("ModifyRequest(): got %v, want no error", err)
	}
	if modifierRun {
		t.Error("modifierRun: got true, want false")
	}
}
Example #13
0
func TestModifyRequest(t *testing.T) {
	tt := []struct {
		name   string
		values []string
		want   bool
	}{
		{
			name:   "Martian-Production",
			values: []string{"true"},
			want:   false,
		},
		{
			name:   "Martian-Testing",
			values: []string{"see-next-value", "true"},
			want:   true,
		},
	}

	for i, tc := range tt {
		var got bool

		f := NewFilter("mARTian-teSTInG", "true")
		f.SetRequestModifier(martian.RequestModifierFunc(
			func(*martian.Context, *http.Request) error {
				got = true
				return nil
			}))

		req, err := http.NewRequest("GET", "http://example.com", nil)
		if err != nil {
			t.Fatalf("%d. http.NewRequest(): got %v, want no error", i, err)
		}
		req.Header[tc.name] = tc.values

		if err := f.ModifyRequest(martian.NewContext(), req); err != nil {
			t.Fatalf("%d. ModifyRequest(): got %v, want no error", i, err)
		}

		if got != tc.want {
			t.Errorf("%d. modifier run: got %t, want %t", i, got, tc.want)
		}
	}
}
Example #14
0
// NewForwardedModifier sets the X-Forwarded-For and X-Forwarded-Proto headers.
//
// If X-Forwarded-For is already present, the client IP is appended to
// the existing value.
//
// TODO: Support "Forwarded" header.
// see: http://tools.ietf.org/html/rfc7239
func NewForwardedModifier() martian.RequestModifier {
	return martian.RequestModifierFunc(
		func(req *http.Request) error {
			req.Header.Set("X-Forwarded-Proto", req.URL.Scheme)

			xff, _, err := net.SplitHostPort(req.RemoteAddr)
			if err != nil {
				xff = req.RemoteAddr
			}

			if v := req.Header.Get("X-Forwarded-For"); v != "" {
				xff = v + ", " + xff
			}

			req.Header.Set("X-Forwarded-For", xff)

			return nil
		})
}
Example #15
0
func TestResultRequestResponseModifierCorrectScope(t *testing.T) {
	mod := struct {
		martian.RequestModifier
		martian.ResponseModifier
	}{
		RequestModifier: martian.RequestModifierFunc(
			func(*martian.Context, *http.Request) error {
				return nil
			}),
		ResponseModifier: martian.ResponseModifierFunc(
			func(*martian.Context, *http.Response) error {
				return nil
			}),
	}
	result := &Result{
		reqmod: mod,
		resmod: nil,
	}
	reqmod := result.RequestModifier()
	if reqmod == nil {
		t.Error("result.RequestModifier: got nil, want not nil")
	}

	resmod := result.ResponseModifier()
	if resmod != nil {
		t.Errorf("result.ResponseModifier: got %v, want nil", resmod)
	}

	result = &Result{
		reqmod: nil,
		resmod: mod,
	}
	reqmod = result.RequestModifier()
	if reqmod != nil {
		t.Errorf("result.RequestModifier: got %v, want nil", reqmod)
	}

	resmod = result.ResponseModifier()
	if resmod == nil {
		t.Error("result.ResponseModifier: got nil, want not nil")
	}
}
Example #16
0
func TestModifyRequest(t *testing.T) {
	mg := NewGroup()

	modifierRun := false
	f := func(*martian.Context, *http.Request) error {
		modifierRun = true
		return nil
	}
	mg.AddRequestModifier(martian.RequestModifierFunc(f))

	req, err := http.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatalf("http.NewRequest(): got %v, want no error", err)
	}
	if err := mg.ModifyRequest(martian.NewContext(), req); err != nil {
		t.Fatalf("mg.ModifyRequest(): got %v, want no error", err)
	}
	if !modifierRun {
		t.Error("modifierRun: got false, want true")
	}
}
Example #17
0
func TestNewResultMismatchedScopes(t *testing.T) {
	reqmod := martian.RequestModifierFunc(
		func(*http.Request) error {
			return nil
		})
	resmod := martian.ResponseModifierFunc(
		func(*http.Response) error {
			return nil
		})

	if _, err := NewResult(reqmod, []ModifierType{Response}); err == nil {
		t.Error("NewResult(reqmod, RESPONSE): got nil, want error")
	}

	if _, err := NewResult(resmod, []ModifierType{Request}); err == nil {
		t.Error("NewResult(resmod, REQUEST): got nil, want error")
	}

	if _, err := NewResult(reqmod, []ModifierType{ModifierType("unknown")}); err == nil {
		t.Error("NewResult(resmod, REQUEST): got nil, want error")
	}
}
Example #18
0
func TestModifyRequest(t *testing.T) {
	m := NewModifier()

	var modRun bool
	m.reqmod = martian.RequestModifierFunc(
		func(*martian.Context, *http.Request) error {
			modRun = true
			return nil
		})

	req, err := http.NewRequest("GET", "http://example.com", nil)
	if err != nil {
		t.Fatalf("http.NewRequest(): got %v, want no error", err)
	}

	if err := m.ModifyRequest(martian.NewContext(), req); err != nil {
		t.Fatalf("ModifyRequest(): got %v, want no error", err)
	}
	if !modRun {
		t.Error("modRun: got false, want true")
	}
}