Hello,
I'm encountering unexpected behavior with net/http routing in Go 1.24.1
(amd64, ubuntu linux) when using http.StripPrefix to delegate to a nested
http.ServeMux which uses the Go 1.22+ METHOD /path routing syntax.
Instead of the nested ServeMux executing its registered handlers for the
stripped path, it consistently returns a 301 Moved Permanently redirect to
the stripped path itself.
```go
package main
import (
"fmt"
"log"
"net/http"
"runtime"
)
func handleInnerTest(w http.ResponseWriter, r *http.Request) {
log.Printf("HANDLER HIT: handleInnerTest (GET /test) | Received Path:
%s\n", r.URL.Path)
fmt.Fprintln(w, "OK - GET /test")
}
func handleInnerLogin(w http.ResponseWriter, r *http.Request) {
log.Printf("HANDLER HIT: handleInnerLogin (POST /login) | Received Path:
%s\n", r.URL.Path)
if r.Method != http.MethodPost {
http.Error(w, "Method Not Allowed (Handler expected POST)",
http.StatusMethodNotAllowed)
return
}
fmt.Fprintln(w, "OK - POST /login")
}
func main() {
log.Printf("Go Version: %s\n", runtime.Version())
innerMux := http.NewServeMux()
innerMux.HandleFunc("GET /test", handleInnerTest)
innerMux.HandleFunc("POST /login", handleInnerLogin)
innerMux.HandleFunc("GET /api2/test2", handleInnerTest)
outerMux := http.NewServeMux()
// This should remove "/api/" before innerMux sees the request.
outerMux.Handle("/api/", http.StripPrefix("/api/", innerMux))
outerMux.Handle("/api2/", innerMux)
outerMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
log.Printf("HANDLER HIT: Outer Root Handler | Received Path: %s\n",
r.URL.Path)
http.NotFound(w, r)
})
port := ":8090"
log.Printf("Starting server on %s...\n", port)
log.Println("----\nEXPECTED BEHAVIOR:")
log.Println(" GET /api/test -> 200 OK ('OK - GET /test')")
log.Println(" GET /api/nonexistent -> 404 Not Found (from innerMux)")
log.Println(" POST /api/login -> 200 OK ('OK - POST /login')")
log.Println("----")
log.Println("Run tests like:")
log.Println(" curl -v http://localhost:8090/api/test") // is
HTTP/1.1 301 Moved Permanently && no log print
log.Println(" curl -v http://localhost:8090/api/nonexistent") // is
HTTP/1.1 301 Moved Permanently && no log print
log.Println(" curl -v -X POST http://localhost:8090/api/login") // is
HTTP/1.1 301 Moved Permanently && no log print
log.Println(" curl -v http://localhost:8090/test/") // is
HTTP/1.1 404 Not Found && prints HANDLER HIT: Outer Root Handler | Received
Path: /test/
log.Println(" curl -v http://localhost:8090/api2/test2") // is
HTTP/1.1 200 OK && prints HANDLER HIT: handleInnerTest (GET /test) |
Received Path: /api2/test2
log.Println("----")
err := http.ListenAndServe(port, outerMux)
if err != nil {
log.Fatalf("Server failed: %v\n", err)
}
}
```
It seems counter-intuitive that after http.StripPrefix modifies the path,
the inner ServeMux doesn't seem to use that stripped path to match its
handlers, instead issuing a redirect, like curl -v
http://localhost:8090/api/test -> `<a href="/test">Moved Permanently</a>`
Have I misunderstood how these components should interact?
Thanks for any insights.
--
You received this message because you are subscribed to the Google Groups
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/golang-nuts/cbd4f731-8bcb-4683-8f81-115bb8e4f1a7n%40googlegroups.com.