Skip to content

Commit 6a493b8

Browse files
committed
simplify uri path parsing
Signed-off-by: Dan Bond <[email protected]>
1 parent ea4056d commit 6a493b8

File tree

4 files changed

+34
-32
lines changed

4 files changed

+34
-32
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "metrics_server"
3-
version = "0.14.0"
3+
version = "0.15.0"
44
authors = ["Dan Bond <[email protected]>"]
55
edition = "2021"
66
rust-version = "1.63"

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ This crate provides a thread safe, minimalstic HTTP/S server used to buffer metr
1616
Include the lib in your `Cargo.toml` dependencies:
1717
```toml
1818
[dependencies]
19-
metrics_server = "0.14"
19+
metrics_server = "0.15"
2020
```
2121

2222
To enable TLS support, pass the optional feature flag:
2323
```toml
2424
[dependencies]
25-
metrics_server = { version = "0.14", features = ["tls"] }
25+
metrics_server = { version = "0.15", features = ["tls"] }
2626
```
2727

2828
### HTTP

src/server.rs

+29-27
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
44
use std::sync::{Arc, Mutex};
55
use std::thread;
66

7-
use http::Uri;
7+
use http::uri::PathAndQuery;
88
use log::{debug, error};
99
use time::{format_description, OffsetDateTime};
1010
use tiny_http::{ConfigListenAddr, Method, Response, Server};
@@ -127,16 +127,17 @@ impl MetricsServer {
127127
///
128128
/// The server will only respond synchronously as it blocks until receiving new requests.
129129
/// Suqsequent calls to this method will return a no-op and not affect the underlying server.
130-
pub fn serve_uri(&mut self, uri: String) {
130+
pub fn serve_uri(&mut self, path: String) {
131131
// Check if we already have a thread running.
132132
if let Some(thread) = &self.thread {
133133
if !thread.is_finished() {
134+
debug!("metrics server already running, continuing");
134135
return;
135136
}
136137
}
137138

138-
// Ensure URI is valid.
139-
let u = parse_uri(uri);
139+
// Ensure path is valid.
140+
let path = parse_path(&path);
140141

141142
// Invoking clone on Arc produces a new Arc instance, which points to the
142143
// same allocation on the heap as the source Arc, while increasing a reference count.
@@ -149,11 +150,12 @@ impl MetricsServer {
149150
for req in s.server.incoming_requests() {
150151
// Check to see if we should stop handling requests.
151152
if s.stop.load(Ordering::Relaxed) {
152-
break;
153+
debug!("metrics server stopping");
154+
return;
153155
}
154156

155-
// Only serve the specified uri path.
156-
if req.url().to_lowercase() != u {
157+
// Only serve the specified URI path.
158+
if req.url() != path {
157159
let res = Response::empty(404);
158160
respond(req, res);
159161
continue;
@@ -198,16 +200,18 @@ impl MetricsServer {
198200
}
199201
}
200202

201-
// Validate the provided URI or return the default /metrics on error.
202-
fn parse_uri(mut uri: String) -> String {
203-
if !uri.starts_with('/') {
204-
uri.insert(0, '/');
205-
}
206-
207-
match Uri::from_str(&uri) {
208-
Ok(u) => u.path().to_lowercase(),
203+
// Validate the provided URL path, or return the default path on error.
204+
fn parse_path(uri: &str) -> String {
205+
match PathAndQuery::from_str(uri) {
206+
Ok(pq) => {
207+
let mut path = pq.path().to_lowercase();
208+
if !path.starts_with('/') {
209+
path.insert(0, '/');
210+
}
211+
path
212+
}
209213
Err(_) => {
210-
error!("invalid uri, defaulting to {}", DEFAULT_METRICS_PATH);
214+
error!("invalid uri, defaulting to {DEFAULT_METRICS_PATH}");
211215
DEFAULT_METRICS_PATH.to_string()
212216
}
213217
}
@@ -242,21 +246,19 @@ mod tests {
242246
use super::*;
243247

244248
#[test]
245-
fn test_parse_uri() {
249+
fn test_parse_path() {
246250
let expected_default = DEFAULT_METRICS_PATH.to_string();
247-
let expected_valid = "/v1/metrics".to_string();
251+
let expected_valid = "/debug/metrics".to_string();
248252

249253
// Invalid.
250-
assert_eq!(parse_uri("Hello, World!".to_string()), expected_default);
251-
// No slash prefix.
252-
assert_eq!(parse_uri("metrics".to_string()), expected_default);
253-
// Leading slash prefix.
254-
assert_eq!(parse_uri("/metrics".to_string()), expected_default);
254+
assert_eq!(parse_path("Hello, World!"), expected_default);
255255
// Whitespace.
256-
assert_eq!(parse_uri(" metr ics ".to_string()), expected_default);
257-
// Uppercase.
258-
assert_eq!(parse_uri("METRICS".to_string()), expected_default);
256+
assert_eq!(parse_path(" metr ics "), expected_default);
257+
// Non-ASCII.
258+
assert_eq!(parse_path("mëtrîcs"), expected_default);
259259
// Valid.
260-
assert_eq!(parse_uri("/v1/metrics".to_string()), expected_valid);
260+
assert_eq!(parse_path("/debug/metrics"), expected_valid);
261+
assert_eq!(parse_path("debug/metrics"), expected_valid);
262+
assert_eq!(parse_path("DEBUG/METRICS"), expected_valid);
261263
}
262264
}

tests/server.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ fn test_http_server_serve() {
8686
let res = reqwest::blocking::get("http://localhost:8001/metricsssss").unwrap();
8787
assert_eq!(404, res.status());
8888

89-
// Assert calls to uppercase URLs returns 200.
89+
// Assert calls to uppercase URLs returns 404.
9090
let res = reqwest::blocking::get("http://localhost:8001/METRICS").unwrap();
91-
assert_eq!(200, res.status());
91+
assert_eq!(404, res.status());
9292

9393
// Assert non GET requests to /metrics endpoint returns 405.
9494
let client = reqwest::blocking::Client::new();

0 commit comments

Comments
 (0)