@@ -70,19 +70,34 @@ func ResponseFormat(h http.Header) Format {
7070 return FmtUnknown
7171}
7272
73- // NewDecoder returns a new decoder based on the given input format.
74- // If the input format does not imply otherwise, a text format decoder is returned.
73+ // NewDecoder returns a new decoder based on the given input format. Metric
74+ // names are validated based on the provided Format -- if the format requires
75+ // escaping, raditional Prometheues validity checking is used. Otherwise, names
76+ // are checked for UTF-8 validity. Supported formats include delimited protobuf
77+ // and Prometheus text format. For historical reasons, this decoder fallbacks
78+ // to classic text decoding for any other format. This decoder does not fully
79+ // support OpenMetrics although it may often succeed due to the similarities
80+ // between the formats. This decoder may not support the latest features of
81+ // Prometheus text format and is not intended for high-performance applications.
82+ // See: https://github.com/prometheus/common/issues/812
7583func NewDecoder (r io.Reader , format Format ) Decoder {
84+ scheme := model .LegacyValidation
85+ if format .ToEscapingScheme () == model .NoEscaping {
86+ scheme = model .UTF8Validation
87+ }
7688 switch format .FormatType () {
7789 case TypeProtoDelim :
78- return & protoDecoder {r : bufio .NewReader (r )}
90+ return & protoDecoder {r : bufio .NewReader (r ), s : scheme }
91+ case TypeProtoText , TypeProtoCompact :
92+ return & errDecoder {err : fmt .Errorf ("format %s not supported for decoding" , format )}
7993 }
80- return & textDecoder {r : r }
94+ return & textDecoder {r : r , s : scheme }
8195}
8296
8397// protoDecoder implements the Decoder interface for protocol buffers.
8498type protoDecoder struct {
8599 r protodelim.Reader
100+ s model.ValidationScheme
86101}
87102
88103// Decode implements the Decoder interface.
@@ -93,8 +108,7 @@ func (d *protoDecoder) Decode(v *dto.MetricFamily) error {
93108 if err := opts .UnmarshalFrom (d .r , v ); err != nil {
94109 return err
95110 }
96- //nolint:staticcheck // model.IsValidMetricName is deprecated.
97- if ! model .IsValidMetricName (model .LabelValue (v .GetName ())) {
111+ if ! d .s .IsValidMetricName (v .GetName ()) {
98112 return fmt .Errorf ("invalid metric name %q" , v .GetName ())
99113 }
100114 for _ , m := range v .GetMetric () {
@@ -108,27 +122,36 @@ func (d *protoDecoder) Decode(v *dto.MetricFamily) error {
108122 if ! model .LabelValue (l .GetValue ()).IsValid () {
109123 return fmt .Errorf ("invalid label value %q" , l .GetValue ())
110124 }
111- //nolint:staticcheck // model.LabelName.IsValid is deprecated.
112- if ! model .LabelName (l .GetName ()).IsValid () {
125+ if ! d .s .IsValidLabelName (l .GetName ()) {
113126 return fmt .Errorf ("invalid label name %q" , l .GetName ())
114127 }
115128 }
116129 }
117130 return nil
118131}
119132
133+ // errDecoder is an error-state decoder that always returns the same error.
134+ type errDecoder struct {
135+ err error
136+ }
137+
138+ func (d * errDecoder ) Decode (v * dto.MetricFamily ) error {
139+ return d .err
140+ }
141+
120142// textDecoder implements the Decoder interface for the text protocol.
121143type textDecoder struct {
122144 r io.Reader
123145 fams map [string ]* dto.MetricFamily
146+ s model.ValidationScheme
124147 err error
125148}
126149
127150// Decode implements the Decoder interface.
128151func (d * textDecoder ) Decode (v * dto.MetricFamily ) error {
129152 if d .err == nil {
130153 // Read all metrics in one shot.
131- var p TextParser
154+ p := TextParser { scheme : d . s }
132155 d .fams , d .err = p .TextToMetricFamilies (d .r )
133156 // If we don't get an error, store io.EOF for the end.
134157 if d .err == nil {
0 commit comments