Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .chloggen/gclog-encoding-h2-protocol.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
change_type: bug_fix
component: extension/google_cloud_logentry_encoding
note: "Accept short ALPN protocol tokens (e.g. `h2`, `h3`) in `httpRequest.protocol` that do not contain a `/`; previously any protocol string without a slash was rejected with an error, causing log entries from Google Cloud Load Balancers that switched to reporting `h2` for HTTP/2 to be silently dropped."
issues: [45214]
change_logs: [user]
Original file line number Diff line number Diff line change
Expand Up @@ -232,21 +232,32 @@ func handleHTTPRequestField(attributes pcommon.Map, req *httpRequest) error {
}

if req.Protocol != "" {
if strings.Count(req.Protocol, "/") != 1 {
return fmt.Errorf(
`invalid protocol %q: expected exactly one "/" (format "<name>/<version>", e.g. "HTTP/1.1")`,
req.Protocol,
)
}
name, version, found := strings.Cut(req.Protocol, "/")
if !found || name == "" || version == "" {
return fmt.Errorf(
`invalid protocol %q: name or version is missing (expected format "<name>/<version>", e.g. "HTTP/1.1")`,
req.Protocol,
)
name, version, hasSlash := strings.Cut(req.Protocol, "/")
if !hasSlash {
// Short ALPN token (RFC 7301). Map the well-known HTTP variants to
// "http" + numeric version so telemetry stays consistent with the
// "HTTP/1.1" form; unknown tokens fall back to the raw value as the
// protocol name with no version.
switch strings.ToLower(req.Protocol) {
case "h2", "h2c":
attributes.PutStr(string(conventions.NetworkProtocolNameKey), "http")
attributes.PutStr(string(conventions.NetworkProtocolVersionKey), "2")
case "h3":
attributes.PutStr(string(conventions.NetworkProtocolNameKey), "http")
attributes.PutStr(string(conventions.NetworkProtocolVersionKey), "3")
default:
attributes.PutStr(string(conventions.NetworkProtocolNameKey), strings.ToLower(req.Protocol))
}
} else {
if name == "" || version == "" {
return fmt.Errorf("invalid protocol %q: name or version is missing", req.Protocol)
}
if strings.Contains(version, "/") {
return fmt.Errorf("invalid protocol %q: expected at most one \"/\"", req.Protocol)
}
attributes.PutStr(string(conventions.NetworkProtocolNameKey), strings.ToLower(name))
attributes.PutStr(string(conventions.NetworkProtocolVersionKey), version)
}
attributes.PutStr(string(conventions.NetworkProtocolNameKey), strings.ToLower(name))
attributes.PutStr(string(conventions.NetworkProtocolVersionKey), version)
}

shared.PutInt(string(conventions.HTTPResponseStatusCodeKey), req.Status, attributes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,54 @@ func TestHandleHTTPRequestField(t *testing.T) {
expectsErr: "failed to parse request url",
},
{
name: "invalid protocol",
// Short ALPN token — well-known HTTP variants are normalised to
// name="http" + numeric version so telemetry stays consistent with
// the HTTP/1.1 form (regression test for #45214).
name: "h2 protocol normalised to http/2",
request: &httpRequest{
Protocol: "invalid",
Protocol: "h2",
},
expectsErr: `expected exactly one "/"`,
expectsAttributes: map[string]any{
"network.protocol.name": "http",
"network.protocol.version": "2",
},
},
{
name: "h2c protocol normalised to http/2",
request: &httpRequest{
Protocol: "h2c",
},
expectsAttributes: map[string]any{
"network.protocol.name": "http",
"network.protocol.version": "2",
},
},
{
name: "h3 protocol normalised to http/3",
request: &httpRequest{
Protocol: "h3",
},
expectsAttributes: map[string]any{
"network.protocol.name": "http",
"network.protocol.version": "3",
},
},
{
// Non-HTTP short ALPN token falls through to the raw-name path.
name: "unknown short ALPN token preserved as name",
request: &httpRequest{
Protocol: "mqtt",
},
expectsAttributes: map[string]any{
"network.protocol.name": "mqtt",
},
},
{
name: "invalid protocol — too many slashes",
request: &httpRequest{
Protocol: "a/b/c",
},
expectsErr: `at most one "/"`,
},
{
name: "invalid protocol 2",
Expand Down