sozu-lib 1.1.1

sozu library to build hot reconfigurable HTTP reverse proxies
Documentation
use std::fmt::Write;

use kawa::{ParsingErrorKind, ParsingPhase, ParsingPhaseMarker};

use super::GenericHttpStream;

#[cfg(feature = "tolerant-http1-parser")]
const CHARSET: &str = "all characters are LATIN-1 (no UTF-8 allowed)";
#[cfg(not(feature = "tolerant-http1-parser"))]
const CHARSET: &str = "all characters are UASCII (no UTF-8 allowed)";

fn hex_dump(buffer: &[u8], window: usize, start: usize, end: usize) -> String {
    let mut result = String::with_capacity(window * 3 + 10);
    result.push('\"');
    if end - start <= window {
        let slice = &buffer[start..end];
        for (i, c) in slice.iter().enumerate() {
            let _ = write!(result, "{c:02x}");
            if i < slice.len() - 1 {
                result.push(' ');
            }
        }
    } else {
        let half = window / 2;
        let slice1 = &buffer[start..start + half - 1];
        let slice2 = &buffer[end - half + 1..end];
        for c in slice1 {
            let _ = write!(result, "{c:02x} ");
        }
        result.push_str("");
        for (i, c) in slice2.iter().enumerate() {
            let _ = write!(result, "{c:02x}");
            if i < slice2.len() - 1 {
                result.push(' ');
            }
        }
    }
    result.push('\"');
    result
}

pub fn diagnostic_400_502(
    marker: ParsingPhaseMarker,
    kind: ParsingErrorKind,
    kawa: &GenericHttpStream,
) -> (String, String, String, String) {
    match kind {
        ParsingErrorKind::Consuming { index } => {
            let message = match marker {
                ParsingPhaseMarker::StatusLine => {
                    format!(
                        "The status line is invalid. Make sure it is well formated and {CHARSET}."
                    )
                }
                ParsingPhaseMarker::Headers | ParsingPhaseMarker::Trailers => {
                    let marker = if marker == ParsingPhaseMarker::Headers {
                        "header"
                    } else {
                        "trailer"
                    };
                    format!("A {marker} is invalid, make sure {CHARSET}.")
                }
                ParsingPhaseMarker::Cookies => {
                    format!("A cookie is invalid, make sure {CHARSET}.")
                }
                ParsingPhaseMarker::Body
                | ParsingPhaseMarker::Chunks
                | ParsingPhaseMarker::Terminated
                | ParsingPhaseMarker::Error => "The parser stopped in an unexpected phase.".into(),
            };
            let buffer = kawa.storage.buffer();
            let successfully_parsed = hex_dump(buffer, 32, kawa.storage.start, kawa.storage.head);
            let partially_parsed = hex_dump(buffer, 32, kawa.storage.head, index as usize);
            let invalid = hex_dump(buffer, 32, index as usize, kawa.storage.end);
            (message, successfully_parsed, partially_parsed, invalid)
        }
        ParsingErrorKind::Processing { message } => (
            format!(
                "The request is correctly structured but presents inconsistent or invalid values: {message}."
            ),
            "null".into(),
            "null".into(),
            "null".into(),
        ),
    }
}

pub fn diagnostic_413_507(parsing_phase: ParsingPhase) -> String {
    match parsing_phase {
        kawa::ParsingPhase::StatusLine => {
            "Status line is too long. Note that an URL should not exceed 2083 characters.".into()
        }
        kawa::ParsingPhase::Headers | kawa::ParsingPhase::Cookies { .. } => {
            "Headers are too long. All headers should fit in a single buffer.".into()
        }
        _ => "The parser stopped in an unexpected phase.".into(),
    }
}