xitca_http/h1/proto/
buf_write.rs

1use core::convert::Infallible;
2
3use std::io::Write;
4
5use crate::{
6    bytes::{Buf, BufMut, BufMutWriter, Bytes, BytesMut, EitherBuf, buf::Chain},
7    util::buffered::{BufWrite, ListWriteBuf, WriteBuf},
8};
9
10/// trait for add http/1 data to buffer that implement [BufWrite] trait.
11pub trait H1BufWrite: BufWrite {
12    /// write http response head(status code and reason line, header lines) to buffer with fallible
13    /// closure. on error path the buffer is reverted back to state before method was called.
14    #[inline]
15    fn write_buf_head<F, T, E>(&mut self, func: F) -> Result<T, E>
16    where
17        F: FnOnce(&mut BytesMut) -> Result<T, E>,
18    {
19        self.write_buf(func)
20    }
21
22    /// write `&'static [u8]` to buffer.
23    fn write_buf_static(&mut self, bytes: &'static [u8]) {
24        let _ = self.write_buf(|buf| {
25            buf.put_slice(bytes);
26            Ok::<_, Infallible>(())
27        });
28    }
29
30    /// write bytes to buffer as is.
31    fn write_buf_bytes(&mut self, bytes: Bytes) {
32        let _ = self.write_buf(|buf| {
33            buf.put_slice(bytes.as_ref());
34            Ok::<_, Infallible>(())
35        });
36    }
37
38    /// write bytes to buffer as `transfer-encoding: chunked` encoded.
39    fn write_buf_bytes_chunked(&mut self, bytes: Bytes) {
40        let _ = self.write_buf(|buf| {
41            write!(BufMutWriter(buf), "{:X}\r\n", bytes.len()).unwrap();
42            buf.reserve(bytes.len() + 2);
43            buf.put_slice(bytes.as_ref());
44            buf.put_slice(b"\r\n");
45            Ok::<_, Infallible>(())
46        });
47    }
48}
49
50impl H1BufWrite for BytesMut {}
51
52impl<const BUF_LIMIT: usize> H1BufWrite for WriteBuf<BUF_LIMIT> {}
53
54// as special type for eof chunk when using transfer-encoding: chunked
55type Eof = Chain<Chain<Bytes, Bytes>, &'static [u8]>;
56
57type EncodedBuf<B, B2> = EitherBuf<B, EitherBuf<B2, &'static [u8]>>;
58
59impl<const BUF_LIMIT: usize> H1BufWrite for ListWriteBuf<EncodedBuf<Bytes, Eof>, BUF_LIMIT> {
60    fn write_buf_head<F, T, E>(&mut self, func: F) -> Result<T, E>
61    where
62        F: FnOnce(&mut BytesMut) -> Result<T, E>,
63    {
64        // list buffer use BufWrite::write_buf for temporary response head storage.
65        // after the head is successfully written we must move the bytes to list.
66        self.write_buf(func).inspect(|_| {
67            let bytes = self.split_buf().freeze();
68            self.buffer(EitherBuf::Left(bytes));
69        })
70    }
71
72    #[inline]
73    fn write_buf_static(&mut self, bytes: &'static [u8]) {
74        self.buffer(EitherBuf::Right(EitherBuf::Right(bytes)));
75    }
76
77    #[inline]
78    fn write_buf_bytes(&mut self, bytes: Bytes) {
79        self.buffer(EitherBuf::Left(bytes));
80    }
81
82    #[inline]
83    fn write_buf_bytes_chunked(&mut self, bytes: Bytes) {
84        let chunk = Bytes::from(format!("{:X}\r\n", bytes.len()))
85            .chain(bytes)
86            .chain(b"\r\n" as &'static [u8]);
87        self.buffer(EitherBuf::Right(EitherBuf::Left(chunk)));
88    }
89}
90
91impl<L, R> H1BufWrite for EitherBuf<L, R>
92where
93    L: H1BufWrite,
94    R: H1BufWrite,
95{
96    #[inline]
97    fn write_buf_head<F, T, E>(&mut self, func: F) -> Result<T, E>
98    where
99        F: FnOnce(&mut BytesMut) -> Result<T, E>,
100    {
101        match *self {
102            Self::Left(ref mut l) => l.write_buf_head(func),
103            Self::Right(ref mut r) => r.write_buf_head(func),
104        }
105    }
106
107    #[inline]
108    fn write_buf_static(&mut self, bytes: &'static [u8]) {
109        match *self {
110            Self::Left(ref mut l) => l.write_buf_static(bytes),
111            Self::Right(ref mut r) => r.write_buf_static(bytes),
112        }
113    }
114
115    #[inline]
116    fn write_buf_bytes(&mut self, bytes: Bytes) {
117        match *self {
118            Self::Left(ref mut l) => l.write_buf_bytes(bytes),
119            Self::Right(ref mut r) => r.write_buf_bytes(bytes),
120        }
121    }
122
123    #[inline]
124    fn write_buf_bytes_chunked(&mut self, bytes: Bytes) {
125        match *self {
126            Self::Left(ref mut l) => l.write_buf_bytes_chunked(bytes),
127            Self::Right(ref mut r) => r.write_buf_bytes_chunked(bytes),
128        }
129    }
130}