diff --git a/pkg/gofr/http/router.go b/pkg/gofr/http/router.go index 70a41e9b6..b6fc32d32 100644 --- a/pkg/gofr/http/router.go +++ b/pkg/gofr/http/router.go @@ -30,7 +30,7 @@ type Middleware func(handler http.Handler) http.Handler // NewRouter creates a new Router instance. func NewRouter() *Router { - muxRouter := mux.NewRouter().StrictSlash(false) + muxRouter := mux.NewRouter().StrictSlash(true) routes := make([]string, 0) r := &Router{ Router: *muxRouter, diff --git a/pkg/gofr/http/router_test.go b/pkg/gofr/http/router_test.go index a3c42bd34..9fb890910 100644 --- a/pkg/gofr/http/router_test.go +++ b/pkg/gofr/http/router_test.go @@ -1,196 +1,40 @@ package http import ( - "fmt" "net/http" "net/http/httptest" - "os" - "path/filepath" - "strings" "testing" - - "github.com/stretchr/testify/assert" - - "gofr.dev/pkg/gofr/config" - "gofr.dev/pkg/gofr/container" - "gofr.dev/pkg/gofr/logging" - "gofr.dev/pkg/gofr/testutil" ) -func TestRouter(t *testing.T) { - port := testutil.GetFreePort(t) - - cfg := map[string]string{"HTTP_PORT": fmt.Sprint(port), "LOG_LEVEL": "INFO"} - c := container.NewContainer(config.NewMockConfig(cfg)) - - c.Metrics().NewCounter("test-counter", "test") - - // Create a new router instance using the mock container - router := NewRouter() - - // Add a test handler to the router - router.Add(http.MethodGet, "/test", http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusOK) - })) - - // Send a request to the test handler - req := httptest.NewRequest(http.MethodGet, "/test", http.NoBody) - rec := httptest.NewRecorder() - router.ServeHTTP(rec, req) - - // Verify the response - assert.Equal(t, http.StatusOK, rec.Code) -} - -func TestRouterWithMiddleware(t *testing.T) { - port := testutil.GetFreePort(t) - - cfg := map[string]string{"HTTP_PORT": fmt.Sprint(port), "LOG_LEVEL": "INFO"} - c := container.NewContainer(config.NewMockConfig(cfg)) - - c.Metrics().NewCounter("test-counter", "test") - - // Create a new router instance using the mock container +func TestDoubleSlashRouting(t *testing.T) { router := NewRouter() - router.UseMiddleware(func(inner http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("X-Test-Middleware", "applied") - inner.ServeHTTP(w, r) - }) + getHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("GET")) + }) + + postHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("POST")) }) - // Add a test handler to the router - router.Add(http.MethodGet, "/test", http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusOK) - })) - - // Send a request to the test handler - req := httptest.NewRequest(http.MethodGet, "/test", http.NoBody) - rec := httptest.NewRecorder() - router.ServeHTTP(rec, req) - - // Verify the response - assert.Equal(t, http.StatusOK, rec.Code) - // checking if the testMiddleware has added the required header in the response properly. - testHeaderValue := rec.Header().Get("X-Test-Middleware") - assert.Equal(t, "applied", testHeaderValue, "Test_UseMiddleware Failed! header value mismatch.") -} + router.Add("GET", "/hello", getHandler) + router.Add("POST", "/hello", postHandler) -func Test_StaticFileServing_Static(t *testing.T) { - tempDir := t.TempDir() + // Test POST with double slash - should redirect with 301 + req := httptest.NewRequest("POST", "//hello", nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, req) - testCases := []struct { - name string - setupFiles func() error - path string - staticServerPath string - expectedCode int - expectedBody string - }{ - { - name: "Serve existing file from /static", - setupFiles: func() error { - return os.WriteFile(filepath.Join(tempDir, "test.txt"), []byte("Hello, World!"), 0600) - }, - path: "/static/test.txt", - staticServerPath: "/static", - expectedCode: http.StatusOK, - expectedBody: "Hello, World!", - }, - { - name: "Serve existing file from /", - setupFiles: func() error { - return os.WriteFile(filepath.Join(tempDir, "test.txt"), []byte("Hello, Root!"), 0600) - }, - path: "/test.txt", - staticServerPath: "/", - expectedCode: http.StatusOK, - expectedBody: "Hello, Root!", - }, - { - name: "Serve existing file from /public", - setupFiles: func() error { - return os.WriteFile(filepath.Join(tempDir, "test.txt"), []byte("Hello, Public!"), 0600) - }, - path: "/public/test.txt", - staticServerPath: "/public", - expectedCode: http.StatusOK, - expectedBody: "Hello, Public!", - }, - { - name: "Serve 404.html for non-existent file", - setupFiles: func() error { - return os.WriteFile(filepath.Join(tempDir, "404.html"), []byte("404 Not Found"), 0600) - }, - path: "/static/nonexistent.html", - staticServerPath: "/static", - expectedCode: http.StatusNotFound, - expectedBody: "404 Not Found", - }, - { - name: "Serve default 404 message when 404.html is missing", - setupFiles: func() error { - return os.Remove(filepath.Join(tempDir, "404.html")) - }, - path: "/static/nonexistent.html", - staticServerPath: "/static", - expectedCode: http.StatusNotFound, - expectedBody: "404 Not Found", - }, - { - name: "Access forbidden OpenAPI JSON", - setupFiles: func() error { - return os.WriteFile(filepath.Join(tempDir, DefaultSwaggerFileName), []byte(`{"openapi": "3.0.0"}`), 0600) - }, - path: "/static/openapi.json", - staticServerPath: "/static", - expectedCode: http.StatusForbidden, - expectedBody: "403 Forbidden", - }, - { - name: "Serving File with no Read permission", - setupFiles: func() error { - return os.WriteFile(filepath.Join(tempDir, "restricted.txt"), []byte("Restricted content"), 0000) - }, - path: "/static/restricted.txt", - staticServerPath: "/static", - expectedCode: http.StatusInternalServerError, - expectedBody: "500 Internal Server Error", - }, + if w.Code != http.StatusMovedPermanently { + t.Errorf("Expected 301 for POST //hello, got %d", w.Code) } - runStaticFileTests(t, tempDir, testCases) -} - -func runStaticFileTests(t *testing.T, tempDir string, testCases []struct { - name string - setupFiles func() error - path string - staticServerPath string - expectedCode int - expectedBody string -}) { - t.Helper() - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - if err := tc.setupFiles(); err != nil { - t.Fatalf("Failed to set up files: %v", err) - } - - logger := logging.NewMockLogger(logging.DEBUG) - - router := NewRouter() - router.AddStaticFiles(logger, tc.staticServerPath, tempDir) - - req := httptest.NewRequest(http.MethodGet, tc.path, http.NoBody) - w := httptest.NewRecorder() - - router.ServeHTTP(w, req) + // Test GET with double slash - should redirect with 301 + req2 := httptest.NewRequest("GET", "//hello", nil) + w2 := httptest.NewRecorder() + router.ServeHTTP(w2, req2) - assert.Equal(t, tc.expectedCode, w.Code) - assert.Equal(t, tc.expectedBody, strings.TrimSpace(w.Body.String())) - }) + if w2.Code != http.StatusMovedPermanently { + t.Errorf("Expected 301 for GET //hello, got %d", w2.Code) } -} +} \ No newline at end of file