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

Store headers in canonical form when converting from gRPC to HTTP #518

Merged
merged 2 commits into from
May 3, 2024

Conversation

andreasgerstmayr
Copy link
Contributor

What this PR does:
When applications access the HTTP header via Get() on a http.Header struct, the key is canonicalized before accessing the map [1]. Therefore, if a header is stored in a not canonicalized form, the header is inaccessible.

The Go http.Server converts header names to their canonicalized form when reading incoming HTTP requests to avoid the above issue, and make them effectively case-insensitive [2].

[1] https://cs.opensource.google/go/go/+/refs/tags/go1.22.2:src/net/textproto/header.go;l=34
[2] https://cs.opensource.google/go/go/+/refs/tags/go1.22.2:src/net/textproto/reader.go;l=548

Which issue(s) this PR fixes:
I ran into the above problem when debugging broken context propagation: OTEL reads the traceparent header with Get() on the http.Header struct [3,4]. If the traceparent header is all-lowercase in the gRPC header and is copied as-is to the http.Header, it effectively is inaccessible via the Get() function on http.Header.

The w3c trace context spec mentions vendors must accept the traceparent header name in any case (upper, lower, mixed).

[3] https://github.com/open-telemetry/opentelemetry-go/blob/dbfc75817a8486891f5b9e461b8a746129533e37/propagation/trace_context.go#L81
[4] https://github.com/open-telemetry/opentelemetry-go/blob/dbfc75817a8486891f5b9e461b8a746129533e37/propagation/propagation.go#L63

Checklist

  • Tests updated
  • CHANGELOG.md updated - the order of entries should be [CHANGE], [FEATURE], [ENHANCEMENT], [BUGFIX]

When applications access the HTTP header via Get() on a http.Header struct,
the key is canonicalized before accessing the map [1]. Therefore, if a header
is stored in a not canonicalized form, the header is inaccessible.

The Go http.Server converts header names to their canonicalized form
when reading incoming HTTP requests to avoid the above issue, and make
them effectively case-insensitive [2].

I ran into the above problem when debugging broken context propagation:
OTEL reads the traceparent header with Get() on the http.Header struct [3,4].
If the traceparent header is all-lowercase in the gRPC header and is
copied as-is to the http.Header, it effectively is inaccessible via the
Get() function on http.Header.

The w3c trace context spec mentions vendors must accept the traceparent
header name in any case (upper, lower, mixed).

[1] https://cs.opensource.google/go/go/+/refs/tags/go1.22.2:src/net/textproto/header.go;l=34
[2] https://cs.opensource.google/go/go/+/refs/tags/go1.22.2:src/net/textproto/reader.go;l=548
[3] https://github.com/open-telemetry/opentelemetry-go/blob/dbfc75817a8486891f5b9e461b8a746129533e37/propagation/trace_context.go#L81
[4] https://github.com/open-telemetry/opentelemetry-go/blob/dbfc75817a8486891f5b9e461b8a746129533e37/propagation/propagation.go#L63

Signed-off-by: Andreas Gerstmayr <agerstmayr@redhat.com>
Signed-off-by: Andreas Gerstmayr <agerstmayr@redhat.com>
Copy link
Contributor

@56quarters 56quarters left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice find! LGTM

@56quarters 56quarters merged commit c41c587 into grafana:main May 3, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants