Description
Bug Description
When using the adaptor package to adapt an existing http.Handler
to fiber.Handler
, the fiber.Ctx#UserContext
is no more accessible and all values already stored within are no more accessible as well.
Here down an example illustrating the issue:
package main
import (
"context"
"fmt"
"net/http"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/adaptor"
)
func main() {
userNameCtxKey := struct{}{}
app := fiber.New()
// Middleware enriching the UserContext
app.Use(func(c *fiber.Ctx) error {
userCtx := context.WithValue(c.UserContext(), userNameCtxKey, "go-fiber")
c.SetUserContext(userCtx)
return c.Next()
})
helloHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Hello %s", r.Context().Value(userNameCtxKey))
})
helloFiberHandler := adaptor.HTTPHandler(helloHandler)
app.Get("/hello", helloFiberHandler)
helloInternalHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusOK)
userCtx := r.Context().Value("__local_user_context__").(context.Context)
fmt.Fprintf(w, "Hello %s", userCtx.Value(userNameCtxKey))
})
helloInternalFiberHandler := adaptor.HTTPHandler(helloInternalHandler)
app.Get("/hello", helloFiberHandler)
app.Get("/hello-internal", helloInternalFiberHandler)
app.Listen(":8080")
}
As illustrated above, the context value with key userNameCtxKey
initially stored in c.UserContext
is not accessible through the raw http.Handler
http.Request#Context
since the latter use *fasthttp.RequestCtx
as its Context
value which still holds the user request context but hidded under the __local_user_context__
internal key as demonstrated within the second handler adaptor.
The above is for sure a simple illustration of the issue but the implications of this in real world scenarios are indeed impacting as context.Context
is the main medium for cross-boundaries values passing.
One of the main use cases where this issue surfaced where propagation of incoming OpenTelemetry trace span when using https://github.com/gofiber/contrib/blob/main/otelfiber/:
- The incoming trace is properly extracted from header then a new span is created and its context is injected as the
UserContext
:
spanName := utils.CopyString(c.Path())
ctx, span := tracer.Start(ctx, spanName, opts...)
defer span.End()
// pass the span through userContext
c.SetUserContext(ctx)
- gqlgen HTTP handler is being used for GQL requests processing:
graphHandler := adaptor.HTTPHandler(gqlServer)
fiberApp.Post("/gqlapi", graphHandler)
- Parent span is not accessible in operation interceptor (and any other function using
context.Context
as argument)
type tracingOperationInterceptor struct {
}
func (i *tracingInterceptor) InterceptOperation(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler {
span := trace.SpanFormContext(ctx) // Span is not properly resolved
}
How to Reproduce
Steps to reproduce the behavior: Run below sample then cURL
http://localhost:8080/hello
the http://localhost:8080/hello-internal
:
package main
import (
"context"
"fmt"
"net/http"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/adaptor"
)
func main() {
userNameCtxKey := struct{}{}
app := fiber.New()
// Middleware enriching the UserContext
app.Use(func(c *fiber.Ctx) error {
userCtx := context.WithValue(c.UserContext(), userNameCtxKey, "go-fiber")
c.SetUserContext(userCtx)
return c.Next()
})
helloHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Hello %s", r.Context().Value(userNameCtxKey))
})
helloFiberHandler := adaptor.HTTPHandler(helloHandler)
app.Get("/hello", helloFiberHandler)
helloInternalHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusOK)
userCtx := r.Context().Value("__local_user_context__").(context.Context)
fmt.Fprintf(w, "Hello %s", userCtx.Value(userNameCtxKey))
})
helloInternalFiberHandler := adaptor.HTTPHandler(helloInternalHandler)
app.Get("/hello", helloFiberHandler)
app.Get("/hello-internal", helloInternalFiberHandler)
app.Listen(":8080")
}
Expected Behavior
Fiber should either:
- Expose the
#UserContext
key, being__local_user_context__
, publicly so it can be resolved from within any function as long as the incomingc.Context()
is accessible - Provide a safe mechanism to copy
#UserContext
into*fiber.Ctx#Context
prior to adapting anhttp.Handler
Fiber Version
v2.50.0
Code Snippet (optional)
package main
import (
"context"
"fmt"
"net/http"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/adaptor"
)
func main() {
userNameCtxKey := struct{}{}
app := fiber.New()
// Middleware enriching the UserContext
app.Use(func(c *fiber.Ctx) error {
userCtx := context.WithValue(c.UserContext(), userNameCtxKey, "go-fiber")
c.SetUserContext(userCtx)
return c.Next()
})
helloHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Hello %s", r.Context().Value(userNameCtxKey))
})
helloFiberHandler := adaptor.HTTPHandler(helloHandler)
app.Get("/hello", helloFiberHandler)
helloInternalHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusOK)
userCtx := r.Context().Value("__local_user_context__").(context.Context)
fmt.Fprintf(w, "Hello %s", userCtx.Value(userNameCtxKey))
})
helloInternalFiberHandler := adaptor.HTTPHandler(helloInternalHandler)
app.Get("/hello", helloFiberHandler)
app.Get("/hello-internal", helloInternalFiberHandler)
app.Listen(":8080")
}
Checklist:
- I agree to follow Fiber's Code of Conduct.
- I have checked for existing issues that describe my problem prior to opening this one.
- I understand that improperly formatted bug reports may be closed without explanation.