prometheus-client-mmap 0.24.3-x86_64-linux → 0.24.4-x86_64-linux

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 96d81412cc07d51c07e2a4d3d63d24a0977f8f3218a469bace79a9d2abc929b6
4
- data.tar.gz: 61c024688e436092d09d4b77214603e1c374be69e6b4b289d0d2997f10218c24
3
+ metadata.gz: 141d4232e008441a87bf9379f2c05a22e745278dd20d319a9ce7e25782293487
4
+ data.tar.gz: 0572e1ed26fb7a1d2cdb63df84407ed845fa5f278610167972039344b0477976
5
5
  SHA512:
6
- metadata.gz: a030d17f52d1530a30e8ff892632a00ff8cd10489ded1a7268eca58898dfd73c627f56f38bd8382066c70d9e58400719e37f1485790fcea723d377080512712c
7
- data.tar.gz: 6a2bf596a09c5a7a255051f4c6945f2f325d6d0db03b0a9f537af50ade9b1b17356240628723401391b8eaa27ad66e2d9b7730c9c65b5a040049d19663b429b1
6
+ metadata.gz: b6b77d80551ca1accaef527fe55147efc97f2244503bfb5575092208c3234433e13013673f144802535e8f09c5ca7933f00ab0539d38503c587da5a7f495294c
7
+ data.tar.gz: b6954547ccff1ff8224dbf9841393f4c929c1938402a85fa57ddcfaac1c9f3bfd8145406011bcd17d20d6a9f8d097ef947bd59c6774673ec2bb1fa8590be9ec4
@@ -63,35 +63,35 @@ fn parse_names(json: &str) -> Option<MetricNames> {
63
63
  return None;
64
64
  }
65
65
 
66
- // Now: family_name","metric_name",[...
67
- let remainder = json.get(2..)?;
68
-
69
- let names_end = remainder.find('[')?;
70
-
71
- // Save the rest of the slice to parse for labels later.
72
- let label_json = remainder.get(names_end..)?;
73
-
74
- // Now: family_name","metric_name",
75
- let remainder = remainder.get(..names_end)?;
66
+ // Captured: "family_name","metric_name",
67
+ // ^^^^^^^^^^^
68
+ let (family_name, remainder) = scan_str(json.get(1..)?)?;
76
69
 
77
- // Split on commas into:
78
- // family_name","metric_name",
79
- // ^^^^one^^^^^ ^^^^^two^^^^^
80
- let mut token_iter = remainder.split(',');
70
+ // Validate comma separated names
71
+ // ["family_name","metric_name",[...
72
+ // ^
73
+ if !remainder.starts_with("\",") {
74
+ return None;
75
+ }
81
76
 
82
- // Captured: family_name","metric_name",
83
- // ^^^^^^^^^^^
84
- let family_name = token_iter.next()?.trim_end_matches('"');
77
+ // Now: "metric_name",[...
78
+ let remainder = remainder.get(2..)?;
85
79
 
86
80
  // Captured: "family_name","metric_name",
87
81
  // ^^^^^^^^^^^
88
- let metric_name = token_iter.next()?.trim_matches('"');
82
+ let (metric_name, remainder) = scan_str(remainder)?;
89
83
 
90
- // Confirm the final entry of the iter is empty, the the trailing ','.
91
- if !token_iter.next()?.is_empty() {
84
+ // Validate comma separated names
85
+ // ["family_name","metric_name",[...
86
+ // ^^
87
+ if !remainder.starts_with("\",") {
92
88
  return None;
93
89
  }
94
90
 
91
+ // Save the rest of the slice to parse for labels later.
92
+ // Now: [...
93
+ let label_json = remainder.get(2..)?;
94
+
95
95
  Some(MetricNames {
96
96
  label_json,
97
97
  family_name,
@@ -101,76 +101,143 @@ fn parse_names(json: &str) -> Option<MetricNames> {
101
101
 
102
102
  fn parse_label_values(json: &str) -> Option<MetricLabelVals> {
103
103
  // Starting with: ["label_a","label_b"],["value_a", "value_b"]]
104
- if !(json.starts_with('[') && json.ends_with("]]")) {
104
+ if !json.starts_with('[') {
105
105
  return None;
106
106
  }
107
107
 
108
+ // Now: "label_a","label_b"],["value_a", "value_b"]]
109
+ let mut remainder = json.get(1..)?;
110
+
108
111
  // Validate we either have the start of a label string or an
109
112
  // empty array, e.g. `["` or `[]`.
110
113
  if !matches!(json.as_bytes().get(1)?, b'"' | b']') {
111
114
  return None;
112
115
  }
113
116
 
114
- // Now: "label_a","label_b"
115
- let labels_end = json.find(']')?;
116
- let label_range = json.get(1..labels_end)?;
117
-
118
117
  let mut labels = SmallVec::new();
119
118
 
120
119
  // Split on commas into:
121
120
  // "label_a","label_b"
122
121
  // ^^^one^^^ ^^^two^^^
123
- for label in label_range.split(',') {
124
- // Captured: "label_a","label_b"
125
- // ^^^^^^^
126
- // If there are no labels, e.g. `[][]`, then don't capture anything.
127
- if !label.is_empty() {
128
- labels.push(label.trim_matches('"'));
129
- }
122
+ loop {
123
+ let Some((label, label_rem)) = scan_str(remainder) else {
124
+ // No further keys.
125
+ break;
126
+ };
127
+ labels.push(label);
128
+
129
+ // Advance past trailing quote.
130
+ remainder = label_rem.get(1..)?;
131
+ match remainder.as_bytes().first() {
132
+ Some(b']') => break, // No further labels.
133
+ Some(b',') => {} // More labels expected.
134
+ _ => return None, // Invalid.
135
+ };
136
+
137
+ // Advance past comma.
138
+ remainder = remainder.get(1..)?;
130
139
  }
131
140
 
132
- // Now: ],["value_a", "value_b"]]
133
- let mut values_range = json.get(labels_end..)?;
134
-
135
- // Validate we have a separating comma with one and only one leading bracket.
136
- if !(values_range.starts_with("],[") && values_range.as_bytes().get(3)? != &b'[') {
141
+ if !remainder.starts_with("],[") {
137
142
  return None;
138
143
  }
139
-
140
144
  // Now: "value_a", "value_b"]]
141
- values_range = values_range.get(3..)?;
142
-
143
- let values_end = values_range.find(']')?;
145
+ remainder = remainder.get(3..)?;
144
146
 
145
- // Validate we have only two trailing brackets.
146
- if values_range.get(values_end..)?.len() > 2 {
147
+ // Validate we don't have extra brackets.
148
+ if remainder.starts_with('[') {
147
149
  return None;
148
150
  }
149
151
 
150
- // Now: "value_a", "value_b"
151
- values_range = values_range.get(..values_end)?;
152
-
153
152
  let mut values = SmallVec::new();
154
-
155
153
  // Split on commas into:
156
154
  // "value_a","value_b"
157
155
  // ^^^one^^^ ^^^two^^^
158
- for value in values_range.split(',') {
159
- // Captured: "value_a","value_b"
160
- // ^^^^^^^^^
161
- // If there are no values, e.g. `[][]`, then don't capture anything.
162
- if !value.is_empty() {
163
- values.push(value.trim_matches('"'));
156
+ loop {
157
+ if remainder.starts_with('"') {
158
+ let (value, value_rem) = scan_str(remainder)?;
159
+ values.push(value);
160
+
161
+ // Advance past trailing quote.
162
+ remainder = value_rem.get(1..)?;
163
+ } else {
164
+ // An unquoted value, such as `true` or `404`.
165
+ let i = remainder.find(|c| c == ',' || c == ']')?;
166
+ let value = &remainder[..i];
167
+
168
+ match (value.is_empty(), is_valid_json_literal(value)) {
169
+ (true, _) => {} // Empty string, do nothing.
170
+ (false, true) => values.push(value), // Valid literal.
171
+ (false, false) => return None, // Invalid literal, fail.
172
+ }
173
+
174
+ remainder = &remainder[i..];
164
175
  }
176
+
177
+ match remainder.as_bytes().first() {
178
+ Some(b']') => break, // End of values.
179
+ Some(b',') => {} // More values expected.
180
+ _ => return None, // Invalid.
181
+ };
182
+
183
+ // Advance past comma.
184
+ remainder = remainder.get(1..)?;
165
185
  }
166
186
 
167
187
  if values.len() != labels.len() {
168
188
  return None;
169
189
  }
170
190
 
191
+ // Now: ]]
192
+ if remainder != "]]" {
193
+ return None;
194
+ }
195
+
171
196
  Some(MetricLabelVals { labels, values })
172
197
  }
173
198
 
199
+ // Read a JSON-encoded str that includes starting and ending double quotes.
200
+ // Returns the validated str with the double quotes trimmed and the remainder
201
+ // of the input str.
202
+ fn scan_str(json: &str) -> Option<(&str, &str)> {
203
+ let mut escaping = false;
204
+
205
+ if !json.starts_with('"') {
206
+ return None;
207
+ }
208
+
209
+ // Trim leading double quote.
210
+ let json = json.get(1..)?;
211
+
212
+ for (i, &c) in json.as_bytes().iter().enumerate() {
213
+ if c == b'\\' {
214
+ escaping = true;
215
+ continue;
216
+ }
217
+
218
+ if c == b'"' && !escaping {
219
+ return Some((json.get(..i)?, json.get(i..)?));
220
+ }
221
+
222
+ escaping = false;
223
+ }
224
+
225
+ None
226
+ }
227
+
228
+ // Confirm an unquoted JSON literal is a boolean, null, or has a passing
229
+ // resemblance to a number. We do not confirm numeric formatting, only
230
+ // that all characters are valid. See https://www.json.org/json-en.html
231
+ // for details.
232
+ fn is_valid_json_literal(s: &str) -> bool {
233
+ match s {
234
+ "true" | "false" | "null" => true,
235
+ _ => s.chars().all(|c| {
236
+ c.is_ascii_digit() || c == '.' || c == '+' || c == '-' || c == 'e' || c == 'E'
237
+ }),
238
+ }
239
+ }
240
+
174
241
  #[cfg(test)]
175
242
  mod test {
176
243
  use smallvec::smallvec;
@@ -217,6 +284,16 @@ mod test {
217
284
  values: smallvec!["value_a", "403"],
218
285
  }),
219
286
  },
287
+ TestCase {
288
+ name: "scientific notation literal",
289
+ input: r#"["metric","name",["label_a"],[-2.0e-5]]"#,
290
+ expected: Some(MetricText {
291
+ family_name: "metric",
292
+ metric_name: "name",
293
+ labels: smallvec!["label_a"],
294
+ values: smallvec!["-2.0e-5"],
295
+ }),
296
+ },
220
297
  TestCase {
221
298
  name: "null value",
222
299
  input: r#"["metric","name",["label_a","label_b"],[null,"value_b"]]"#,
@@ -237,6 +314,36 @@ mod test {
237
314
  values: smallvec![],
238
315
  }),
239
316
  },
317
+ TestCase {
318
+ name: "comma in items",
319
+ input: r#"["met,ric","na,me",["label,_a","label_b"],["value,_a","value_b"]]"#,
320
+ expected: Some(MetricText {
321
+ family_name: "met,ric",
322
+ metric_name: "na,me",
323
+ labels: smallvec!["label,_a", "label_b"],
324
+ values: smallvec!["value,_a", "value_b"],
325
+ }),
326
+ },
327
+ TestCase {
328
+ name: "bracket in value",
329
+ input: r#"["met[r]ic","na[m]e",["label[_]a","label_b"],["value_a","val[ue]_b"]]"#,
330
+ expected: Some(MetricText {
331
+ family_name: "met[r]ic",
332
+ metric_name: "na[m]e",
333
+ labels: smallvec!["label[_]a", "label_b"],
334
+ values: smallvec!["value_a", "val[ue]_b"],
335
+ }),
336
+ },
337
+ TestCase {
338
+ name: "escaped quotes",
339
+ input: r#"["met\"ric","na\"me",["label\"_a","label_b"],["value\"_a","value_b"]]"#,
340
+ expected: Some(MetricText {
341
+ family_name: r#"met\"ric"#,
342
+ metric_name: r#"na\"me"#,
343
+ labels: smallvec![r#"label\"_a"#, "label_b"],
344
+ values: smallvec![r#"value\"_a"#, "value_b"],
345
+ }),
346
+ },
240
347
  ];
241
348
 
242
349
  for case in tc {
@@ -313,23 +420,18 @@ mod test {
313
420
  expected: None,
314
421
  },
315
422
  TestCase {
316
- name: "comma in family name",
317
- input: r#"["met,ric","name",["label_a","label_b"],["value_a","value_b"]]"#,
318
- expected: None,
319
- },
320
- TestCase {
321
- name: "comma in metric name",
322
- input: r#"["metric","na,me",["label_a","label_b"],["value_a","value_b"]]"#,
423
+ name: "misplaced bracket",
424
+ input: r#"["metric","name",["label_a","label_b"],]["value_a","value_b"]]"#,
323
425
  expected: None,
324
426
  },
325
427
  TestCase {
326
- name: "comma in value",
327
- input: r#"["metric","na,me",["label_a","label_b"],["val,ue_a","value_b"]]"#,
428
+ name: "comma in numeric value",
429
+ input: r#"["metric","name",["label_a","label_b"],[400,0,"value_b"]]"#,
328
430
  expected: None,
329
431
  },
330
432
  TestCase {
331
- name: "comma in numeric value",
332
- input: r#"["metric","name",["label_a","label_b"],[400,0,"value_b"]]"#,
433
+ name: "non-e letter in numeric value",
434
+ input: r#"["metric","name",["label_a","label_b"],[400x0,"value_b"]]"#,
333
435
  expected: None,
334
436
  },
335
437
  ];
Binary file
Binary file
Binary file
Binary file
@@ -1,5 +1,5 @@
1
1
  module Prometheus
2
2
  module Client
3
- VERSION = '0.24.3'.freeze
3
+ VERSION = '0.24.4'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prometheus-client-mmap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.24.3
4
+ version: 0.24.4
5
5
  platform: x86_64-linux
6
6
  authors:
7
7
  - Tobias Schmidt
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2023-05-20 00:00:00.000000000 Z
14
+ date: 2023-06-12 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rb_sys