Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gin Context implementation breaks context.Context contract #3888

Closed
dlinkov-roblox opened this issue Mar 13, 2024 · 4 comments
Closed

Gin Context implementation breaks context.Context contract #3888

dlinkov-roblox opened this issue Mar 13, 2024 · 4 comments
Labels

Comments

@dlinkov-roblox
Copy link

Description

As also described in this issue, open-telemetry/opentelemetry-go-contrib#5277, the existing OpenTelemetry packages do not interact very well with gin.Context. It was pointed out here that this is an example of gin.Context not fulfilling the following specification of context.Context.WithValue: The provided key must be comparable and should not be of type string or any other built-in type to avoid collisions between packages using context. Users of WithValue should define their own types for keys.

The two pieces of code that are having colliding values in the context are here for OpenTelemetry, and here for Gin.

How to reproduce

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
	"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
	oteltrace "go.opentelemetry.io/otel/sdk/trace"
	"go.opentelemetry.io/otel/trace"
)

func main() {
	sampler := oteltrace.AlwaysSample()
	exporter, _ := stdouttrace.New(stdouttrace.WithPrettyPrint())

	tp := oteltrace.NewTracerProvider(
		oteltrace.WithBatcher(exporter),
		oteltrace.WithSampler(sampler),
	)
	otel.SetTracerProvider(tp)
	r := gin.New()
	r.GET("/health_check", otelgin.Middleware("example-service"), HealthCheckHandler)
	r.Run(":9000")
}

func HealthCheckHandler(c *gin.Context) {
	span := trace.SpanFromContext(c)
	c.JSON(http.StatusOK, gin.H{"span_id": span.SpanContext().SpanID()})
}

Expectations

$ curl http://localhost:9000/health_check
<A span ID that is not all zeros>

Actual result

$ curl -i http://localhost:9000/hello/world
<A span ID that is all zeros>

Note that this is an issue as the span context will not be propagated properly unless a user passes c.Request.Context() explicitly. Since a gin.Context implements the context.Context interface, this is a subtle bug that would not be caught by the type system.

Environment

  • go version: 1.19
  • gin version (or commit ref): 1.9.1
  • operating system: Mac OS Sonoma
@FarmerChillax
Copy link
Contributor

FarmerChillax commented Mar 20, 2024

I also encountered this confusion. I don’t know why gin does this. It seems to be a legacy issue. I can’t find a place to use it in the source code.

Therefore I made changes in this PR(#3897) to be as compatible as possible. respect the context.WithValue contract well should better.

@FarmerChillax
Copy link
Contributor

FarmerChillax commented Mar 20, 2024

A more compatible approach might be like this?

// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
func (c *Context) Value(key any) any {
	if key == 0 {
		val := c.Request.Context().Value(key)
		if val != nil {
			return val
		}
		return c.Request
	}
	if key == ContextKey {
		return c
	}
	if keyAsString, ok := key.(string); ok {
		if val, exists := c.Get(keyAsString); exists {
			return val
		}
	}
	if !c.hasRequestContext() {
		return nil
	}
	return c.Request.Context().Value(key)
}

@appleboy
Copy link
Member

Fixed in #3897 Thanks @FarmerChillax

@appleboy appleboy added the bug label Mar 21, 2024
@FarmerChillax
Copy link
Contributor

Hi @appleboy , this feature is very important to me, I would like to know when it will be released

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants