func TestRouterNotFound(t *testing.T) { handlerFunc := func(_ *fasthttp.RequestCtx, _ Params) {} router := New() router.GET("/path", handlerFunc) router.GET("/dir/", handlerFunc) router.GET("/", handlerFunc) testRoutes := []struct { route string code int }{ {"/path/", 301}, // TSR -/ {"/dir", 301}, // TSR +/ {"/", 200}, // TSR +/ {"/PATH", 301}, // Fixed Case {"/DIR", 301}, // Fixed Case {"/PATH/", 301}, // Fixed Case -/ {"/DIR/", 301}, // Fixed Case +/ {"/../path", 200}, // CleanPath {"/nope", 404}, // NotFound } s := &fasthttp.Server{ Handler: router.Handler, } rw := &readWriter{} br := bufio.NewReader(&rw.w) var resp fasthttp.Response ch := make(chan error) for _, tr := range testRoutes { rw.r.WriteString(fmt.Sprintf("GET %s HTTP/1.1\r\n\r\n", tr.route)) go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if !(resp.Header.StatusCode() == tr.code) { t.Errorf("NotFound handling route %s failed: Code=%d want=%d", tr.route, resp.Header.StatusCode(), tr.code) } } // Test custom not found handler var notFound bool router.NotFound = fasthttp.RequestHandler(func(ctx *fasthttp.RequestCtx) { ctx.SetStatusCode(404) notFound = true }) rw.r.WriteString("GET /nope HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if !(resp.Header.StatusCode() == 404 && notFound == true) { t.Errorf("Custom NotFound handler failed: Code=%d, Header=%v", resp.Header.StatusCode(), string(resp.Header.Peek("Location"))) } // Test other method than GET (want 307 instead of 301) router.PATCH("/path", handlerFunc) rw.r.WriteString("PATCH /path/ HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if !(resp.Header.StatusCode() == 307) { t.Errorf("Custom NotFound handler failed: Code=%d, Header=%v", resp.Header.StatusCode(), string(resp.Header.Peek("Location"))) } // Test special case where no node for the prefix "/" exists router = New() router.GET("/a", handlerFunc) s.Handler = router.Handler rw.r.WriteString("GET / HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if !(resp.Header.StatusCode() == 404) { t.Errorf("NotFound handling route / failed: Code=%d", resp.Header.StatusCode()) } }
// NewRedirectHandler returns a new redirect handler func NewProxyHandler(url string) fasthttp.RequestHandler { handler := ProxyHandler{url: url} return fasthttp.RequestHandler(handler.ServeHTTP) }
func TestRouterNotAllowed(t *testing.T) { handlerFunc := func(_ *fasthttp.RequestCtx, _ Params) {} router := New() router.POST("/path", handlerFunc) // Test not allowed s := &fasthttp.Server{ Handler: router.Handler, } rw := &readWriter{} ch := make(chan error) rw.r.WriteString("GET /path HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } br := bufio.NewReader(&rw.w) var resp fasthttp.Response if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if !(resp.Header.StatusCode() == fasthttp.StatusMethodNotAllowed) { t.Errorf("NotAllowed handling failed: Code=%d, Header=%v", resp.Header.StatusCode(), resp.Header) } else if allow := string(resp.Header.Peek("Allow")); allow != "POST, OPTIONS" { t.Error("unexpected Allow header value: " + allow) } // add another method router.DELETE("/path", handlerFunc) router.OPTIONS("/path", handlerFunc) // must be ignored // test again rw.r.WriteString("GET /path HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if !(resp.Header.StatusCode() == fasthttp.StatusMethodNotAllowed) { t.Errorf("NotAllowed handling failed: Code=%d, Header=%v", resp.Header.StatusCode(), resp.Header) } else if allow := string(resp.Header.Peek("Allow")); allow != "POST, DELETE, OPTIONS" && allow != "DELETE, POST, OPTIONS" { t.Error("unexpected Allow header value: " + allow) } responseText := "custom method" router.MethodNotAllowed = fasthttp.RequestHandler(func(ctx *fasthttp.RequestCtx) { ctx.SetStatusCode(fasthttp.StatusTeapot) ctx.Write([]byte(responseText)) }) rw.r.WriteString("GET /path HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if !bytes.Equal(resp.Body(), []byte(responseText)) { t.Errorf("unexpected response got %q want %q", string(resp.Body()), responseText) } if resp.Header.StatusCode() != fasthttp.StatusTeapot { t.Errorf("unexpected response code %d want %d", resp.Header.StatusCode(), fasthttp.StatusTeapot) } if allow := string(resp.Header.Peek("Allow")); allow != "POST, DELETE, OPTIONS" && allow != "DELETE, POST, OPTIONS" { t.Error("unexpected Allow header value: " + allow) } }