Skip to content

expfmt: NegotiateIncludingOpenMetrics makes it hard to select the first supported format when a server supports only a subset of formats #928

Description

@adziauho

NegotiateIncludingOpenMetrics (in https://github.com/prometheus/common/blob/main/expfmt/encode.go) returns the single highest-quality format from the Accept header. If a server does not support that format, there is no straightforward way to fall back to the next
recognised format from the client's preference list — the caller would have to reimplement the entire negotiation loop manually.

Example

A server that supports OpenMetrics and text/plain but not protobuf receives:

Accept: application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.6,                                                                                                                                                                  
          application/openmetrics-text;version=1.0.0;escaping=allow-utf-8;q=0.5,                                                                                                                                                                                             
          text/plain;version=0.0.4;q=0.2                   

NegotiateIncludingOpenMetrics returns FmtProtoDelim. The server cannot serve it, but has no API to ask "what is the next format the client accepts that I can serve?" — the only escape hatch today is an unconditional fallback to FmtText, ignoring
application/openmetrics-text which both sides support.

Suggested fix

Add a companion function to expfmt/encode.go, leaving NegotiateIncludingOpenMetrics unchanged:

// NegotiateFormatsIncludingOpenMetrics works like NegotiateIncludingOpenMetrics                                                                                                                                                                                           
// but returns all recognised formats from the Accept header in descending                                                                                                                                                                                                 
// preference order instead of stopping at the first match. If no recognised                                                                                                                                                                                               
// formats are found, the slice contains FmtText as the default fallback.                                                                                                                                                                                                  
func NegotiateFormatsIncludingOpenMetrics(h http.Header) []Format      

The caller can then iterate and pick the first format it supports, without reimplementing the negotiation loop.

This is useful fix to a content negotiation bug in kube-state-metrics (kubernetes/kube-state-metrics#3010).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions