Repository: goadesign/goa
Pull request #3364 (2023-09-25 11:41:51)

Unescape HTTP path parameters
This is a fix about the change from httptreemux to chi.

httptreemux unescapes http path parameters but chi does not.
This fix restores the previous httptreemux-like behavior.

https://github.com/dimfeld/httptreemux/blob/v5.5.0/unescape_18.go

tchssk: FYI: Due to the change from httptremux to chi, requests with a trailing slash are no longer handled as same as v3.12.4 or earier. I created a  plugin that reproduces the previous httptreemux-like behavior. https://github.com/tchssk/goaplugins/tree/master/autotrailingslash

Patch:
diff --git a/go.mod b/go.mod
index 37123f63..dbbd509a 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,7 @@ require (
 	github.com/stretchr/testify v1.8.4
 	golang.org/x/text v0.13.0
 	golang.org/x/tools v0.13.0
-	google.golang.org/grpc v1.58.1
+	google.golang.org/grpc v1.58.2
 	google.golang.org/protobuf v1.31.0
 	gopkg.in/yaml.v3 v3.0.1
 )
diff --git a/go.sum b/go.sum
index c92ffc29..eeb2c884 100644
--- a/go.sum
+++ b/go.sum
@@ -76,8 +76,8 @@ golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
-google.golang.org/grpc v1.58.1 h1:OL+Vz23DTtrrldqHK49FUOPHyY75rvFqJfXC84NYW58=
-google.golang.org/grpc v1.58.1/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
+google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I=
+google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
diff --git a/http/mux.go b/http/mux.go
index 924030cf..f918c877 100644
--- a/http/mux.go
+++ b/http/mux.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"fmt"
 	"net/http"
+	"net/url"
 	"regexp"
 
 	chi "github.com/go-chi/chi/v5"
@@ -113,14 +114,22 @@ func (m *mux) Vars(r *http.Request) map[string]string {
 	for i, k := range params.Keys {
 		if k == "*" {
 			wildcard := m.wildcards[r.Method+"::"+rctx.RoutePattern()]
-			vars[wildcard] = params.Values[i]
+			vars[wildcard] = unescape(params.Values[i])
 			continue
 		}
-		vars[k] = params.Values[i]
+		vars[k] = unescape(params.Values[i])
 	}
 	return vars
 }
 
+func unescape(s string) string {
+	u, err := url.PathUnescape(s)
+	if err != nil {
+		return s
+	}
+	return u
+}
+
 // Use appends a middleware to the list of middlewares to be applied
 // downstream the Muxer.
 func (m *mux) Use(f func(http.Handler) http.Handler) {


Test patch:
diff --git a/http/mux_test.go b/http/mux_test.go
index d8b8c569..f3eacc17 100644
--- a/http/mux_test.go
+++ b/http/mux_test.go
@@ -104,6 +104,23 @@ func TestVars(t *testing.T) {
 				"post_id": "456/789",
 			},
 		},
+		{
+			Name:    "escaped",
+			Pattern: "/users/{id}",
+			URL:     "/users/%40123",
+			Expected: map[string]string{
+				"id": "@123",
+			},
+		},
+		{
+			Name:    "escaped wildcard",
+			Pattern: "/users/{id}/posts/{*post_id}",
+			URL:     "/users/%40123/posts/456/789%24",
+			Expected: map[string]string{
+				"id":      "@123",
+				"post_id": "456/789$",
+			},
+		},
 		{
 			Name:     "no var",
 			Pattern:  "/users",