Skip to content

Commit e580b43

Browse files
duncanistaclaude
andauthored
fix(dogstatsd): handle non-UTF-8 packets gracefully instead of panicking (#79)
* fix(dogstatsd): handle non-UTF-8 packets gracefully instead of panicking process_packet() used expect() on std::str::from_utf8(), which would panic if a client sent malformed (non-UTF-8) bytes over UDP. This replaces the panic with an error log and early return, dropping the invalid packet safely. Adds a unit test to verify the behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fmt --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 2eb009a commit e580b43

File tree

1 file changed

+36
-2
lines changed

1 file changed

+36
-2
lines changed

crates/dogstatsd/src/dogstatsd.rs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -494,8 +494,18 @@ fn process_packet(
494494

495495
trace!("Received packet: {} bytes from {}", buf.len(), src);
496496

497-
#[allow(clippy::expect_used)]
498-
let msgs = std::str::from_utf8(buf).expect("couldn't parse as string");
497+
let msgs = match std::str::from_utf8(buf) {
498+
Ok(s) => s,
499+
Err(e) => {
500+
error!(
501+
"Received non-UTF-8 packet ({} bytes) from {}, dropping: {}",
502+
buf.len(),
503+
src,
504+
e
505+
);
506+
return;
507+
}
508+
};
499509
trace!("Received message: {} from {}", msgs, src);
500510
let statsd_metric_strings: Vec<&str> = msgs.split('\n').collect();
501511
let metric_count_in_packet = statsd_metric_strings
@@ -940,4 +950,28 @@ single_machine_performance.rouster.metrics_max_timestamp_latency:1376.90870216|d
940950

941951
response
942952
}
953+
954+
#[tokio::test]
955+
#[traced_test]
956+
async fn test_dogstatsd_non_utf8_packet_does_not_panic() {
957+
let (service, handle) =
958+
AggregatorService::new(EMPTY_TAGS, 1_024).expect("aggregator service creation failed");
959+
let service_task = tokio::spawn(service.run());
960+
961+
// 0xFF 0xFE are invalid UTF-8 bytes
962+
let invalid_bytes: &[u8] = &[0xFF, 0xFE, b':', b'1', b'|', b'c'];
963+
let src =
964+
MessageSource::Network(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0));
965+
process_packet(invalid_bytes, &src, &handle, None);
966+
967+
assert!(logs_contain("Received non-UTF-8 packet"));
968+
969+
// No metrics should have been inserted
970+
let response = handle.flush().await.expect("Failed to flush");
971+
assert_eq!(response.series.len(), 0);
972+
assert_eq!(response.distributions.len(), 0);
973+
974+
handle.shutdown().expect("Failed to shutdown");
975+
service_task.await.expect("Service task failed");
976+
}
943977
}

0 commit comments

Comments
 (0)