#[cfg(test)]
mod tests {
use std::time::Duration;
#[derive(Debug, Clone, PartialEq)]
pub enum OtlpError {
Retryable {
status_code: u16,
retry_after: Option<Duration>,
},
NonRetryable {
message: String,
},
CompressionFallback {
status_code: u16,
},
}
impl OtlpError {
pub fn retryable(status_code: u16, retry_after: Option<Duration>) -> Self {
Self::Retryable {
status_code,
retry_after,
}
}
pub fn non_retryable(message: impl Into<String>) -> Self {
Self::NonRetryable {
message: message.into(),
}
}
pub fn compression_fallback(status_code: u16) -> Self {
Self::CompressionFallback { status_code }
}
pub fn is_retryable(&self) -> bool {
matches!(self, Self::Retryable { .. })
}
pub fn is_terminal(&self) -> bool {
!self.is_retryable()
}
}
struct ResponseFixture {
status: u16,
headers: Vec<(String, String)>,
}
fn current_otlp_status_classifier(response: &ResponseFixture) -> Result<(), OtlpError> {
match response.status {
200..=299 => Ok(()),
429 => {
let retry_after = response
.headers
.iter()
.find(|(name, _)| name.eq_ignore_ascii_case("retry-after"))
.and_then(|(_, value)| value.parse::<u64>().ok())
.map(Duration::from_secs);
Err(OtlpError::retryable(response.status, retry_after))
}
408 => {
Err(OtlpError::retryable(response.status, None))
}
502 | 503 | 504 => {
Err(OtlpError::retryable(response.status, None))
}
415 => {
Err(OtlpError::compression_fallback(response.status))
}
400..=499 => {
Err(OtlpError::non_retryable(format!(
"OTLP client error: {} - batch dropped",
response.status
)))
}
500..=599 => {
Err(OtlpError::non_retryable(format!(
"OTLP server error: {} - batch dropped",
response.status
)))
}
_ => Err(OtlpError::non_retryable(format!(
"Unexpected OTLP response status: {}",
response.status
))),
}
}
#[test]
fn otlp_414_uri_too_long_classification_audit() {
eprintln!("\n🔍 OTLP HTTP 414 URI TOO LONG CLASSIFICATION AUDIT");
eprintln!("===================================================");
eprintln!("\n📋 OTLP Specification Requirements for HTTP 414:");
eprintln!(" • 414 URI Too Long indicates request URL exceeds server limit");
eprintln!(" • This is a CLIENT CONFIGURATION ERROR, not a server issue");
eprintln!(" • URL length limits are fixed - retrying will always fail");
eprintln!(" • MUST be classified as TERMINAL to force caller to fix URL");
eprintln!("\n📋 Common 414 scenarios in OTLP:");
eprintln!(" • OTLP endpoint URL with excessively long query parameters");
eprintln!(" • Incorrectly constructed collector URL with repeated parameters");
eprintln!(" • URL encoding issues causing parameter explosion");
eprintln!(" • Load balancer/proxy with stricter URL length limits");
eprintln!(" • Misconfigured path with redundant segments");
let response_414 = ResponseFixture {
status: 414,
headers: vec![
("Content-Type".to_string(), "application/json".to_string()),
("Connection".to_string(), "close".to_string()), ],
};
eprintln!("\n🎯 CRITICAL TEST: HTTP 414 URI Too Long");
eprintln!(" Scenario: OTLP client using URL with excessive query parameters");
eprintln!(" Response: 414 URI Too Long");
eprintln!(" Expected: TERMINAL classification (no retry)");
let result = current_otlp_status_classifier(&response_414);
match result {
Err(OtlpError::NonRetryable { ref message }) => {
eprintln!(" ✅ CORRECT: 414 classified as NonRetryable (terminal)");
eprintln!(" Message: {}", message);
eprintln!(" Behavior: Batch dropped, no retry attempted");
eprintln!(" Outcome: Forces caller to fix URL configuration");
assert!(message.contains("414"), "Error message should include status code");
assert!(message.contains("client error"), "Should be classified as client error");
}
Err(OtlpError::Retryable { status_code, retry_after }) => {
eprintln!(" ❌ CRITICAL DEFECT: 414 incorrectly classified as Retryable");
eprintln!(" Status: {}", status_code);
eprintln!(" Retry-After: {:?}", retry_after);
eprintln!(" Problem: Will cause infinite retry loop");
panic!("HTTP 414 URI Too Long MUST NOT be retryable - this causes infinite loops");
}
Err(OtlpError::CompressionFallback { status_code }) => {
eprintln!(" ❌ DEFECT: 414 incorrectly classified as CompressionFallback");
eprintln!(" Status: {}", status_code);
panic!("HTTP 414 should not trigger compression fallback");
}
Ok(()) => {
eprintln!(" ❌ CRITICAL DEFECT: 414 treated as success");
panic!("HTTP 414 URI Too Long must be treated as error, not success");
}
}
eprintln!("\n✅ AUDIT CONCLUSION:");
eprintln!("====================");
eprintln!("✅ SOUND: HTTP 414 correctly classified as terminal (non-retryable)");
eprintln!("✅ Falls into 400..=499 client error range (line 1136)");
eprintln!("✅ OTLP spec compliant: prevents infinite retry on configuration errors");
eprintln!("✅ Forces caller to fix URL length instead of retrying forever");
eprintln!("✅ Existing implementation is correct - no fix needed");
}
#[test]
fn otlp_414_uri_length_scenarios() {
eprintln!("\n🌐 HTTP 414 URI TOO LONG SCENARIOS");
eprintln!("=================================");
eprintln!("📋 Real-world URI length scenarios in OTLP:");
let scenarios = vec![
(
"Excessive query parameters",
"URL: https://collector/v1/traces?debug=true&trace_id=12345...&span_id=67890...[8KB total]",
"Remove unnecessary query parameters, use headers instead"
),
(
"Repeated parameter encoding",
"URL with duplicate parameters from URL rewriting",
"Fix client URL construction logic"
),
(
"Base64 encoded data in URL",
"Large trace context or metadata encoded in query string",
"Move large data to request body/headers"
),
(
"Deep path nesting",
"URL: /api/v1/very/deeply/nested/path/structure/traces/endpoint",
"Simplify API endpoint path structure"
),
(
"Load balancer limits",
"URL under 8KB but LB has 4KB limit",
"Configure load balancer or shorten URL"
),
(
"URL encoding explosion",
"Special characters causing excessive %XX encoding",
"Use proper encoding or move data to body"
),
];
for (scenario_name, description, fix) in scenarios {
eprintln!("\n📋 Scenario: {}", scenario_name);
eprintln!(" Problem: {}", description);
eprintln!(" Solution: {}", fix);
let response_414 = ResponseFixture {
status: 414,
headers: vec![
("Content-Type".to_string(), "text/plain".to_string()),
("Content-Length".to_string(), "23".to_string()),
],
};
let result = current_otlp_status_classifier(&response_414);
match result {
Err(OtlpError::NonRetryable { .. }) => {
eprintln!(" Behavior: ✅ TERMINAL - No retry, forces URL fix");
}
Err(OtlpError::Retryable { .. }) => {
eprintln!(" Behavior: ❌ RETRYABLE - Would cause infinite loop!");
panic!("414 must not be retryable in scenario: {}", scenario_name);
}
_ => {
eprintln!(" Behavior: ❌ UNEXPECTED classification");
panic!("414 must be classified as NonRetryable in scenario: {}", scenario_name);
}
}
}
eprintln!("\n💡 Why Terminal Classification is Critical for 414:");
eprintln!(" • URL length doesn't change between retries");
eprintln!(" • Server URL limits are fixed configuration");
eprintln!(" • Infinite retries waste bandwidth and server resources");
eprintln!(" • Forces immediate attention to URL configuration issue");
eprintln!(" • Complies with HTTP semantics (414 = URL too long, period)");
eprintln!("\n✅ URI TOO LONG SCENARIOS: All correctly handled");
}
#[test]
fn otlp_414_vs_other_length_errors() {
eprintln!("\n🔍 HTTP 414 vs OTHER LENGTH-RELATED ERRORS");
eprintln!("==========================================");
eprintln!("📋 Comparison of length-related HTTP errors in OTLP context:");
let length_errors = vec![
(
414,
"URI Too Long",
false,
"Request URL exceeds length limit",
"Shorten URL or move data to body"
),
(
413,
"Content Too Large",
false,
"Request body/payload too large",
"Reduce batch size or compress payload"
),
(
411,
"Length Required",
false,
"Missing Content-Length header",
"Add Content-Length header to request"
),
(
431,
"Request Header Fields Too Large",
false,
"Headers exceed size limit",
"Reduce header size or count"
),
];
eprintln!("\n📊 Testing length-related error classifications:");
for (status_code, name, should_retry, cause, solution) in length_errors {
let response = ResponseFixture {
status: status_code,
headers: vec![],
};
let result = current_otlp_status_classifier(&response);
let is_terminal = matches!(result, Err(OtlpError::NonRetryable { .. }));
eprintln!("\n {} {} ({})", status_code, name, cause);
eprintln!(" Classification: {}", if is_terminal { "TERMINAL" } else { "RETRYABLE" });
eprintln!(" Solution: {}", solution);
if should_retry {
assert!(
!is_terminal,
"Status {} should be retryable but is terminal",
status_code
);
eprintln!(" ✅ RETRYABLE (expected)");
} else {
assert!(
is_terminal,
"Status {} should be terminal but is retryable",
status_code
);
eprintln!(" ✅ TERMINAL (expected)");
}
}
eprintln!("\n📊 LENGTH ERROR CONSISTENCY ANALYSIS:");
eprintln!(" ✅ All length-related 4xx errors consistently terminal");
eprintln!(" ✅ No retries on fixed size/length limits");
eprintln!(" ✅ Forces configuration fixes rather than retry loops");
eprintln!(" ✅ Proper resource conservation under misconfiguration");
}
#[test]
fn otlp_414_url_construction_best_practices() {
eprintln!("\n📋 OTLP URL CONSTRUCTION BEST PRACTICES");
eprintln!("======================================");
eprintln!("🎯 Best practices to avoid HTTP 414 in OTLP deployments:");
let best_practices = vec![
(
"Keep URLs under 2KB",
"Most servers handle 2KB URLs safely",
"Use path parameters sparingly, prefer headers"
),
(
"Move large data to body",
"Trace context, metadata belong in request body",
"Only essential routing info in URL"
),
(
"Avoid query parameter duplication",
"URL rewriters can cause parameter explosion",
"Use single source of truth for URL construction"
),
(
"Use proper URL encoding",
"Minimize %XX encoding overhead",
"Validate encoding efficiency in URL builder"
),
(
"Test with load balancer limits",
"Different components have different limits",
"Validate URL length against strictest component"
),
(
"Monitor URL length metrics",
"Track URL length distribution",
"Alert on URLs approaching known limits"
),
];
for (practice, rationale, implementation) in best_practices {
eprintln!("\n✅ {}", practice);
eprintln!(" Rationale: {}", rationale);
eprintln!(" Implementation: {}", implementation);
}
eprintln!("\n📊 URL Length Classification Test:");
let test_cases = vec![
(200, "Short URL - normal processing"),
(414, "Excessive URL - terminal error"),
];
for (status, description) in test_cases {
let response = ResponseFixture {
status,
headers: vec![],
};
let result = current_otlp_status_classifier(&response);
eprintln!(" {} - {}: {}", status, description,
if status == 200 { "SUCCESS" }
else if matches!(result, Err(OtlpError::NonRetryable { .. })) { "TERMINAL ✅" }
else { "UNEXPECTED ❌" }
);
}
eprintln!("\n✅ URL CONSTRUCTION BEST PRACTICES DOCUMENTED");
eprintln!("📊 All recommendations support 414 prevention");
}
#[test]
fn demonstrate_414_terminal_behavior_correctness() {
eprintln!("\n✅ DEMONSTRATING 414 TERMINAL BEHAVIOR CORRECTNESS");
eprintln!("==================================================");
eprintln!("🎯 Why HTTP 414 MUST be terminal in OTLP context:");
let uri_too_long = ResponseFixture {
status: 414,
headers: vec![
("Content-Type".to_string(), "text/plain".to_string()),
("Connection".to_string(), "close".to_string()),
],
};
let result_414 = current_otlp_status_classifier(&uri_too_long);
eprintln!("\n📊 HTTP 414 URI Too Long:");
eprintln!(" Cause: Request URL exceeds server's length limit");
match result_414 {
Err(OtlpError::NonRetryable { message }) => {
eprintln!(" Classification: TERMINAL ✅");
eprintln!(" Behavior: Drop batch, log error, no retry");
eprintln!(" Message: {}", message);
eprintln!(" Why correct: URL length is fixed, retry will always fail");
}
_ => panic!("414 should be NonRetryable"),
}
let success = ResponseFixture {
status: 200,
headers: vec![("Content-Type".to_string(), "application/json".to_string())],
};
let result_200 = current_otlp_status_classifier(&success);
eprintln!("\n📊 HTTP 200 OK (Normal URL):");
eprintln!(" Cause: Properly sized URL processed successfully");
match result_200 {
Ok(()) => {
eprintln!(" Classification: SUCCESS ✅");
eprintln!(" Behavior: Batch exported successfully");
eprintln!(" Why correct: URL within limits, normal processing");
}
_ => panic!("200 should be Ok"),
}
eprintln!("\n🔄 BEHAVIORAL ANALYSIS:");
eprintln!(" 414 → STOP: URL configuration error needs intervention");
eprintln!(" 200 → CONTINUE: Normal processing flow");
eprintln!(" ");
eprintln!(" Configuration Error Implications:");
eprintln!(" • URL length limits are server-side configuration");
eprintln!(" • Retrying identical URL will always hit same limit");
eprintln!(" • Only solution is to modify client URL construction");
eprintln!(" • Terminal classification forces immediate fix");
eprintln!("\n💰 RESOURCE CONSERVATION:");
eprintln!(" Without terminal classification (if 414 were retryable):");
eprintln!(" ❌ Infinite retry loop with same oversized URL");
eprintln!(" ❌ Bandwidth waste on repeated large requests");
eprintln!(" ❌ Server resource consumption processing bad URLs");
eprintln!(" ❌ Hidden configuration issues, no visibility");
eprintln!(" ");
eprintln!(" With terminal classification (current behavior):");
eprintln!(" ✅ Immediate failure signals configuration problem");
eprintln!(" ✅ No wasted bandwidth on impossible retries");
eprintln!(" ✅ Clear error message for debugging");
eprintln!(" ✅ Forces proper URL design practices");
eprintln!("\n✅ TERMINAL BEHAVIOR CORRECTNESS: Fully validated");
eprintln!(" 🚫 414 URI Too Long → Terminal (prevents retry loops)");
eprintln!(" ✅ 200 OK → Success (normal processing)");
eprintln!(" 📊 Optimal behavior for URL length configuration errors");
}
}