Example #1
0
func TestStatus(t *testing.T) {
	status := Status{
		Rules: []httpserver.HandlerConfig{
			NewRule("/foo", http.StatusNotFound),
			NewRule("/teapot", http.StatusTeapot),
			NewRule("/foo/bar1", http.StatusInternalServerError),
			NewRule("/temporary-redirected", http.StatusTemporaryRedirect),
		},
		Next: httpserver.HandlerFunc(urlPrinter),
	}

	tests := []struct {
		path           string
		statusExpected bool
		status         int
	}{
		{"/foo", true, http.StatusNotFound},
		{"/teapot", true, http.StatusTeapot},
		{"/foo/bar", true, http.StatusNotFound},
		{"/foo/bar1", true, http.StatusInternalServerError},
		{"/someotherpath", false, 0},
		{"/temporary-redirected", false, http.StatusTemporaryRedirect},
	}

	for i, test := range tests {
		req, err := http.NewRequest("GET", test.path, nil)
		if err != nil {
			t.Fatalf("Test %d: Could not create HTTP request: %v",
				i, err)
		}

		rec := httptest.NewRecorder()
		actualStatus, err := status.ServeHTTP(rec, req)
		if err != nil {
			t.Fatalf("Test %d: Serving request failed with error %v",
				i, err)
		}

		if test.statusExpected {
			if test.status != actualStatus {
				t.Errorf("Test %d: Expected status code %d, got %d",
					i, test.status, actualStatus)
			}
			if rec.Body.String() != "" {
				t.Errorf("Test %d: Expected empty body, got '%s'",
					i, rec.Body.String())
			}
		} else {
			if test.status != 0 { // Expecting status in response
				if test.status != rec.Code {
					t.Errorf("Test %d: Expected status code %d, got %d",
						i, test.status, rec.Code)
				}
			} else if rec.Body.String() != test.path {
				t.Errorf("Test %d: Expected body '%s', got '%s'",
					i, test.path, rec.Body.String())
			}
		}
	}
}
Example #2
0
func TestMultipleHeaders(t *testing.T) {
	he := Headers{
		Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			fmt.Fprint(w, "This is a test")
			return 0, nil
		}),
		Rules: []Rule{
			{Path: "/a", Headers: []Header{
				{Name: "+Link", Value: "</images/image.png>; rel=preload"},
				{Name: "+Link", Value: "</css/main.css>; rel=preload"},
			}},
		},
	}

	req, err := http.NewRequest("GET", "/a", nil)
	if err != nil {
		t.Fatalf("Could not create HTTP request: %v", err)
	}

	rec := httptest.NewRecorder()
	he.ServeHTTP(rec, req)

	desiredHeaders := []string{"</css/main.css>; rel=preload", "</images/image.png>; rel=preload"}
	actualHeaders := rec.HeaderMap[http.CanonicalHeaderKey("Link")]
	sort.Strings(actualHeaders)

	if !reflect.DeepEqual(desiredHeaders, actualHeaders) {
		t.Errorf("Expected header to contain: %v but got: %v", desiredHeaders, actualHeaders)
	}
}
Example #3
0
func TestExpVar(t *testing.T) {
	rw := ExpVar{
		Next:     httpserver.HandlerFunc(contentHandler),
		Resource: "/d/v",
	}

	tests := []struct {
		from   string
		result int
	}{
		{"/d/v", 0},
		{"/x/y", http.StatusOK},
	}

	for i, test := range tests {
		req, err := http.NewRequest("GET", test.from, nil)
		if err != nil {
			t.Fatalf("Test %d: Could not create HTTP request %v", i, err)
		}
		rec := httptest.NewRecorder()
		result, err := rw.ServeHTTP(rec, req)
		if err != nil {
			t.Fatalf("Test %d: Could not ServeHTTP %v", i, err)
		}
		if result != test.result {
			t.Errorf("Test %d: Expected Header '%d' but was '%d'",
				i, test.result, result)
		}
	}
}
Example #4
0
func nextFunc(ss string) httpserver.Handler {
	return httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
		if _, err := w.Write([]byte(ss)); err != nil {
			return 500, err
		}
		return 200, nil
	})
}
Example #5
0
func TestBrowseTemplate(t *testing.T) {
	tmpl, err := template.ParseFiles("testdata/photos.tpl")
	if err != nil {
		t.Fatalf("An error occured while parsing the template: %v", err)
	}

	b := Browse{
		Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			t.Fatalf("Next shouldn't be called")
			return 0, nil
		}),
		Configs: []Config{
			{
				PathScope: "/photos",
				Root:      http.Dir("./testdata"),
				Template:  tmpl,
			},
		},
	}

	req, err := http.NewRequest("GET", "/photos/", nil)
	if err != nil {
		t.Fatalf("Test: Could not create HTTP request: %v", err)
	}

	rec := httptest.NewRecorder()

	code, _ := b.ServeHTTP(rec, req)
	if code != http.StatusOK {
		t.Fatalf("Wrong status, expected %d, got %d", http.StatusOK, code)
	}

	respBody := rec.Body.String()
	expectedBody := `<!DOCTYPE html>
<html>
<head>
<title>Template</title>
</head>
<body>
<h1>Header</h1>

<h1>/photos/</h1>

<a href="./test.html">test.html</a><br>

<a href="./test2.html">test2.html</a><br>

<a href="./test3.html">test3.html</a><br>

</body>
</html>
`

	if respBody != expectedBody {
		t.Fatalf("Expected body: %v got: %v", expectedBody, respBody)
	}

}
Example #6
0
func TestLogRequestBody(t *testing.T) {
	var got bytes.Buffer
	logger := Logger{
		Rules: []*Rule{{
			PathScope: "/",
			Entries: []*Entry{{
				Format: "{request_body}",
				Log:    log.New(&got, "", 0),
			}},
		}},
		Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			// drain up body
			ioutil.ReadAll(r.Body)
			return 0, nil
		}),
	}

	for i, c := range []struct {
		body   string
		expect string
	}{
		{"", "\n"},
		{"{hello} world!", "{hello} world!\n"},
		{func() string {
			length := httpserver.MaxLogBodySize + 100
			b := make([]byte, length)
			for i := 0; i < length; i++ {
				b[i] = 0xab
			}
			return string(b)
		}(), func() string {
			b := make([]byte, httpserver.MaxLogBodySize)
			for i := 0; i < httpserver.MaxLogBodySize; i++ {
				b[i] = 0xab
			}
			return string(b) + "\n"
		}(),
		},
	} {
		got.Reset()
		r, err := http.NewRequest("POST", "/", bytes.NewBufferString(c.body))
		if err != nil {
			t.Fatal(err)
		}
		r.Header.Set("Content-Type", "application/json")
		status, err := logger.ServeHTTP(httptest.NewRecorder(), r)
		if status != 0 {
			t.Errorf("case %d: Expected status to be 0, but was %d", i, status)
		}
		if err != nil {
			t.Errorf("case %d: Expected error to be nil, instead got: %v", i, err)
		}
		if got.String() != c.expect {
			t.Errorf("case %d: Expected body %q, but got %q", i, c.expect, got.String())
		}
	}
}
Example #7
0
func genErrorHandler(status int, err error, body string) httpserver.Handler {
	return httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
		if len(body) > 0 {
			w.Header().Set("Content-Length", strconv.Itoa(len(body)))
			fmt.Fprint(w, body)
		}
		return status, err
	})
}
Example #8
0
func newTestHandler(t *testing.T, caddyFile string) *handler {
	c := caddy.NewTestController("http", caddyFile)
	mc, err := parse(c)
	if err != nil {
		t.Fatal(err)
	}
	h := newHandler(mc, nil)
	h.Next = httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
		return http.StatusTeapot, nil
	})
	return h
}
Example #9
0
func TestHeader(t *testing.T) {
	hostname, err := os.Hostname()
	if err != nil {
		t.Fatalf("Could not determine hostname: %v", err)
	}
	for i, test := range []struct {
		from  string
		name  string
		value string
	}{
		{"/a", "Foo", "Bar"},
		{"/a", "Bar", ""},
		{"/a", "Baz", ""},
		{"/a", "Server", ""},
		{"/a", "ServerName", hostname},
		{"/b", "Foo", ""},
		{"/b", "Bar", "Removed in /a"},
	} {
		he := Headers{
			Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
				w.Header().Set("Bar", "Removed in /a")
				w.WriteHeader(http.StatusOK)
				return 0, nil
			}),
			Rules: []Rule{
				{Path: "/a", Headers: http.Header{
					"Foo":        []string{"Bar"},
					"ServerName": []string{"{hostname}"},
					"-Bar":       []string{""},
					"-Server":    []string{},
				}},
			},
		}

		req, err := http.NewRequest("GET", test.from, nil)
		if err != nil {
			t.Fatalf("Test %d: Could not create HTTP request: %v", i, err)
		}

		rec := httptest.NewRecorder()
		// preset header
		rec.Header().Set("Server", "Caddy")

		he.ServeHTTP(rec, req)

		if got := rec.Header().Get(test.name); got != test.value {
			t.Errorf("Test %d: Expected %s header to be %q but was %q",
				i, test.name, test.value, got)
		}
	}
}
Example #10
0
func TestBasicAuth(t *testing.T) {
	rw := BasicAuth{
		Next: httpserver.HandlerFunc(contentHandler),
		Rules: []Rule{
			{Username: "******", Password: PlainMatcher("ttest"), Resources: []string{"/testing"}},
		},
	}

	tests := []struct {
		from   string
		result int
		cred   string
	}{
		{"/testing", http.StatusUnauthorized, "ttest:test"},
		{"/testing", http.StatusOK, "test:ttest"},
		{"/testing", http.StatusUnauthorized, ""},
	}

	for i, test := range tests {

		req, err := http.NewRequest("GET", test.from, nil)
		if err != nil {
			t.Fatalf("Test %d: Could not create HTTP request %v", i, err)
		}
		auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(test.cred))
		req.Header.Set("Authorization", auth)

		rec := httptest.NewRecorder()
		result, err := rw.ServeHTTP(rec, req)
		if err != nil {
			t.Fatalf("Test %d: Could not ServeHTTP %v", i, err)
		}
		if result != test.result {
			t.Errorf("Test %d: Expected Header '%d' but was '%d'",
				i, test.result, result)
		}
		if result == http.StatusUnauthorized {
			headers := rec.Header()
			if val, ok := headers["Www-Authenticate"]; ok {
				if val[0] != "Basic" {
					t.Errorf("Test %d, Www-Authenticate should be %s provided %s", i, "Basic", val[0])
				}
			} else {
				t.Errorf("Test %d, should provide a header Www-Authenticate", i)
			}
		}

	}

}
Example #11
0
func nextFunc(shouldMime bool, contentType string) httpserver.Handler {
	return httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
		if shouldMime {
			if w.Header().Get("Content-Type") != contentType {
				return 0, fmt.Errorf("expected Content-Type: %v, found %v", contentType, r.Header.Get("Content-Type"))
			}
			return 0, nil
		}
		if w.Header().Get("Content-Type") != "" {
			return 0, fmt.Errorf("Content-Type header not expected")
		}
		return 0, nil
	})
}
Example #12
0
func TestResponseFilterWriter(t *testing.T) {
	tests := []struct {
		body           string
		shouldCompress bool
	}{
		{"Hello\t\t\t\n", false},
		{"Hello the \t\t\t world is\n\n\n great", true},
		{"Hello \t\t\nfrom gzip", true},
		{"Hello gzip\n", false},
	}

	filters := []ResponseFilter{
		LengthFilter(15),
	}

	server := Gzip{Configs: []Config{
		{ResponseFilters: filters},
	}}

	for i, ts := range tests {
		server.Next = httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			w.Header().Set("Content-Length", fmt.Sprint(len(ts.body)))
			w.Write([]byte(ts.body))
			return 200, nil
		})

		r := urlRequest("/")
		r.Header.Set("Accept-Encoding", "gzip")

		w := httptest.NewRecorder()

		server.ServeHTTP(w, r)

		resp := w.Body.String()

		if !ts.shouldCompress {
			if resp != ts.body {
				t.Errorf("Test %v: No compression expected, found %v", i, resp)
			}
		} else {
			if resp == ts.body {
				t.Errorf("Test %v: Compression expected, found %v", i, resp)
			}
		}
	}
}
Example #13
0
func TestMultiEntries(t *testing.T) {
	var (
		got1 bytes.Buffer
		got2 bytes.Buffer
	)
	logger := Logger{
		Rules: []*Rule{{
			PathScope: "/",
			Entries: []*Entry{
				{
					Format: "foo {request_body}",
					Log:    log.New(&got1, "", 0),
				},
				{
					Format: "{method} {request_body}",
					Log:    log.New(&got2, "", 0),
				},
			},
		}},
		Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			// drain up body
			ioutil.ReadAll(r.Body)
			return 0, nil
		}),
	}

	r, err := http.NewRequest("POST", "/", bytes.NewBufferString("hello world"))
	if err != nil {
		t.Fatal(err)
	}
	r.Header.Set("Content-Type", "application/json")
	status, err := logger.ServeHTTP(httptest.NewRecorder(), r)
	if status != 0 {
		t.Errorf("Expected status to be 0, but was %d", status)
	}
	if err != nil {
		t.Errorf("Expected error to be nil, instead got: %v", err)
	}
	if got, expect := got1.String(), "foo hello world\n"; got != expect {
		t.Errorf("Expected %q, but got %q", expect, got)
	}
	if got, expect := got2.String(), "POST hello world\n"; got != expect {
		t.Errorf("Expected %q, but got %q", expect, got)
	}
}
Example #14
0
func TestMultipleOverlappingRules(t *testing.T) {
	rw := BasicAuth{
		Next: httpserver.HandlerFunc(contentHandler),
		Rules: []Rule{
			{Username: "******", Password: PlainMatcher("p1"), Resources: []string{"/t"}},
			{Username: "******", Password: PlainMatcher("p2"), Resources: []string{"/t/t"}},
		},
	}

	tests := []struct {
		from   string
		result int
		cred   string
	}{
		{"/t", http.StatusOK, "t:p1"},
		{"/t/t", http.StatusOK, "t:p1"},
		{"/t/t", http.StatusOK, "t1:p2"},
		{"/a", http.StatusOK, "t1:p2"},
		{"/t/t", http.StatusUnauthorized, "t1:p3"},
		{"/t", http.StatusUnauthorized, "t1:p2"},
	}

	for i, test := range tests {

		req, err := http.NewRequest("GET", test.from, nil)
		if err != nil {
			t.Fatalf("Test %d: Could not create HTTP request %v", i, err)
		}
		auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(test.cred))
		req.Header.Set("Authorization", auth)

		rec := httptest.NewRecorder()
		result, err := rw.ServeHTTP(rec, req)
		if err != nil {
			t.Fatalf("Test %d: Could not ServeHTTP %v", i, err)
		}
		if result != test.result {
			t.Errorf("Test %d: Expected Header '%d' but was '%d'",
				i, test.result, result)
		}

	}

}
Example #15
0
func nextFunc(shouldGzip bool) httpserver.Handler {
	return httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
		// write a relatively large text file
		b, err := ioutil.ReadFile("testdata/test.txt")
		if err != nil {
			return 500, err
		}
		if _, err := w.Write(b); err != nil {
			return 500, err
		}

		if shouldGzip {
			if r.Header.Get("Accept-Encoding") != "" {
				return 0, fmt.Errorf("Accept-Encoding header not expected")
			}
			if w.Header().Get("Content-Encoding") != "gzip" {
				return 0, fmt.Errorf("Content-Encoding must be gzip, found %v", r.Header.Get("Content-Encoding"))
			}
			if w.Header().Get("Vary") != "Accept-Encoding" {
				return 0, fmt.Errorf("Vary must be Accept-Encoding, found %v", r.Header.Get("Vary"))
			}
			if _, ok := w.(*gzipResponseWriter); !ok {
				return 0, fmt.Errorf("ResponseWriter should be gzipResponseWriter, found %T", w)
			}
			if strings.Contains(w.Header().Get("Content-Type"), "application/x-gzip") {
				return 0, fmt.Errorf("Content type should not be gzip.")
			}
			return 0, nil
		}
		if r.Header.Get("Accept-Encoding") == "" {
			return 0, fmt.Errorf("Accept-Encoding header expected")
		}
		if w.Header().Get("Content-Encoding") == "gzip" {
			return 0, fmt.Errorf("Content-Encoding must not be gzip, found gzip")
		}
		if _, ok := w.(*gzipResponseWriter); ok {
			return 0, fmt.Errorf("ResponseWriter should not be gzipResponseWriter")
		}
		return 0, nil
	})
}
Example #16
0
func TestInternal(t *testing.T) {
	im := Internal{
		Next:  httpserver.HandlerFunc(internalTestHandlerFunc),
		Paths: []string{"/internal"},
	}

	tests := []struct {
		url          string
		expectedCode int
		expectedBody string
	}{
		{"/internal", http.StatusNotFound, ""},

		{"/public", 0, "/public"},
		{"/public/internal", 0, "/public/internal"},

		{"/redirect", 0, "/internal"},

		{"/cycle", http.StatusInternalServerError, ""},
	}

	for i, test := range tests {
		req, err := http.NewRequest("GET", test.url, nil)
		if err != nil {
			t.Fatalf("Test %d: Could not create HTTP request: %v", i, err)
		}

		rec := httptest.NewRecorder()
		code, _ := im.ServeHTTP(rec, req)

		if code != test.expectedCode {
			t.Errorf("Test %d: Expected status code %d for %s, but got %d",
				i, test.expectedCode, test.url, code)
		}
		if rec.Body.String() != test.expectedBody {
			t.Errorf("Test %d: Expected body '%s' for %s, but got '%s'",
				i, test.expectedBody, test.url, rec.Body.String())
		}
	}
}
Example #17
0
func setup(c *caddy.Controller) error {
	rules, err := parseRules(c)
	if err != nil {
		return err
	}
	siteConfig := httpserver.GetConfig(c)
	siteConfig.AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
		return httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			for _, rule := range rules {
				if httpserver.Path(r.URL.Path).Matches(rule.Path) {
					rule.Conf.HandleRequest(w, r)
					if cors.IsPreflight(r) {
						return 200, nil
					}
					break
				}
			}
			return next.ServeHTTP(w, r)
		})
	})
	return nil
}
Example #18
0
func TestServeHTTP(t *testing.T) {
	h := Handler{
		Next: httpserver.HandlerFunc(nextHandler),
		Mux:  NewMux(),
	}

	w := httptest.NewRecorder()
	r, err := http.NewRequest("GET", "/debug/pprof", nil)
	if err != nil {
		t.Fatal(err)
	}
	status, err := h.ServeHTTP(w, r)

	if status != 0 {
		t.Errorf("Expected status %d but got %d", 0, status)
	}
	if err != nil {
		t.Errorf("Expected nil error, but got: %v", err)
	}
	if w.Body.String() == "content" {
		t.Errorf("Expected pprof to handle request, but it didn't")
	}

	w = httptest.NewRecorder()
	r, err = http.NewRequest("GET", "/foo", nil)
	if err != nil {
		t.Fatal(err)
	}
	status, err = h.ServeHTTP(w, r)
	if status != http.StatusNotFound {
		t.Errorf("Test two: Expected status %d but got %d", http.StatusNotFound, status)
	}
	if err != nil {
		t.Errorf("Test two: Expected nil error, but got: %v", err)
	}
	if w.Body.String() != "content" {
		t.Errorf("Expected pprof to pass the request thru, but it didn't; got: %s", w.Body.String())
	}
}
Example #19
0
func TestBrowseHTTPMethods(t *testing.T) {
	tmpl, err := template.ParseFiles("testdata/photos.tpl")
	if err != nil {
		t.Fatalf("An error occured while parsing the template: %v", err)
	}

	b := Browse{
		Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			return http.StatusTeapot, nil // not t.Fatalf, or we will not see what other methods yield
		}),
		Configs: []Config{
			{
				PathScope: "/photos",
				Root:      http.Dir("./testdata"),
				Template:  tmpl,
			},
		},
	}

	rec := httptest.NewRecorder()
	for method, expected := range map[string]int{
		http.MethodGet:     http.StatusOK,
		http.MethodHead:    http.StatusOK,
		http.MethodOptions: http.StatusNotImplemented,
		"PROPFIND":         http.StatusNotImplemented,
	} {
		req, err := http.NewRequest(method, "/photos/", nil)
		if err != nil {
			t.Fatalf("Test: Could not create HTTP request: %v", err)
		}

		code, _ := b.ServeHTTP(rec, req)
		if code != expected {
			t.Errorf("Wrong status with HTTP Method %s: expected %d, got %d", method, expected, code)
		}
	}
}
Example #20
0
func TestVisibleErrorWithPanic(t *testing.T) {
	const panicMsg = "I'm a panic"
	eh := ErrorHandler{
		ErrorPages: make(map[int]string),
		Debug:      true,
		Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			panic(panicMsg)
		}),
	}

	req, err := http.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatal(err)
	}
	rec := httptest.NewRecorder()

	code, err := eh.ServeHTTP(rec, req)

	if code != 0 {
		t.Errorf("Expected error handler to return 0 (it should write to response), got status %d", code)
	}
	if err != nil {
		t.Errorf("Expected error handler to return nil error (it should panic!), but got '%v'", err)
	}

	body := rec.Body.String()

	if !strings.Contains(body, "[PANIC /] caddyhttp/errors/errors_test.go") {
		t.Errorf("Expected response body to contain error log line, but it didn't:\n%s", body)
	}
	if !strings.Contains(body, panicMsg) {
		t.Errorf("Expected response body to contain panic message, but it didn't:\n%s", body)
	}
	if len(body) < 500 {
		t.Errorf("Expected response body to contain stack trace, but it was too short: len=%d", len(body))
	}
}
Example #21
0
func TestIPv6(t *testing.T) {
	TestCases := []struct {
		inputIpfilterConfig string
		shouldErr           bool
		reqIP               string
		reqPath             string
		expectedStatus      int
	}{
		{
			`ipfilter / {
				rule allow
				ip 2001:db8:1234::/48
			}`, false, "[2001:db8:1234:0000:0000:0000:0000:0000]:_", "/", http.StatusOK,
		},
		{
			`ipfilter / {
				rule allow
				ip 2001:db8:1234::/48
			}`, false, "[2001:db8:1234:ffff:ffff:ffff:ffff:ffff]:_", "/", http.StatusOK,
		},
		{
			`ipfilter / {
				rule allow
				ip 2001:db8:1234::/48
			}`, false, "[2001:db8:1244:0000:0000:0000:0000:0000]:_", "/", http.StatusForbidden,
		},
		{
			`ipfilter / {
				rule allow
				ip 8.8.8.8 2001:db8:85a3:8d3:1319:8a2e:370:7348 8.8.4.4
			}`, false, "[2001:db8:85a3:8d3:1319:8a2e:370:7338]:_", "/", http.StatusForbidden,
		},
		{
			`ipfilter / {
				rule allow
				ip 8.8.8.8 2001:db8:85a3:8d3:1319:8a2e:370:7348 8.8.4.4
			}`, false, "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:_", "/", http.StatusOK,
		},
		{
			`ipfilter / {
				rule allow
				ip 2001:db8:85a3::8a2e:370:7334 10.0.0 192.168.1.5-40
			}`, false, "192.168.1.33:_", "/", http.StatusOK,
		},
		{
			`ipfilter / {
				rule allow
				ip 2001:db8:85a3::8a2e:370:7334/64 10.0.0
			}`, false, "10.0.0.5:_", "/", http.StatusOK,
		},
	}

	for i, tc := range TestCases {
		// Parse the text config
		c := caddy.NewTestController("http", tc.inputIpfilterConfig)
		config, err := ipfilterParse(c)

		if err != nil && !tc.shouldErr {
			t.Errorf("Test %d failed, error generated while it should not: %v", i, err)
		} else if err == nil && tc.shouldErr {
			t.Errorf("Test %d failed, no error generated while it should", i)
		} else if err != nil {
			continue
		}

		ipf := IPFilter{
			Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
				return http.StatusOK, nil
			}),
			Config: config,
		}

		req, err := http.NewRequest("GET", tc.reqPath, nil)
		if err != nil {
			t.Fatalf("Could not create HTTP request: %v", err)
		}

		req.RemoteAddr = tc.reqIP

		rec := httptest.NewRecorder()

		status, err := ipf.ServeHTTP(rec, req)
		if err != nil {
			t.Fatalf("Test %d failed. Error generated:\n%v", i, err)
		}
		if status != tc.expectedStatus {
			t.Fatalf("Test %d failed. Expected StatusCode: '%d', Got: '%d'\nTestCase: %v\n",
				i, tc.expectedStatus, status, tc)
		}
	}

}
Example #22
0
func TestStrict(t *testing.T) {
	TestCases := []struct {
		ipfconf        IPFConfig
		reqIP          string
		fwdFor         string
		scope          string
		expectedStatus int
	}{
		{
			IPFConfig{
				Paths: []IPPath{
					{
						PathScopes: []string{"/"},
						IsBlock:    true,
						Nets:       parseCIDRs([]string{"8.8.8.8/32"}),
						Strict:     true,
					},
				},
			},
			"8.8.4.4:_",
			"8.8.8.8",
			"/",
			http.StatusOK,
		},
		{
			IPFConfig{
				Paths: []IPPath{
					{
						PathScopes: []string{"/"},
						IsBlock:    true,
						Nets:       parseCIDRs([]string{"8.8.8.8/32"}),
						Strict:     true,
					},
				},
			},
			"8.8.8.8:_",
			"8.8.8.8",
			"/",
			http.StatusForbidden,
		},
		{
			IPFConfig{
				Paths: []IPPath{
					{
						PathScopes: []string{"/"},
						IsBlock:    true,
						Nets:       parseCIDRs([]string{"8.8.8.8/32"}),
						Strict:     false,
					},
				},
			},
			"8.8.4.4:_",
			"8.8.8.8",
			"/",
			http.StatusForbidden,
		},
	}

	for _, tc := range TestCases {
		ipf := IPFilter{
			Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
				return http.StatusOK, nil
			}),
			Config: tc.ipfconf,
		}

		req, err := http.NewRequest("GET", tc.scope, nil)
		if err != nil {
			t.Fatalf("Could not create HTTP request: %v", err)
		}

		req.RemoteAddr = tc.reqIP
		if tc.fwdFor != "" {
			req.Header.Set("X-Forwarded-For", tc.fwdFor)
		}

		rec := httptest.NewRecorder()

		status, _ := ipf.ServeHTTP(rec, req)
		if status != tc.expectedStatus {
			t.Fatalf("Expected StatusCode: '%d', Got: '%d'\nTestCase: %v\n",
				tc.expectedStatus, status, tc)
		}
	}
}
Example #23
0
func TestMultipleIpFilters(t *testing.T) {
	TestCases := []struct {
		inputIpfilterConfig string
		shouldErr           bool
		reqIP               string
		reqPath             string
		expectedStatus      int
	}{
		{
			`ipfilter / {
				rule block
				ip 192.168.1.10
			}
			ipfilter /allowed {
				rule allow
				ip 192.168.1.10
			}`, false, "192.168.1.10:_", "/", http.StatusForbidden,
		},
		{
			`ipfilter / {
				rule block
				ip 192.168.1.10
			}
			ipfilter /allowed {
				rule allow
				ip 192.168.1.10
			}`, false, "192.168.1.10:_", "/allowed", http.StatusOK,
		},
		{
			`ipfilter / {
				rule block
				ip 192.168.1.10
			}
			ipfilter /allowed {
				rule allow
				ip 192.168.1.10
			}`, false, "212.168.23.13:_", "/", http.StatusOK,
		},
		{
			`ipfilter / {
				rule block
				ip 192.168.1.10
			}
			ipfilter /allowed {
				rule allow
				ip 192.168.1.10
			}`, false, "212.168.23.13:_", "/allowed", http.StatusForbidden,
		},
		{
			fmt.Sprintf(`ipfilter / {
				rule allow
				ip 192.168.1.10
			}
			ipfilter /allowed {
				rule allow
				country US
				database %s
			}`, DataBase), false, "8.8.8.8:_", "/allowed", http.StatusOK,
		},
		{
			fmt.Sprintf(`ipfilter /local {
				rule allow
				ip 192.168.1
			}
			ipfilter /private {
				rule allow
				ip 192.168.1.10-15
			}
			ipfilter /notglobal /secret {
				rule block
				country RU
				database %s
			}
			ipfilter / {
				rule allow
				ip 212.222.222.1
			}`, DataBase), false, "192.168.1.9:_", "/private", http.StatusForbidden,
		},
		{
			fmt.Sprintf(`ipfilter /local {
				rule allow
				ip 192.168.1
			}
			ipfilter /private {
				rule allow
				ip 192.168.1.10-15
			}
			ipfilter /notglobal /secret {
				rule block
				country RU
				database %s
			}
			ipfilter / {
				rule allow
				ip 212.222.222.1
			}`, DataBase), false, "212.222.222.1:_", "/list", http.StatusOK,
		},
		{
			fmt.Sprintf(`ipfilter /local {
				rule allow
				ip 192.168.1
			}
			ipfilter /private {
				rule allow
				ip 192.168.1.10-15
			}
			ipfilter /notglobal /secret {
				rule block
				country RU
				database %s
			}
			ipfilter / {
				rule allow
				ip 212.222.222.1
			}`, DataBase), false, "5.175.96.22:_", "/secret", http.StatusForbidden,
		},
		{
			fmt.Sprintf(`ipfilter /local {
				rule allow
				ip 192.168.1
			}
			ipfilter /private {
				rule allow
				ip 192.168.1.10-15
			}
			ipfilter /notglobal /secret {
				rule block
				country RU
				database %s
			}
			ipfilter / {
				rule allow
				ip 212.222.222.1
			}`, DataBase), false, "192.168.1.14:_", "/local", http.StatusOK,
		},
		{
			fmt.Sprintf(`ipfilter /local {
				rule allow
				ip 192.168.1
			}
			ipfilter /private {
				rule allow
				ip 192.168.1.10-15
			}
			ipfilter /notglobal /secret {
				rule block
				country RU
				database %s
			}
			ipfilter / {
				rule allow
				ip 212.222.222.1
			}`, DataBase), false, "192.168.1.16:_", "/private", http.StatusForbidden,
		},
	}

	for i, tc := range TestCases {
		// Parse the text config
		c := caddy.NewTestController("http", tc.inputIpfilterConfig)
		config, err := ipfilterParse(c)

		if err != nil && !tc.shouldErr {
			t.Errorf("Test %d failed, error generated while it should not: %v", i, err)
		} else if err == nil && tc.shouldErr {
			t.Errorf("Test %d failed, no error generated while it should", i)
		} else if err != nil {
			continue
		}

		ipf := IPFilter{
			Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
				return http.StatusOK, nil
			}),
			Config: config,
		}

		req, err := http.NewRequest("GET", tc.reqPath, nil)
		if err != nil {
			t.Fatalf("Could not create HTTP request: %v", err)
		}

		req.RemoteAddr = tc.reqIP

		rec := httptest.NewRecorder()

		status, err := ipf.ServeHTTP(rec, req)
		if err != nil {
			t.Fatalf("Test %d failed. Error generated:\n%v", i, err)
		}
		if status != tc.expectedStatus {
			t.Fatalf("Test %d failed. Expected StatusCode: '%d', Got: '%d'\nTestCase: %v\n",
				i, tc.expectedStatus, status, tc)
		}
	}
}
Example #24
0
func TestCountryCodes(t *testing.T) {
	TestCases := []struct {
		ipfconf        IPFConfig
		reqIP          string
		scope          string
		expectedBody   string
		expectedStatus int
	}{
		{IPFConfig{
			Paths: []IPPath{
				{
					PathScopes:   []string{"/"},
					BlockPage:    BlockPage,
					IsBlock:      false,
					CountryCodes: []string{"JP", "SA"},
				},
			},
		},
			"8.8.8.8:_", // US
			"/",
			BlockMsg,
			http.StatusOK,
		},

		{IPFConfig{
			Paths: []IPPath{
				{
					PathScopes:   []string{"/private"},
					BlockPage:    BlockPage,
					IsBlock:      true,
					CountryCodes: []string{"US", "CA"},
				},
			},
		},
			"24.53.192.20:_", // CA
			"/private",
			BlockMsg,
			http.StatusOK,
		},

		{IPFConfig{
			Paths: []IPPath{
				{
					PathScopes:   []string{"/testdata"},
					IsBlock:      true,
					CountryCodes: []string{"RU", "CN"},
				},
			},
		},
			"42.48.120.7:_", // CN
			"/",
			"",
			http.StatusOK, // pass-thru, out of scope
		},

		{IPFConfig{
			Paths: []IPPath{
				{
					PathScopes:   []string{"/"},
					IsBlock:      true,
					CountryCodes: []string{"RU", "JP", "SA"},
				},
			},
		},
			"78.95.221.163:_", // SA
			"/",
			"",
			http.StatusForbidden,
		},

		{IPFConfig{
			Paths: []IPPath{
				{
					PathScopes:   []string{"/onlyus"},
					IsBlock:      false,
					CountryCodes: []string{"US"},
				},
			},
		},
			"5.175.96.22:_", // RU
			"/onlyus",
			"",
			http.StatusForbidden,
		},

		{IPFConfig{
			Paths: []IPPath{
				{
					PathScopes:   []string{"/"},
					IsBlock:      false,
					CountryCodes: []string{"FR", "GB", "AE", "DE"},
				},
			},
		},
			"5.4.9.3:_", // DE
			"/",
			"",
			http.StatusOK, // Allowed
		},
	}
	// open the db
	db, err := maxminddb.Open(DataBase)
	if err != nil {
		t.Fatalf("Error opening the database: %v", err)
	}
	defer db.Close()

	for _, tc := range TestCases {

		ipf := IPFilter{
			Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
				return http.StatusOK, nil
			}),
			Config: tc.ipfconf,
		}

		// set the DBHandler
		ipf.Config.DBHandler = db

		req, err := http.NewRequest("GET", tc.scope, nil)
		if err != nil {
			t.Fatalf("Could not create HTTP request: %v", err)
		}

		req.RemoteAddr = tc.reqIP

		rec := httptest.NewRecorder()

		status, _ := ipf.ServeHTTP(rec, req)
		if status != tc.expectedStatus {
			t.Fatalf("Expected StatusCode: '%d', Got: '%d'\nTestCase: %v\n",
				tc.expectedStatus, status, tc)
		}

		if rec.Body.String() != tc.expectedBody {
			t.Fatalf("Expected Body: '%s', Got: '%s'\nTestCase: %v\n",
				tc.expectedBody, rec.Body.String(), tc)
		}
	}
}
Example #25
0
func TestFwdForIPs(t *testing.T) {
	// These test cases provide test coverage for proxied requests support (Refer to https://github.com/pyed/ipfilter/pull/4)
	TestCases := []struct {
		ipfconf        IPFConfig
		reqIP          string
		fwdFor         string
		scope          string
		expectedStatus int
	}{
		// Middleware should block request when filtering rule is set to 'Block', a *blocked* IP is passed in the 'X-Forwarded-For' header and the request is coming from *permitted* remote address
		{
			IPFConfig{
				Paths: []IPPath{
					{
						PathScopes: []string{"/"},
						IsBlock:    true,
						Nets:       parseCIDRs([]string{"8.8.8.8/32"}),
					},
				},
			},
			"8.8.4.4:_",
			"8.8.8.8",
			"/",
			http.StatusForbidden,
		},
		// Middleware should allow request when filtering rule is set to 'Block', no IP is passed in the 'X-Forwarded-For' header and the request is coming from *permitted* remote address
		{
			IPFConfig{
				Paths: []IPPath{
					{
						PathScopes: []string{"/"},
						IsBlock:    true,
						Nets:       parseCIDRs([]string{"8.8.8.8/32"}),
					},
				},
			},
			"8.8.4.4:_",
			"",
			"/",
			http.StatusOK,
		},
		// Middleware should allow request when filtering rule is set to 'Block', a *permitted* IP is passed in the 'X-Forwarded-For' header and the request is coming from *blocked* remote address
		{
			IPFConfig{
				Paths: []IPPath{
					{
						PathScopes: []string{"/"},
						IsBlock:    true,
						Nets:       parseCIDRs([]string{"8.8.8.8/32"}),
					},
				},
			},
			"8.8.8.8:_",
			"8.8.4.4",
			"/",
			http.StatusOK,
		},
		// Middleware should allow request when filtering rule is set to 'Allow', a *permitted* IP is passed in the 'X-Forwarded-For' header and the request is coming from *blocked* remote address
		{
			IPFConfig{
				Paths: []IPPath{
					{
						PathScopes: []string{"/"},
						IsBlock:    false,
						Nets:       parseCIDRs([]string{"8.8.8.8/32"}),
					},
				},
			},
			"8.8.4.4:_",
			"8.8.8.8",
			"/",
			http.StatusOK,
		},
		// Middleware should block request when filtering rule is set to 'Allow', no IP is passed in the 'X-Forwarded-For' header and the request is coming from *blocked* remote address
		{
			IPFConfig{
				Paths: []IPPath{
					{
						PathScopes: []string{"/"},
						IsBlock:    false,
						Nets:       parseCIDRs([]string{"8.8.8.8/32"}),
					},
				},
			},
			"8.8.4.4:_",
			"",
			"/",
			http.StatusForbidden,
		},
		// Middleware should block request when filtering rule is set to 'Allow', a *blocked* IP is passed in the 'X-Forwarded-For' header and the request is coming from *permitted* remote address
		{
			IPFConfig{
				Paths: []IPPath{
					{
						PathScopes: []string{"/"},
						IsBlock:    false,
						Nets:       parseCIDRs([]string{"8.8.8.8/32"}),
					},
				},
			},
			"8.8.8.8:_",
			"8.8.4.4",
			"/",
			http.StatusForbidden,
		},
	}

	for _, tc := range TestCases {
		ipf := IPFilter{
			Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
				return http.StatusOK, nil
			}),
			Config: tc.ipfconf,
		}

		req, err := http.NewRequest("GET", tc.scope, nil)
		if err != nil {
			t.Fatalf("Could not create HTTP request: %v", err)
		}

		req.RemoteAddr = tc.reqIP
		if tc.fwdFor != "" {
			req.Header.Set("X-Forwarded-For", tc.fwdFor)
		}

		rec := httptest.NewRecorder()

		status, _ := ipf.ServeHTTP(rec, req)
		if status != tc.expectedStatus {
			t.Fatalf("Expected StatusCode: '%d', Got: '%d'\nTestCase: %v\n",
				tc.expectedStatus, status, tc)
		}
	}
}
Example #26
0
func TestNets(t *testing.T) {
	TestCases := []struct {
		ipfconf        IPFConfig
		reqIP          string
		scope          string
		expectedBody   string
		expectedStatus int
	}{
		{IPFConfig{
			Paths: []IPPath{
				{
					PathScopes: []string{"/"},
					BlockPage:  BlockPage,
					IsBlock:    true,
					Nets: parseCIDRs([]string{"243.1.3.10/31", "243.1.3.12/30",
						"243.1.3.16/30", "243.1.3.20/32"}),
				},
			},
		},
			"243.1.3.15:_",
			"/",
			BlockMsg,
			http.StatusOK,
		},

		{IPFConfig{
			Paths: []IPPath{
				{
					PathScopes: []string{"/private"},
					BlockPage:  BlockPage,
					IsBlock:    true,
					Nets:       parseCIDRs([]string{"243.1.3.0/24", "202.33.44.0/24"}),
				},
			},
		},
			"202.33.44.224:_",
			"/private",
			BlockMsg,
			http.StatusOK,
		},

		{IPFConfig{
			Paths: []IPPath{
				{
					PathScopes: []string{"/"},
					BlockPage:  BlockPage,
					IsBlock:    true,
					Nets: parseCIDRs([]string{
						"243.1.3.10/31", "243.1.3.12/30", "243.1.3.16/30", "243.1.3.20/32",
					}),
				},
			},
		},
			"243.1.3.9:_",
			"/",
			"",
			http.StatusOK,
		},

		{IPFConfig{
			Paths: []IPPath{
				{
					PathScopes: []string{"/eighties"},
					BlockPage:  BlockPage,
					IsBlock:    false,
					Nets: parseCIDRs([]string{
						"243.1.3.10/31", "243.1.3.12/30", "243.1.3.16/30", "243.1.3.20/32",
						"80.0.0.0/8",
					}),
				},
			},
		},
			"80.245.155.250:_",
			"/eighties",
			"",
			http.StatusOK,
		},

		{IPFConfig{
			Paths: []IPPath{
				{
					PathScopes: []string{"/eighties"},
					IsBlock:    true,
					Nets: parseCIDRs([]string{
						"243.1.3.10/31", "243.1.3.12/30", "243.1.3.16/30", "243.1.3.20/32",
						"80.0.0.0/8",
					}),
				},
			},
		},
			"80.245.155.250:_",
			"/",
			"",
			http.StatusOK,
		},

		{IPFConfig{
			Paths: []IPPath{
				{
					PathScopes: []string{"/"},
					IsBlock:    true,
					Nets: parseCIDRs([]string{
						"243.1.3.10/31", "243.1.3.12/30", "243.1.3.16/30", "243.1.3.20/32",
						"80.0.0.0/8", "23.1.3.1/32", "23.1.3.2/31", "23.1.3.4/30", "23.1.3.8/29",
						"23.1.3.16/30", "23.1.3.20/32", "85.0.0.0/8",
					}),
				},
			},
		},
			"23.1.3.9:_",
			"/",
			"",
			http.StatusForbidden,
		},
		// From here on out, tests are covering single IPNets
		{IPFConfig{
			Paths: []IPPath{
				{
					PathScopes: []string{"/"},
					BlockPage:  BlockPage,
					IsBlock:    true,
					Nets:       parseCIDRs([]string{"8.8.8.8/32"}),
				},
			},
		},
			"8.8.4.4:_",
			"/",
			"",
			http.StatusOK,
		},

		{IPFConfig{
			Paths: []IPPath{
				{
					PathScopes: []string{"/"},
					BlockPage:  BlockPage,
					IsBlock:    false,
					Nets:       parseCIDRs([]string{"8.8.8.8/32"}),
				},
			},
		},
			"8.8.4.4:_",
			"/",
			BlockMsg,
			http.StatusOK,
		},

		{IPFConfig{
			Paths: []IPPath{
				{
					PathScopes: []string{"/private"},
					BlockPage:  BlockPage,
					IsBlock:    false,
					Nets: parseCIDRs([]string{
						"52.9.1.2/32", "52.9.1.3/32", "52.9.1.4/32",
					}),
				},
			},
		},
			"52.9.1.3:_",
			"/private",
			"",
			http.StatusOK,
		},

		{IPFConfig{
			Paths: []IPPath{
				{
					PathScopes: []string{"/private"},
					BlockPage:  BlockPage,
					IsBlock:    false,
					Nets:       parseCIDRs([]string{"99.1.8.8/32"}),
				},
			},
		},
			"90.90.90.90:_",
			"/",
			"",
			http.StatusOK,
		},

		{IPFConfig{
			Paths: []IPPath{
				{
					PathScopes: []string{"/private"},
					IsBlock:    true,
					Nets: parseCIDRs([]string{
						"52.9.1.2/32",
						"52.9.1.3/32",
						"52.9.1.4/32",
					}),
				},
			},
		},
			"52.9.1.3:_",
			"/private",
			"",
			http.StatusForbidden,
		},
	}

	for _, tc := range TestCases {
		ipf := IPFilter{
			Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
				return http.StatusOK, nil
			}),
			Config: tc.ipfconf,
		}
		req, err := http.NewRequest("GET", tc.scope, nil)
		if err != nil {
			t.Fatalf("Could not create HTTP request: %v", err)
		}

		req.RemoteAddr = tc.reqIP

		rec := httptest.NewRecorder()

		status, _ := ipf.ServeHTTP(rec, req)
		if status != tc.expectedStatus {
			t.Fatalf("Expected StatusCode: '%d', Got: '%d'\nTestCase: %v\n",
				tc.expectedStatus, status, tc)
		}

		if rec.Body.String() != tc.expectedBody {
			t.Fatalf("Expected Body: '%s', Got: '%s'\nTestCase: %v\n",
				tc.expectedBody, rec.Body.String(), tc)
		}
	}
}
Example #27
0
func TestBrowseJson(t *testing.T) {
	b := Browse{
		Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			t.Fatalf("Next shouldn't be called")
			return 0, nil
		}),
		Configs: []Config{
			{
				PathScope: "/photos/",
				Root:      http.Dir("./testdata"),
			},
		},
	}

	//Getting the listing from the ./testdata/photos, the listing returned will be used to validate test results
	testDataPath := filepath.Join("./testdata", "photos")
	file, err := os.Open(testDataPath)
	if err != nil {
		if os.IsPermission(err) {
			t.Fatalf("Os Permission Error")
		}
	}
	defer file.Close()

	files, err := file.Readdir(-1)
	if err != nil {
		t.Fatalf("Unable to Read Contents of the directory")
	}
	var fileinfos []FileInfo

	for i, f := range files {
		name := f.Name()

		// Tests fail in CI environment because all file mod times are the same for
		// some reason, making the sorting unpredictable. To hack around this,
		// we ensure here that each file has a different mod time.
		chTime := f.ModTime().UTC().Add(-(time.Duration(i) * time.Second))
		if err := os.Chtimes(filepath.Join(testDataPath, name), chTime, chTime); err != nil {
			t.Fatal(err)
		}

		if f.IsDir() {
			name += "/"
		}

		url := url.URL{Path: "./" + name}

		fileinfos = append(fileinfos, FileInfo{
			IsDir:   f.IsDir(),
			Name:    f.Name(),
			Size:    f.Size(),
			URL:     url.String(),
			ModTime: chTime,
			Mode:    f.Mode(),
		})
	}
	listing := Listing{Items: fileinfos} // this listing will be used for validation inside the tests

	tests := []struct {
		QueryURL       string
		SortBy         string
		OrderBy        string
		Limit          int
		shouldErr      bool
		expectedResult []FileInfo
	}{
		//test case 1: testing for default sort and  order and without the limit parameter, default sort is by name and the default order is ascending
		//without the limit query entire listing will be produced
		{"/", "", "", -1, false, listing.Items},
		//test case 2: limit is set to 1, orderBy and sortBy is default
		{"/?limit=1", "", "", 1, false, listing.Items[:1]},
		//test case 3 : if the listing request is bigger than total size of listing then it should return everything
		{"/?limit=100000000", "", "", 100000000, false, listing.Items},
		//test case 4 : testing for negative limit
		{"/?limit=-1", "", "", -1, false, listing.Items},
		//test case 5 : testing with limit set to -1 and order set to descending
		{"/?limit=-1&order=desc", "", "desc", -1, false, listing.Items},
		//test case 6 : testing with limit set to 2 and order set to descending
		{"/?limit=2&order=desc", "", "desc", 2, false, listing.Items},
		//test case 7 : testing with limit set to 3 and order set to descending
		{"/?limit=3&order=desc", "", "desc", 3, false, listing.Items},
		//test case 8 : testing with limit set to 3 and order set to ascending
		{"/?limit=3&order=asc", "", "asc", 3, false, listing.Items},
		//test case 9 : testing with limit set to 1111111 and order set to ascending
		{"/?limit=1111111&order=asc", "", "asc", 1111111, false, listing.Items},
		//test case 10 : testing with limit set to default and order set to ascending and sorting by size
		{"/?order=asc&sort=size", "size", "asc", -1, false, listing.Items},
		//test case 11 : testing with limit set to default and order set to ascending and sorting by last modified
		{"/?order=asc&sort=time", "time", "asc", -1, false, listing.Items},
		//test case 12 : testing with limit set to 1 and order set to ascending and sorting by last modified
		{"/?order=asc&sort=time&limit=1", "time", "asc", 1, false, listing.Items},
		//test case 13 : testing with limit set to -100 and order set to ascending and sorting by last modified
		{"/?order=asc&sort=time&limit=-100", "time", "asc", -100, false, listing.Items},
		//test case 14 : testing with limit set to -100 and order set to ascending and sorting by size
		{"/?order=asc&sort=size&limit=-100", "size", "asc", -100, false, listing.Items},
	}

	for i, test := range tests {
		var marsh []byte
		req, err := http.NewRequest("GET", "/photos"+test.QueryURL, nil)

		if err == nil && test.shouldErr {
			t.Errorf("Test %d didn't error, but it should have", i)
		} else if err != nil && !test.shouldErr {
			t.Errorf("Test %d errored, but it shouldn't have; got '%v'", i, err)
		}

		req.Header.Set("Accept", "application/json")
		rec := httptest.NewRecorder()

		code, err := b.ServeHTTP(rec, req)

		if code != http.StatusOK {
			t.Fatalf("In test %d: Wrong status, expected %d, got %d", i, http.StatusOK, code)
		}
		if rec.HeaderMap.Get("Content-Type") != "application/json; charset=utf-8" {
			t.Fatalf("Expected Content type to be application/json; charset=utf-8, but got %s ", rec.HeaderMap.Get("Content-Type"))
		}

		actualJSONResponse := rec.Body.String()
		copyOflisting := listing
		if test.SortBy == "" {
			copyOflisting.Sort = "name"
		} else {
			copyOflisting.Sort = test.SortBy
		}
		if test.OrderBy == "" {
			copyOflisting.Order = "asc"
		} else {
			copyOflisting.Order = test.OrderBy
		}

		copyOflisting.applySort()

		limit := test.Limit
		if limit <= len(copyOflisting.Items) && limit > 0 {
			marsh, err = json.Marshal(copyOflisting.Items[:limit])
		} else { // if the 'limit' query is empty, or has the wrong value, list everything
			marsh, err = json.Marshal(copyOflisting.Items)
		}

		if err != nil {
			t.Fatalf("Unable to Marshal the listing ")
		}
		expectedJSON := string(marsh)

		if actualJSONResponse != expectedJSON {
			t.Errorf("JSON response doesn't match the expected for test number %d with sort=%s, order=%s\nExpected response %s\nActual response = %s\n",
				i+1, test.SortBy, test.OrderBy, expectedJSON, actualJSONResponse)
		}
	}
}
Example #28
0
func TestRewrite(t *testing.T) {
	rw := Rewrite{
		Next: httpserver.HandlerFunc(urlPrinter),
		Rules: []httpserver.HandlerConfig{
			NewSimpleRule("/from", "/to"),
			NewSimpleRule("/a", "/b"),
			NewSimpleRule("/b", "/b{uri}"),
		},
		FileSys: http.Dir("."),
	}

	regexps := [][]string{
		{"/reg/", ".*", "/to", ""},
		{"/r/", "[a-z]+", "/toaz", "!.html|"},
		{"/url/", "a([a-z0-9]*)s([A-Z]{2})", "/to/{path}", ""},
		{"/ab/", "ab", "/ab?{query}", ".txt|"},
		{"/ab/", "ab", "/ab?type=html&{query}", ".html|"},
		{"/abc/", "ab", "/abc/{file}", ".html|"},
		{"/abcd/", "ab", "/a/{dir}/{file}", ".html|"},
		{"/abcde/", "ab", "/a#{fragment}", ".html|"},
		{"/ab/", `.*\.jpg`, "/ajpg", ""},
		{"/reggrp", `/ad/([0-9]+)([a-z]*)`, "/a{1}/{2}", ""},
		{"/reg2grp", `(.*)`, "/{1}", ""},
		{"/reg3grp", `(.*)/(.*)/(.*)`, "/{1}{2}{3}", ""},
		{"/hashtest", "(.*)", "/{1}", ""},
	}

	for _, regexpRule := range regexps {
		var ext []string
		if s := strings.Split(regexpRule[3], "|"); len(s) > 1 {
			ext = s[:len(s)-1]
		}
		rule, err := NewComplexRule(regexpRule[0], regexpRule[1], regexpRule[2], 0, ext, httpserver.IfMatcher{})
		if err != nil {
			t.Fatal(err)
		}
		rw.Rules = append(rw.Rules, rule)
	}

	tests := []struct {
		from       string
		expectedTo string
	}{
		{"/from", "/to"},
		{"/a", "/b"},
		{"/b", "/b/b"},
		{"/aa", "/aa"},
		{"/", "/"},
		{"/a?foo=bar", "/b?foo=bar"},
		{"/asdf?foo=bar", "/asdf?foo=bar"},
		{"/foo#bar", "/foo#bar"},
		{"/a#foo", "/b#foo"},
		{"/reg/foo", "/to"},
		{"/re", "/re"},
		{"/r/", "/r/"},
		{"/r/123", "/r/123"},
		{"/r/a123", "/toaz"},
		{"/r/abcz", "/toaz"},
		{"/r/z", "/toaz"},
		{"/r/z.html", "/r/z.html"},
		{"/r/z.js", "/toaz"},
		{"/url/asAB", "/to/url/asAB"},
		{"/url/aBsAB", "/url/aBsAB"},
		{"/url/a00sAB", "/to/url/a00sAB"},
		{"/url/a0z0sAB", "/to/url/a0z0sAB"},
		{"/ab/aa", "/ab/aa"},
		{"/ab/ab", "/ab/ab"},
		{"/ab/ab.txt", "/ab"},
		{"/ab/ab.txt?name=name", "/ab?name=name"},
		{"/ab/ab.html?name=name", "/ab?type=html&name=name"},
		{"/abc/ab.html", "/abc/ab.html"},
		{"/abcd/abcd.html", "/a/abcd/abcd.html"},
		{"/abcde/abcde.html", "/a"},
		{"/abcde/abcde.html#1234", "/a#1234"},
		{"/ab/ab.jpg", "/ajpg"},
		{"/reggrp/ad/12", "/a12/"},
		{"/reggrp/ad/124a", "/a124/a"},
		{"/reggrp/ad/124abc", "/a124/abc"},
		{"/reg2grp/ad/124abc", "/ad/124abc"},
		{"/reg3grp/ad/aa/66", "/adaa66"},
		{"/reg3grp/ad612/n1n/ab", "/ad612n1nab"},
		{"/hashtest/a%20%23%20test", "/a%20%23%20test"},
		{"/hashtest/a%20%3F%20test", "/a%20%3F%20test"},
		{"/hashtest/a%20%3F%23test", "/a%20%3F%23test"},
	}

	for i, test := range tests {
		req, err := http.NewRequest("GET", test.from, nil)
		if err != nil {
			t.Fatalf("Test %d: Could not create HTTP request: %v", i, err)
		}

		rec := httptest.NewRecorder()
		rw.ServeHTTP(rec, req)

		if rec.Body.String() != test.expectedTo {
			t.Errorf("Test %d: Expected URL to be '%s' but was '%s'",
				i, test.expectedTo, rec.Body.String())
		}
	}

	statusTests := []struct {
		status         int
		base           string
		to             string
		regexp         string
		statusExpected bool
	}{
		{400, "/status", "", "", true},
		{400, "/ignore", "", "", false},
		{400, "/", "", "^/ignore", false},
		{400, "/", "", "(.*)", true},
		{400, "/status", "", "", true},
	}

	for i, s := range statusTests {
		urlPath := fmt.Sprintf("/status%d", i)
		rule, err := NewComplexRule(s.base, s.regexp, s.to, s.status, nil, httpserver.IfMatcher{})
		if err != nil {
			t.Fatalf("Test %d: No error expected for rule but found %v", i, err)
		}
		rw.Rules = []httpserver.HandlerConfig{rule}
		req, err := http.NewRequest("GET", urlPath, nil)
		if err != nil {
			t.Fatalf("Test %d: Could not create HTTP request: %v", i, err)
		}

		rec := httptest.NewRecorder()
		code, err := rw.ServeHTTP(rec, req)
		if err != nil {
			t.Fatalf("Test %d: No error expected for handler but found %v", i, err)
		}
		if s.statusExpected {
			if rec.Body.String() != "" {
				t.Errorf("Test %d: Expected empty body but found %s", i, rec.Body.String())
			}
			if code != s.status {
				t.Errorf("Test %d: Expected status code %d found %d", i, s.status, code)
			}
		} else {
			if code != 0 {
				t.Errorf("Test %d: Expected no status code found %d", i, code)
			}
		}
	}
}
Example #29
0
func TestRedirect(t *testing.T) {
	for i, test := range []struct {
		from             string
		expectedLocation string
		expectedCode     int
	}{
		{"http://localhost/from", "/to", http.StatusMovedPermanently},
		{"http://localhost/a", "/b", http.StatusTemporaryRedirect},
		{"http://localhost/aa", "", http.StatusOK},
		{"http://localhost/", "", http.StatusOK},
		{"http://localhost/a?foo=bar", "/b", http.StatusTemporaryRedirect},
		{"http://localhost/asdf?foo=bar", "", http.StatusOK},
		{"http://localhost/foo#bar", "", http.StatusOK},
		{"http://localhost/a#foo", "/b", http.StatusTemporaryRedirect},

		// The scheme checks that were added to this package don't actually
		// help with redirects because of Caddy's design: a redirect middleware
		// for http will always be different than the redirect middleware for
		// https because they have to be on different listeners. These tests
		// just go to show extra bulletproofing, I guess.
		{"http://localhost/scheme", "https://localhost/scheme", http.StatusMovedPermanently},
		{"https://localhost/scheme", "", http.StatusOK},
		{"https://localhost/scheme2", "http://localhost/scheme2", http.StatusMovedPermanently},
		{"http://localhost/scheme2", "", http.StatusOK},
		{"http://localhost/scheme3", "https://localhost/scheme3", http.StatusMovedPermanently},
		{"https://localhost/scheme3", "", http.StatusOK},
	} {
		var nextCalled bool

		re := Redirect{
			Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
				nextCalled = true
				return 0, nil
			}),
			Rules: []Rule{
				{FromPath: "/from", To: "/to", Code: http.StatusMovedPermanently, RequestMatcher: httpserver.IfMatcher{}},
				{FromPath: "/a", To: "/b", Code: http.StatusTemporaryRedirect, RequestMatcher: httpserver.IfMatcher{}},

				// These http and https schemes would never actually be mixed in the same
				// redirect rule with Caddy because http and https schemes have different listeners,
				// so they don't share a redirect rule. So although these tests prove something
				// impossible with Caddy, it's extra bulletproofing at very little cost.
				{FromScheme: "http", FromPath: "/scheme", To: "https://localhost/scheme", Code: http.StatusMovedPermanently, RequestMatcher: httpserver.IfMatcher{}},
				{FromScheme: "https", FromPath: "/scheme2", To: "http://localhost/scheme2", Code: http.StatusMovedPermanently, RequestMatcher: httpserver.IfMatcher{}},
				{FromScheme: "", FromPath: "/scheme3", To: "https://localhost/scheme3", Code: http.StatusMovedPermanently, RequestMatcher: httpserver.IfMatcher{}},
			},
		}

		req, err := http.NewRequest("GET", test.from, nil)
		if err != nil {
			t.Fatalf("Test %d: Could not create HTTP request: %v", i, err)
		}
		if strings.HasPrefix(test.from, "https://") {
			req.TLS = new(tls.ConnectionState) // faux HTTPS
		}

		rec := httptest.NewRecorder()
		re.ServeHTTP(rec, req)

		if rec.Header().Get("Location") != test.expectedLocation {
			t.Errorf("Test %d: Expected Location header to be %q but was %q",
				i, test.expectedLocation, rec.Header().Get("Location"))
		}

		if rec.Code != test.expectedCode {
			t.Errorf("Test %d: Expected status code to be %d but was %d",
				i, test.expectedCode, rec.Code)
		}

		if nextCalled && test.expectedLocation != "" {
			t.Errorf("Test %d: Next handler was unexpectedly called", i)
		}
	}
}
Example #30
0
func TestTemplates(t *testing.T) {
	tmpl := Templates{
		Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			return 0, nil
		}),
		Rules: []Rule{
			{
				Extensions: []string{".html"},
				IndexFiles: []string{"index.html"},
				Path:       "/photos",
			},
			{
				Extensions: []string{".html", ".htm"},
				IndexFiles: []string{"index.html", "index.htm"},
				Path:       "/images",
				Delims:     [2]string{"{%", "%}"},
			},
		},
		Root:    "./testdata",
		FileSys: http.Dir("./testdata"),
	}

	tmplroot := Templates{
		Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			return 0, nil
		}),
		Rules: []Rule{
			{
				Extensions: []string{".html"},
				IndexFiles: []string{"index.html"},
				Path:       "/",
			},
		},
		Root:    "./testdata",
		FileSys: http.Dir("./testdata"),
	}

	// Test tmpl on /photos/test.html
	req, err := http.NewRequest("GET", "/photos/test.html", nil)
	if err != nil {
		t.Fatalf("Test: Could not create HTTP request: %v", err)
	}

	rec := httptest.NewRecorder()

	tmpl.ServeHTTP(rec, req)

	if rec.Code != http.StatusOK {
		t.Fatalf("Test: Wrong response code: %d, should be %d", rec.Code, http.StatusOK)
	}

	respBody := rec.Body.String()
	expectedBody := `<!DOCTYPE html><html><head><title>test page</title></head><body><h1>Header title</h1>
</body></html>
`

	if respBody != expectedBody {
		t.Fatalf("Test: the expected body %v is different from the response one: %v", expectedBody, respBody)
	}

	// Test tmpl on /images/img.htm
	req, err = http.NewRequest("GET", "/images/img.htm", nil)
	if err != nil {
		t.Fatalf("Could not create HTTP request: %v", err)
	}

	rec = httptest.NewRecorder()

	tmpl.ServeHTTP(rec, req)

	if rec.Code != http.StatusOK {
		t.Fatalf("Test: Wrong response code: %d, should be %d", rec.Code, http.StatusOK)
	}

	respBody = rec.Body.String()
	expectedBody = `<!DOCTYPE html><html><head><title>img</title></head><body><h1>Header title</h1>
</body></html>
`

	if respBody != expectedBody {
		t.Fatalf("Test: the expected body %v is different from the response one: %v", expectedBody, respBody)
	}

	// Test tmpl on /images/img2.htm
	req, err = http.NewRequest("GET", "/images/img2.htm", nil)
	if err != nil {
		t.Fatalf("Could not create HTTP request: %v", err)
	}

	rec = httptest.NewRecorder()

	tmpl.ServeHTTP(rec, req)

	if rec.Code != http.StatusOK {
		t.Fatalf("Test: Wrong response code: %d, should be %d", rec.Code, http.StatusOK)
	}

	respBody = rec.Body.String()
	expectedBody = `<!DOCTYPE html><html><head><title>img</title></head><body>{{.Include "header.html"}}</body></html>
`

	if respBody != expectedBody {
		t.Fatalf("Test: the expected body %v is different from the response one: %v", expectedBody, respBody)
	}

	// Test tmplroot on /root.html
	req, err = http.NewRequest("GET", "/root.html", nil)
	if err != nil {
		t.Fatalf("Could not create HTTP request: %v", err)
	}

	rec = httptest.NewRecorder()

	tmplroot.ServeHTTP(rec, req)

	if rec.Code != http.StatusOK {
		t.Fatalf("Test: Wrong response code: %d, should be %d", rec.Code, http.StatusOK)
	}

	respBody = rec.Body.String()
	expectedBody = `<!DOCTYPE html><html><head><title>root</title></head><body><h1>Header title</h1>
</body></html>
`

	if respBody != expectedBody {
		t.Fatalf("Test: the expected body %v is different from the response one: %v", expectedBody, respBody)
	}
}