sozu_lib/protocol/kawa_h1/
diagnostics.rs

1use std::fmt::Write;
2
3use kawa::{ParsingErrorKind, ParsingPhase, ParsingPhaseMarker};
4
5use super::GenericHttpStream;
6
7#[cfg(feature = "tolerant-http1-parser")]
8const CHARSET: &str = "all characters are LATIN-1 (no UTF-8 allowed)";
9#[cfg(not(feature = "tolerant-http1-parser"))]
10const CHARSET: &str = "all characters are UASCII (no UTF-8 allowed)";
11
12fn hex_dump(buffer: &[u8], window: usize, start: usize, end: usize) -> String {
13    let mut result = String::with_capacity(window * 3 + 10);
14    result.push('\"');
15    if end - start <= window {
16        let slice = &buffer[start..end];
17        for (i, c) in slice.iter().enumerate() {
18            let _ = write!(result, "{c:02x}");
19            if i < slice.len() - 1 {
20                result.push(' ');
21            }
22        }
23    } else {
24        let half = window / 2;
25        let slice1 = &buffer[start..start + half - 1];
26        let slice2 = &buffer[end - half + 1..end];
27        for c in slice1 {
28            let _ = write!(result, "{c:02x} ");
29        }
30        result.push_str("… ");
31        for (i, c) in slice2.iter().enumerate() {
32            let _ = write!(result, "{c:02x}");
33            if i < slice2.len() - 1 {
34                result.push(' ');
35            }
36        }
37    }
38    result.push('\"');
39    result
40}
41
42pub fn diagnostic_400_502(
43    marker: ParsingPhaseMarker,
44    kind: ParsingErrorKind,
45    kawa: &GenericHttpStream,
46) -> (String, String, String, String) {
47    match kind {
48        ParsingErrorKind::Consuming { index } => {
49            let message = match marker {
50                ParsingPhaseMarker::StatusLine => {
51                    format!(
52                        "The status line is invalid. Make sure it is well formated and {CHARSET}."
53                    )
54                }
55                ParsingPhaseMarker::Headers | ParsingPhaseMarker::Trailers => {
56                    let marker = if marker == ParsingPhaseMarker::Headers {
57                        "header"
58                    } else {
59                        "trailer"
60                    };
61                    format!("A {marker} is invalid, make sure {CHARSET}.")
62                }
63                ParsingPhaseMarker::Cookies => {
64                    format!("A cookie is invalid, make sure {CHARSET}.")
65                }
66                ParsingPhaseMarker::Body
67                | ParsingPhaseMarker::Chunks
68                | ParsingPhaseMarker::Terminated
69                | ParsingPhaseMarker::Error => {
70                    "The parser stopped in an unexpected phase.".into()
71                }
72            };
73            let buffer = kawa.storage.buffer();
74            let successfully_parsed = hex_dump(buffer, 32, kawa.storage.start, kawa.storage.head);
75            let partially_parsed = hex_dump(buffer, 32, kawa.storage.head, index as usize);
76            let invalid = hex_dump(buffer, 32, index as usize, kawa.storage.end);
77            (message, successfully_parsed, partially_parsed, invalid)
78        }
79        ParsingErrorKind::Processing { message } => (
80            format!("The request is correctly structured but presents inconsistent or invalid values: {message}."),
81            "null".into(),
82            "null".into(),
83            "null".into(),
84        ),
85    }
86}
87
88pub fn diagnostic_413_507(parsing_phase: ParsingPhase) -> String {
89    match parsing_phase {
90        kawa::ParsingPhase::StatusLine => {
91            "Status line is too long. Note that an URL should not exceed 2083 characters.".into()
92        }
93        kawa::ParsingPhase::Headers | kawa::ParsingPhase::Cookies { .. } => {
94            "Headers are too long. All headers should fit in a single buffer.".into()
95        }
96        _ => "The parser stopped in an unexpected phase.".into(),
97    }
98}