forked from mirror/client_golang
Fix escaping even more.
Change-Id: Ie958d557aae0dda68c451e9fafc615221cc07bb0
This commit is contained in:
parent
82e55cd560
commit
d3ebb29141
|
@ -54,7 +54,8 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
||||||
if in.Help != nil {
|
if in.Help != nil {
|
||||||
n, err := fmt.Fprintf(
|
n, err := fmt.Fprintf(
|
||||||
out, "# HELP %s %s\n",
|
out, "# HELP %s %s\n",
|
||||||
name, strings.Replace(*in.Help, "\n", `\n`, -1))
|
name, escapeString(*in.Help, false),
|
||||||
|
)
|
||||||
written += n
|
written += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return written, err
|
return written, err
|
||||||
|
@ -217,7 +218,7 @@ func labelPairsToText(
|
||||||
for _, lp := range in {
|
for _, lp := range in {
|
||||||
n, err := fmt.Fprintf(
|
n, err := fmt.Fprintf(
|
||||||
out, `%c%s="%s"`,
|
out, `%c%s="%s"`,
|
||||||
separator, lp.GetName(), escapeLabelValue(lp.GetValue()),
|
separator, lp.GetName(), escapeString(lp.GetValue(), true),
|
||||||
)
|
)
|
||||||
written += n
|
written += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -229,7 +230,7 @@ func labelPairsToText(
|
||||||
n, err := fmt.Fprintf(
|
n, err := fmt.Fprintf(
|
||||||
out, `%c%s="%s"`,
|
out, `%c%s="%s"`,
|
||||||
separator, additionalLabelName,
|
separator, additionalLabelName,
|
||||||
escapeLabelValue(additionalLabelValue),
|
escapeString(additionalLabelValue, true),
|
||||||
)
|
)
|
||||||
written += n
|
written += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -244,16 +245,17 @@ func labelPairsToText(
|
||||||
return written, nil
|
return written, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// escapeLabelValue replaces '\' by '\\', '"' by '\"', and new line character by '\n'.
|
// escapeString replaces '\' by '\\', new line character by '\n', and - if
|
||||||
func escapeLabelValue(v string) string {
|
// includeDoubleQuote is true - '"' by '\"'.
|
||||||
|
func escapeString(v string, includeDoubleQuote bool) string {
|
||||||
result := bytes.NewBuffer(make([]byte, 0, len(v)))
|
result := bytes.NewBuffer(make([]byte, 0, len(v)))
|
||||||
for _, c := range v {
|
for _, c := range v {
|
||||||
switch c {
|
switch {
|
||||||
case '\\':
|
case c == '\\':
|
||||||
result.WriteString(`\\`)
|
result.WriteString(`\\`)
|
||||||
case '"':
|
case includeDoubleQuote && c == '"':
|
||||||
result.WriteString(`\"`)
|
result.WriteString(`\"`)
|
||||||
case '\n':
|
case c == '\n':
|
||||||
result.WriteString(`\n`)
|
result.WriteString(`\n`)
|
||||||
default:
|
default:
|
||||||
result.WriteRune(c)
|
result.WriteRune(c)
|
||||||
|
|
|
@ -33,7 +33,7 @@ func testCreate(t test.Tester) {
|
||||||
{
|
{
|
||||||
in: &dto.MetricFamily{
|
in: &dto.MetricFamily{
|
||||||
Name: proto.String("name"),
|
Name: proto.String("name"),
|
||||||
Help: proto.String("two-line\n doc string"),
|
Help: proto.String("two-line\n doc str\\ing"),
|
||||||
Type: dto.MetricType_COUNTER.Enum(),
|
Type: dto.MetricType_COUNTER.Enum(),
|
||||||
Metric: []*dto.Metric{
|
Metric: []*dto.Metric{
|
||||||
&dto.Metric{
|
&dto.Metric{
|
||||||
|
@ -69,7 +69,7 @@ func testCreate(t test.Tester) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
out: `# HELP name two-line\n doc string
|
out: `# HELP name two-line\n doc str\\ing
|
||||||
# TYPE name counter
|
# TYPE name counter
|
||||||
name{labelname="val1",basename="basevalue"} NaN
|
name{labelname="val1",basename="basevalue"} NaN
|
||||||
name{labelname="val2",basename="basevalue"} 0.23 1234567890
|
name{labelname="val2",basename="basevalue"} 0.23 1234567890
|
||||||
|
@ -79,7 +79,7 @@ name{labelname="val2",basename="basevalue"} 0.23 1234567890
|
||||||
{
|
{
|
||||||
in: &dto.MetricFamily{
|
in: &dto.MetricFamily{
|
||||||
Name: proto.String("gauge_name"),
|
Name: proto.String("gauge_name"),
|
||||||
Help: proto.String("gauge\ndoc\nstring"),
|
Help: proto.String("gauge\ndoc\nstr\"ing"),
|
||||||
Type: dto.MetricType_GAUGE.Enum(),
|
Type: dto.MetricType_GAUGE.Enum(),
|
||||||
Metric: []*dto.Metric{
|
Metric: []*dto.Metric{
|
||||||
&dto.Metric{
|
&dto.Metric{
|
||||||
|
@ -114,7 +114,7 @@ name{labelname="val2",basename="basevalue"} 0.23 1234567890
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
out: `# HELP gauge_name gauge\ndoc\nstring
|
out: `# HELP gauge_name gauge\ndoc\nstr"ing
|
||||||
# TYPE gauge_name gauge
|
# TYPE gauge_name gauge
|
||||||
gauge_name{name_1="val with\nnew line",name_2="val with \\backslash and \"quotes\""} +Inf
|
gauge_name{name_1="val with\nnew line",name_2="val with \\backslash and \"quotes\""} +Inf
|
||||||
gauge_name{name_1="Björn",name_2="佖佥"} 3.14e+42
|
gauge_name{name_1="Björn",name_2="佖佥"} 3.14e+42
|
||||||
|
|
|
@ -401,7 +401,7 @@ func (p *Parser) startTimestamp() stateFn {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
p.currentMetric.TimestampMs = proto.Int64(timestamp)
|
p.currentMetric.TimestampMs = proto.Int64(timestamp)
|
||||||
if p.readTokenUntilNewline(); p.err != nil {
|
if p.readTokenUntilNewline(false); p.err != nil {
|
||||||
return nil // Unexpected end of input.
|
return nil // Unexpected end of input.
|
||||||
}
|
}
|
||||||
if p.currentToken.Len() > 0 {
|
if p.currentToken.Len() > 0 {
|
||||||
|
@ -419,12 +419,10 @@ func (p *Parser) readingHelp() stateFn {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Rest of line is the docstring.
|
// Rest of line is the docstring.
|
||||||
if p.readTokenUntilNewline(); p.err != nil {
|
if p.readTokenUntilNewline(true); p.err != nil {
|
||||||
return nil // Unexpected end of input.
|
return nil // Unexpected end of input.
|
||||||
}
|
}
|
||||||
p.currentMF.Help = proto.String(
|
p.currentMF.Help = proto.String(p.currentToken.String())
|
||||||
strings.Replace(p.currentToken.String(), `\n`, "\n", -1),
|
|
||||||
)
|
|
||||||
return p.startOfLine
|
return p.startOfLine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,7 +434,7 @@ func (p *Parser) readingType() stateFn {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Rest of line is the type.
|
// Rest of line is the type.
|
||||||
if p.readTokenUntilNewline(); p.err != nil {
|
if p.readTokenUntilNewline(false); p.err != nil {
|
||||||
return nil // Unexpected end of input.
|
return nil // Unexpected end of input.
|
||||||
}
|
}
|
||||||
metricType, ok := dto.MetricType_value[strings.ToUpper(p.currentToken.String())]
|
metricType, ok := dto.MetricType_value[strings.ToUpper(p.currentToken.String())]
|
||||||
|
@ -490,11 +488,34 @@ func (p *Parser) readTokenUntilWhitespace() {
|
||||||
// readTokenUntilNewline copies bytes from p.buf into p.currentToken. The first
|
// readTokenUntilNewline copies bytes from p.buf into p.currentToken. The first
|
||||||
// byte considered is the byte already read (now in p.currentByte). The first
|
// byte considered is the byte already read (now in p.currentByte). The first
|
||||||
// newline byte encountered is still copied into p.currentByte, but not into
|
// newline byte encountered is still copied into p.currentByte, but not into
|
||||||
// p.currentToken.
|
// p.currentToken. If recognizeEscapeSequence is true, two escape sequences are
|
||||||
func (p *Parser) readTokenUntilNewline() {
|
// recognized: '\\' tranlates into '\', and '\n' into a line-feed character. All
|
||||||
|
// other escape sequences are invalid and cause an error.
|
||||||
|
func (p *Parser) readTokenUntilNewline(recognizeEscapeSequence bool) {
|
||||||
p.currentToken.Reset()
|
p.currentToken.Reset()
|
||||||
for p.err == nil && p.currentByte != '\n' {
|
escaped := false
|
||||||
p.currentToken.WriteByte(p.currentByte)
|
for p.err == nil {
|
||||||
|
if recognizeEscapeSequence && escaped {
|
||||||
|
switch p.currentByte {
|
||||||
|
case '\\':
|
||||||
|
p.currentToken.WriteByte(p.currentByte)
|
||||||
|
case 'n':
|
||||||
|
p.currentToken.WriteByte('\n')
|
||||||
|
default:
|
||||||
|
p.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", p.currentByte))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
escaped = false
|
||||||
|
} else {
|
||||||
|
switch p.currentByte {
|
||||||
|
case '\n':
|
||||||
|
return
|
||||||
|
case '\\':
|
||||||
|
escaped = true
|
||||||
|
default:
|
||||||
|
p.currentToken.WriteByte(p.currentByte)
|
||||||
|
}
|
||||||
|
}
|
||||||
p.currentByte, p.err = p.buf.ReadByte()
|
p.currentByte, p.err = p.buf.ReadByte()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,9 +91,9 @@ no_labels{} 3
|
||||||
# TYPE name counter
|
# TYPE name counter
|
||||||
name{labelname="val1",basename="basevalue"} NaN
|
name{labelname="val1",basename="basevalue"} NaN
|
||||||
name {labelname="val2",basename="base\"v\\al\nue"} 0.23 1234567890
|
name {labelname="val2",basename="base\"v\\al\nue"} 0.23 1234567890
|
||||||
# HELP name two-line\n doc string
|
# HELP name two-line\n doc str\\ing
|
||||||
|
|
||||||
# HELP name2 doc string 2
|
# HELP name2 doc str"ing 2
|
||||||
# TYPE name2 gauge
|
# TYPE name2 gauge
|
||||||
name2{labelname="val2" ,basename = "basevalue2" } +Inf 54321
|
name2{labelname="val2" ,basename = "basevalue2" } +Inf 54321
|
||||||
name2{ labelname = "val1" }-Inf
|
name2{ labelname = "val1" }-Inf
|
||||||
|
@ -101,7 +101,7 @@ name2{ labelname = "val1" }-Inf
|
||||||
out: []*dto.MetricFamily{
|
out: []*dto.MetricFamily{
|
||||||
&dto.MetricFamily{
|
&dto.MetricFamily{
|
||||||
Name: proto.String("name"),
|
Name: proto.String("name"),
|
||||||
Help: proto.String("two-line\n doc string"),
|
Help: proto.String("two-line\n doc str\\ing"),
|
||||||
Type: dto.MetricType_COUNTER.Enum(),
|
Type: dto.MetricType_COUNTER.Enum(),
|
||||||
Metric: []*dto.Metric{
|
Metric: []*dto.Metric{
|
||||||
&dto.Metric{
|
&dto.Metric{
|
||||||
|
@ -139,7 +139,7 @@ name2{ labelname = "val1" }-Inf
|
||||||
},
|
},
|
||||||
&dto.MetricFamily{
|
&dto.MetricFamily{
|
||||||
Name: proto.String("name2"),
|
Name: proto.String("name2"),
|
||||||
Help: proto.String("doc string 2"),
|
Help: proto.String("doc str\"ing 2"),
|
||||||
Type: dto.MetricType_GAUGE.Enum(),
|
Type: dto.MetricType_GAUGE.Enum(),
|
||||||
Metric: []*dto.Metric{
|
Metric: []*dto.Metric{
|
||||||
&dto.Metric{
|
&dto.Metric{
|
||||||
|
|
Loading…
Reference in New Issue