Skip to main content

sark_core/http/response/
chunked.rs

1use http::StatusCode;
2use o3::buffer::Shared;
3
4use super::Response;
5use super::body::Body;
6use super::header::HeaderList;
7use super::wire_emit::{CRLF, HeadWrite, ProvenWireWriter, TransferEncodingChunked};
8use crate::http::codec::Wire;
9
10#[derive(Clone, Debug)]
11pub struct Chunked {
12    status: StatusCode,
13    headers: HeaderList,
14    wire_headers: Shared,
15    parts: Vec<Shared>,
16}
17
18impl Chunked {
19    pub fn status(&self) -> StatusCode {
20        self.status
21    }
22
23    pub fn into_response(self) -> Response {
24        Response {
25            status: self.status,
26            headers: self.headers,
27            wire_headers: self.wire_headers.as_ref().to_vec(),
28            body: Body::empty(),
29            chunked_parts: Some(self.parts),
30        }
31    }
32
33    pub(super) fn from_parts(
34        status: StatusCode,
35        headers: HeaderList,
36        wire_headers: Shared,
37        parts: Vec<Shared>,
38    ) -> Self {
39        Self {
40            status,
41            headers,
42            wire_headers,
43            parts,
44        }
45    }
46
47    const ZERO_CHUNK: &'static [u8] = b"0\r\n\r\n";
48
49    fn body_wire_len(&self) -> Option<usize> {
50        let mut len = 0usize;
51        for part in &self.parts {
52            if part.is_empty() {
53                continue;
54            }
55            let mut hex = [0u8; 16];
56            let hex_n = Wire::write_hex(part.len(), &mut hex);
57            len = len
58                .checked_add(hex_n)?
59                .checked_add(CRLF.len())?
60                .checked_add(part.len())?
61                .checked_add(CRLF.len())?;
62        }
63        len.checked_add(Self::ZERO_CHUNK.len())
64    }
65
66    fn write_body(&self, out: &mut ProvenWireWriter<'_>) {
67        for part in &self.parts {
68            if part.is_empty() {
69                continue;
70            }
71            let mut hex = [0u8; 16];
72            let hex_n = Wire::write_hex(part.len(), &mut hex);
73            out.put(&hex[..hex_n]);
74            out.put(CRLF);
75            out.put(part.as_ref());
76            out.put(CRLF);
77        }
78        out.put(Self::ZERO_CHUNK);
79    }
80
81    fn head(&self) -> HeadWrite<'_, [u8], TransferEncodingChunked> {
82        HeadWrite {
83            status: self.status,
84            headers: self.wire_headers.as_ref(),
85            framing: TransferEncodingChunked,
86        }
87    }
88
89    pub fn write_into_slice(&self, out: &mut [u8], date: &[u8; 29]) -> Option<usize> {
90        let head = self.head();
91        let total = head.wire_len().checked_add(self.body_wire_len()?)?;
92        let mut out = ProvenWireWriter::new(out, total)?;
93        head.emit(&mut out, date);
94        self.write_body(&mut out);
95        Some(out.finish(total))
96    }
97
98    pub fn write_head_split(self, out: &mut [u8], date: &[u8; 29]) -> Option<(usize, Shared)> {
99        let head = self.head();
100        let head_len = head.wire_len();
101        let body_len = self.body_wire_len()?;
102        let mut out = ProvenWireWriter::new(out, head_len)?;
103        head.emit(&mut out, date);
104        let written = out.finish(head_len);
105
106        let mut body = vec![0u8; body_len];
107        let mut out = ProvenWireWriter::exact(&mut body);
108        self.write_body(&mut out);
109        out.finish(body_len);
110        Some((written, Shared::from(body)))
111    }
112}