Skip to main content

sark_core/http/response/
chunked.rs

1use http::StatusCode;
2use o3::buffer::{Owned, Shared};
3
4use super::Response;
5use super::body::Body;
6use super::header::HeaderList;
7use super::wire_emit::{CRLF, HeadWrite, Out, 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: Owned::from(self.wire_headers.as_ref()),
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) -> 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 += hex_n + CRLF.len() + part.len() + CRLF.len();
58        }
59        len + Self::ZERO_CHUNK.len()
60    }
61
62    fn write_body(&self, dst: &mut [u8], off: &mut usize) {
63        for part in &self.parts {
64            if part.is_empty() {
65                continue;
66            }
67            let mut hex = [0u8; 16];
68            let hex_n = Wire::write_hex(part.len(), &mut hex);
69            Out::put(dst, off, &hex[..hex_n]);
70            Out::put(dst, off, CRLF);
71            Out::put(dst, off, part.as_ref());
72            Out::put(dst, off, CRLF);
73        }
74        Out::put(dst, off, Self::ZERO_CHUNK);
75    }
76
77    fn head(&self) -> HeadWrite<'_, [u8], TransferEncodingChunked> {
78        HeadWrite {
79            status_str: self.status.as_str().as_bytes(),
80            reason: self
81                .status
82                .canonical_reason()
83                .map(str::as_bytes)
84                .unwrap_or(b""),
85            headers: self.wire_headers.as_ref(),
86            framing: TransferEncodingChunked,
87        }
88    }
89
90    pub fn write_into_slice(&self, out: &mut [u8], date: &[u8; 29]) -> Option<usize> {
91        let head = self.head();
92        let total = head.wire_len() + self.body_wire_len();
93        if out.len() < total {
94            return None;
95        }
96
97        let mut off = 0usize;
98        head.write(out, &mut off, date);
99        self.write_body(out, &mut off);
100        Some(off)
101    }
102
103    pub fn write_head_split(self, out: &mut [u8], date: &[u8; 29]) -> Option<(usize, Shared)> {
104        let head = self.head();
105        if out.len() < head.wire_len() {
106            return None;
107        }
108        let mut off = 0usize;
109        head.write(out, &mut off, date);
110
111        let mut body = vec![0u8; self.body_wire_len()];
112        let mut boff = 0usize;
113        self.write_body(&mut body, &mut boff);
114        Some((off, Shared::copy_from_slice(&body)))
115    }
116}