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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions