From 3d0061307615de1cb55cf505510b27f65b0165ae Mon Sep 17 00:00:00 2001
From: Aliaksandr Valialkin <valyala@gmail.com>
Date: Sun, 6 Dec 2020 11:59:13 +0200
Subject: [PATCH] lib/protoparser/influx: allow multiple whitespace chars
 between measurement, fields and timestamp in Influx line protocol

---
 docs/CHANGELOG.md                     |  4 ++++
 lib/protoparser/influx/parser.go      | 11 ++++++++--
 lib/protoparser/influx/parser_test.go | 30 +++++++++++++++++++++++++++
 3 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index d81781eb6e..4e0c99124e 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -2,6 +2,10 @@
 
 # tip
 
+* FEATURE: allow multiple whitespace chars between measurements, fields and timestamp when parsing InfluxDB line protocol.
+  Though [InfluxDB line protocol](https://docs.influxdata.com/influxdb/v1.8/write_protocols/line_protocol_tutorial/) denies multiple whitespace chars between these entities,
+  some apps improperly put multiple whitespace chars. This workaround allows accepting data from such apps.
+
 
 # [v1.49.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.49.0)
 
diff --git a/lib/protoparser/influx/parser.go b/lib/protoparser/influx/parser.go
index e08d42ee2a..0e8680697e 100644
--- a/lib/protoparser/influx/parser.go
+++ b/lib/protoparser/influx/parser.go
@@ -69,7 +69,7 @@ func (r *Row) unmarshal(s string, tagsPool []Tag, fieldsPool []Field, noEscapeCh
 		return tagsPool, fieldsPool, fmt.Errorf("cannot find Whitespace I in %q", s)
 	}
 	measurementTags := s[:n]
-	s = s[n+1:]
+	s = stripLeadingWhitespace(s[n+1:])
 
 	// Parse measurement and tags
 	var err error
@@ -110,7 +110,7 @@ func (r *Row) unmarshal(s string, tagsPool []Tag, fieldsPool []Field, noEscapeCh
 		return tagsPool, fieldsPool, err
 	}
 	r.Fields = fieldsPool[fieldsStart:]
-	s = s[n+1:]
+	s = stripLeadingWhitespace(s[n+1:])
 
 	// Parse timestamp
 	timestamp, err := fastfloat.ParseInt64(s)
@@ -409,3 +409,10 @@ func isInQuote(s string, noEscapeChars bool) bool {
 		s = s[n+1:]
 	}
 }
+
+func stripLeadingWhitespace(s string) string {
+	for len(s) > 0 && s[0] == ' ' {
+		s = s[1:]
+	}
+	return s
+}
diff --git a/lib/protoparser/influx/parser_test.go b/lib/protoparser/influx/parser_test.go
index 6d75d04c05..50c625b9fc 100644
--- a/lib/protoparser/influx/parser_test.go
+++ b/lib/protoparser/influx/parser_test.go
@@ -468,6 +468,36 @@ func TestRowsUnmarshalSuccess(t *testing.T) {
 		},
 	})
 
+	// Superfluous whitespace between tags, fields and timestamps.
+	f(`cpu_utilization,host=mnsbook-pro.local value=119.8 1607222595591`, &Rows{
+		Rows: []Row{{
+			Measurement: "cpu_utilization",
+			Tags: []Tag{{
+				Key:   "host",
+				Value: "mnsbook-pro.local",
+			}},
+			Fields: []Field{{
+				Key:   "value",
+				Value: 119.8,
+			}},
+			Timestamp: 1607222595591,
+		}},
+	})
+	f(`cpu_utilization,host=mnsbook-pro.local   value=119.8   1607222595591`, &Rows{
+		Rows: []Row{{
+			Measurement: "cpu_utilization",
+			Tags: []Tag{{
+				Key:   "host",
+				Value: "mnsbook-pro.local",
+			}},
+			Fields: []Field{{
+				Key:   "value",
+				Value: 119.8,
+			}},
+			Timestamp: 1607222595591,
+		}},
+	})
+
 	f("x,y=z,g=p:\\ \\ 5432\\,\\ gp\\ mon\\ [lol]\\ con10\\ cmd5\\ SELECT f=1", &Rows{
 		Rows: []Row{{
 			Measurement: "x",