Skip to content

Commit 41068fa

Browse files
committed
fixes issue 375: context not preserved after routing
After RouteInfo is called new request object with new context is passed, and all middlewares handling that request after returning don't have access to it. This fix copies the request with routing context to the request passed as a function variable so all middlewares now preserve the request context.
1 parent f4f99b4 commit 41068fa

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

middleware/router.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ func NewRouter(ctx *Context, next http.Handler) http.Handler {
6565
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
6666
if _, rCtx, ok := ctx.RouteInfo(r); ok {
6767
next.ServeHTTP(rw, rCtx)
68+
*r = *rCtx
6869
return
6970
}
7071

middleware/router_context_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package middleware
2+
3+
import (
4+
stdcontext "context"
5+
stderrors "errors"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
"net/http/httptest"
10+
"testing"
11+
12+
"github.com/go-openapi/runtime/internal/testing/petstore"
13+
"github.com/go-openapi/testify/v2/assert"
14+
"github.com/go-openapi/testify/v2/require"
15+
)
16+
17+
func TestRouterContext_Issue375(t *testing.T) {
18+
spec, api := petstore.NewAPI(t)
19+
spec.Spec().BasePath = "/api/"
20+
context := NewContext(spec, api, nil)
21+
22+
type authCtxKey uint8
23+
const authCtx authCtxKey = iota + 1
24+
authCtxErr := stderrors.New("test error in context")
25+
26+
mw := NewRouter(context, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
27+
// check context after API middleware
28+
authContext := stdcontext.WithValue(r.Context(), authCtx, authCtxErr)
29+
*r = *r.WithContext(authContext)
30+
}))
31+
32+
start := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
33+
t.Logf("calling API router with context: %v", authCtxErr)
34+
mw.ServeHTTP(w, r)
35+
36+
value := r.Context().Value(authCtx)
37+
assert.NotNilf(t, value, "end of middleware chain: expected to find authCtx in request context")
38+
39+
if value == nil {
40+
w.WriteHeader(http.StatusInternalServerError)
41+
}
42+
43+
errAuth, ok := value.(error)
44+
assert.Truef(t, ok, "expected authCtx to be an error, but got: %T", value)
45+
t.Logf("got context from request: %v", errAuth)
46+
fmt.Fprintf(w, "%v", errAuth)
47+
w.WriteHeader(http.StatusOK)
48+
49+
// *r = *r.WithContext(authContext)
50+
// mw.ServeHTTP(w, r)
51+
})
52+
53+
recorder := httptest.NewRecorder()
54+
request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "/api/pets/123", nil)
55+
require.NoError(t, err)
56+
57+
start.ServeHTTP(recorder, request)
58+
assert.Equal(t, http.StatusOK, recorder.Code)
59+
60+
res := recorder.Result()
61+
require.NotNil(t, res.Body)
62+
msg, err := io.ReadAll(res.Body)
63+
require.NoError(t, err)
64+
t.Logf("response message: %q", string(msg))
65+
}

0 commit comments

Comments
 (0)