Skip to main content

gproxy_protocol/connection/
body.rs

1use std::{fmt, pin::Pin};
2
3use futures_core::Stream;
4
5use super::{Bytes, HeaderMap, TransportError};
6
7/// Raw HTTP body chunks. A transport chunk need not align with a JSON value,
8/// UTF-8 character, SSE event, or multipart boundary.
9#[cfg(not(target_arch = "wasm32"))]
10pub type ByteStream = Pin<Box<dyn Stream<Item = Result<Bytes, TransportError>> + Send + 'static>>;
11
12#[cfg(target_arch = "wasm32")]
13pub type ByteStream = Pin<Box<dyn Stream<Item = Result<Bytes, TransportError>> + 'static>>;
14
15/// An encoded HTTP body. Content type and encoding belong to the HTTP headers.
16/// Both JSON and multipart can arrive buffered or streamed.
17pub enum HttpBody {
18    Bytes(Bytes),
19    Stream(ByteStream),
20}
21
22impl fmt::Debug for HttpBody {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        match self {
25            Self::Bytes(bytes) => f.debug_struct("Bytes").field("len", &bytes.len()).finish(),
26            Self::Stream(_) => f.write_str("Stream(..)"),
27        }
28    }
29}
30
31/// How records are delimited inside an HTTP stream, independently of the
32/// transport's chunk boundaries and the vendor's JSON schema.
33///
34/// This is format metadata, not a decoder. SSE fields remain part of the SSE
35/// encoding until a decoder handles them; a byte chunk is never assumed to be
36/// a complete event or JSON value.
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum StreamFraming {
39    Sse,
40    /// A single JSON array delivered incrementally, including `[` and `]`.
41    JsonArray,
42    /// Separate JSON values delimited by newlines (NDJSON).
43    NdJson,
44}
45
46/// Parsed multipart parts, delivered in their original order. Repeated field
47/// names are retained rather than collapsed into a map.
48#[cfg(not(target_arch = "wasm32"))]
49pub type PartStream =
50    Pin<Box<dyn Stream<Item = Result<MultipartPart, TransportError>> + Send + 'static>>;
51
52#[cfg(target_arch = "wasm32")]
53pub type PartStream = Pin<Box<dyn Stream<Item = Result<MultipartPart, TransportError>> + 'static>>;
54
55/// A decoded multipart body. Each part's headers retain its content disposition
56/// (including field name and filename), content type, and extension headers.
57///
58/// MIME boundary handling belongs to the codec and the outer Content-Type
59/// header. This view models parts, not boundary bytes. The parser adapter owns
60/// coordination between the part stream and each part's content stream.
61pub struct Multipart {
62    pub parts: PartStream,
63}
64
65impl fmt::Debug for Multipart {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        f.write_str("Multipart { parts: .. }")
68    }
69}
70
71#[derive(Debug)]
72pub struct MultipartPart {
73    pub headers: HeaderMap,
74    pub body: HttpBody,
75}
76
77/// A multipart field carries its bytes directly; its part headers are MIME
78/// metadata, not nested JSON properties in the API payload.
79#[cfg(feature = "schema")]
80impl schemars::JsonSchema for MultipartPart {
81    fn schema_name() -> std::borrow::Cow<'static, str> {
82        "BinaryPart".into()
83    }
84
85    fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
86        schemars::json_schema!({ "type": "string", "format": "binary" })
87    }
88}