Skip to main content

aloha/bhttp/
builder.rs

1// Copyright (c) 2022-2023 Cloudflare, Inc.
2// Licensed under the Apache-2.0 license found in the LICENSE file or
3// at http://www.apache.org/licenses/LICENSE-2.0
4
5use bytes::BufMut;
6
7use super::*;
8
9/// Entrypoint to build a bHTTP message.
10pub struct Builder<B> {
11    buf: B,
12    framing: Framing,
13}
14
15impl<B: BufMut> Builder<B> {
16    /// Create a new builder.
17    pub fn new(buf: B, framing: Framing) -> Self {
18        Self { buf, framing }
19    }
20
21    /// Push request control data.
22    pub fn push_ctrl(
23        mut self,
24        mut method: &[u8],
25        mut scheme: &[u8],
26        mut authority: &[u8],
27        mut path: &[u8],
28    ) -> Result<HeaderBuilder<B>> {
29        if !self.framing.is_request() {
30            return Err(Error::UnexpectedFraming);
31        }
32
33        if !self.buf.has_remaining_mut() {
34            return Err(Error::ShortBuf);
35        }
36        self.buf.put_u8(self.framing as u8);
37
38        compose_len_bytes(&mut self.buf, &mut method)?;
39        compose_len_bytes(&mut self.buf, &mut scheme)?;
40        compose_len_bytes(&mut self.buf, &mut authority)?;
41        compose_len_bytes(&mut self.buf, &mut path)?;
42
43        Ok(HeaderBuilder {
44            buf: self.buf,
45            framing: self.framing,
46            appending: false,
47        })
48    }
49
50    /// Push informational/final response contral data.
51    pub fn push_status(mut self, status: usize) -> Result<InfoBuilder<B>> {
52        if self.framing.is_request() {
53            return Err(Error::UnexpectedFraming);
54        }
55
56        if !self.buf.has_remaining_mut() {
57            return Err(Error::ShortBuf);
58        }
59        self.buf.put_u8(self.framing as u8);
60
61        VarInt::try_from(status)?.compose(&mut self.buf)?;
62
63        Ok(InfoBuilder {
64            buf: self.buf,
65            framing: self.framing,
66            is_final: is_final_ctrl(status),
67            appending: false,
68        })
69    }
70}
71
72/// Build response informational/final control data.
73pub struct RCtrlBuilder<B> {
74    buf: B,
75    framing: Framing,
76}
77
78impl<B: BufMut> RCtrlBuilder<B> {
79    /// Push informational/final response contral data.
80    pub fn push_status(mut self, status: usize) -> Result<InfoBuilder<B>> {
81        VarInt::try_from(status)?.compose(&mut self.buf)?;
82        Ok(InfoBuilder {
83            buf: self.buf,
84            framing: self.framing,
85            is_final: is_final_ctrl(status),
86            appending: false,
87        })
88    }
89}
90
91/// Build informational response fields.
92pub struct InfoBuilder<B> {
93    buf: B,
94    framing: Framing,
95    is_final: bool,
96    appending: bool,
97}
98
99impl<B: BufMut> InfoBuilder<B> {
100    /// Push all the informational fields.
101    pub fn push_fields(mut self, fields: &[(&[u8], &[u8])]) -> Result<RCtrlBuilder<B>> {
102        if self.is_final {
103            return Err(Error::UnexpectedBuildState);
104        }
105
106        push_fields(&mut self.buf, self.framing, fields)?;
107        Ok(RCtrlBuilder {
108            buf: self.buf,
109            framing: self.framing,
110        })
111    }
112
113    /// Append a single field line in indeterminate length mode.
114    pub fn append_field(mut self, field: (&[u8], &[u8])) -> Result<Self> {
115        if self.framing.known_len() {
116            return Err(Error::UnexpectedFraming);
117        }
118
119        if self.is_final {
120            return Err(Error::UnexpectedBuildState);
121        }
122
123        let (mut name, mut value) = field;
124        if name.is_empty() {
125            return Err(Error::InvalidInput);
126        }
127        compose_len_bytes(&mut self.buf, &mut name)?;
128        compose_len_bytes(&mut self.buf, &mut value)?;
129        self.appending = true;
130        Ok(self)
131    }
132
133    /// Finish appending field line.
134    pub fn done(mut self) -> Result<RCtrlBuilder<B>> {
135        if self.framing.known_len() {
136            return Err(Error::UnexpectedFraming);
137        }
138
139        if self.is_final || !self.appending {
140            return Err(Error::UnexpectedBuildState);
141        }
142
143        if !self.buf.has_remaining_mut() {
144            return Err(Error::ShortBuf);
145        }
146        self.buf.put_u8(CONTENT_TERMINATOR);
147
148        Ok(RCtrlBuilder {
149            buf: self.buf,
150            framing: self.framing,
151        })
152    }
153
154    /// Move to next builder in chain.
155    pub fn next(self) -> Result<HeaderBuilder<B>> {
156        if !self.is_final {
157            return Err(Error::UnexpectedBuildState);
158        }
159
160        Ok(HeaderBuilder {
161            buf: self.buf,
162            framing: self.framing,
163            appending: false,
164        })
165    }
166}
167
168/// Build headers.
169pub struct HeaderBuilder<B> {
170    buf: B,
171    framing: Framing,
172    appending: bool,
173}
174
175impl<B: BufMut> HeaderBuilder<B> {
176    /// Push all the headers.
177    pub fn push_headers(mut self, fields: &[(&[u8], &[u8])]) -> Result<ContentBuilder<B>> {
178        push_fields(&mut self.buf, self.framing, fields)?;
179        Ok(ContentBuilder {
180            buf: self.buf,
181            framing: self.framing,
182            appending: false,
183        })
184    }
185
186    /// Append a single header in indeterminate length mode.
187    pub fn append_header(mut self, field: (&[u8], &[u8])) -> Result<Self> {
188        if self.framing.known_len() {
189            return Err(Error::UnexpectedFraming);
190        }
191
192        let (mut name, mut value) = field;
193        if name.is_empty() {
194            return Err(Error::InvalidInput);
195        }
196        compose_len_bytes(&mut self.buf, &mut name)?;
197        compose_len_bytes(&mut self.buf, &mut value)?;
198        self.appending = true;
199        Ok(self)
200    }
201
202    /// Move to next builder in chain.
203    pub fn next(mut self) -> Result<ContentBuilder<B>> {
204        if !self.appending {
205            return Err(Error::UnexpectedBuildState);
206        }
207
208        if !self.buf.has_remaining_mut() {
209            return Err(Error::ShortBuf);
210        }
211        self.buf.put_u8(CONTENT_TERMINATOR);
212        Ok(ContentBuilder {
213            buf: self.buf,
214            framing: self.framing,
215            appending: false,
216        })
217    }
218}
219
220/// Build content.
221pub struct ContentBuilder<B> {
222    buf: B,
223    framing: Framing,
224    appending: bool,
225}
226
227impl<B: BufMut> ContentBuilder<B> {
228    /// Push content at once.
229    pub fn push_content(mut self, mut content: &[u8]) -> Result<TailerBuilder<B>> {
230        let empty = content.is_empty();
231        compose_len_bytes(&mut self.buf, &mut content)?;
232
233        // Content has already been terminated if empty.
234        if !self.framing.known_len() && !empty {
235            if !self.buf.has_remaining_mut() {
236                return Err(Error::ShortBuf);
237            }
238            self.buf.put_u8(CONTENT_TERMINATOR);
239        }
240
241        Ok(TailerBuilder {
242            buf: self.buf,
243            framing: self.framing,
244            appending: false,
245        })
246    }
247
248    /// Append a content chunk in indeterminate length mode.
249    pub fn append_chunk(mut self, mut chunk: &[u8]) -> Result<Self> {
250        if chunk.is_empty() {
251            return Err(Error::InvalidInput);
252        }
253
254        compose_len_bytes(&mut self.buf, &mut chunk)?;
255        self.appending = true;
256        Ok(self)
257    }
258
259    /// Move to next builder in chain.
260    pub fn next(mut self) -> Result<TailerBuilder<B>> {
261        if !self.buf.has_remaining_mut() {
262            return Err(Error::ShortBuf);
263        }
264        self.buf.put_u8(CONTENT_TERMINATOR);
265
266        Ok(TailerBuilder {
267            buf: self.buf,
268            framing: self.framing,
269            appending: false,
270        })
271    }
272}
273
274/// Build tailers.
275pub struct TailerBuilder<B> {
276    buf: B,
277    framing: Framing,
278    appending: bool,
279}
280
281impl<B: BufMut> TailerBuilder<B> {
282    /// Push all tailers at once.
283    pub fn push_tailers(mut self, fields: &[(&[u8], &[u8])]) -> Result<PaddingBuilder<B>> {
284        push_fields(&mut self.buf, self.framing, fields)?;
285        Ok(PaddingBuilder { buf: self.buf })
286    }
287
288    /// Append a single tailer in indeterminate length mode.
289    pub fn append_tailer(mut self, field: (&[u8], &[u8])) -> Result<Self> {
290        if self.framing.known_len() {
291            return Err(Error::UnexpectedFraming);
292        }
293
294        let (mut name, mut value) = field;
295        if name.is_empty() {
296            return Err(Error::InvalidInput);
297        }
298        compose_len_bytes(&mut self.buf, &mut name)?;
299        compose_len_bytes(&mut self.buf, &mut value)?;
300        self.appending = true;
301        Ok(self)
302    }
303
304    /// Move to next builder in chain.
305    pub fn next(mut self) -> Result<PaddingBuilder<B>> {
306        if !self.appending {
307            return Err(Error::UnexpectedBuildState);
308        }
309
310        if !self.buf.has_remaining_mut() {
311            return Err(Error::ShortBuf);
312        }
313        self.buf.put_u8(CONTENT_TERMINATOR);
314        Ok(PaddingBuilder { buf: self.buf })
315    }
316}
317
318/// Build padding.
319pub struct PaddingBuilder<B> {
320    buf: B,
321}
322
323impl<B: BufMut> PaddingBuilder<B> {
324    /// Push n bytes of padding.
325    pub fn push_padding(mut self, n: usize) -> Result<()> {
326        if self.buf.remaining_mut() < n {
327            return Err(Error::ShortBuf);
328        }
329        self.buf.put_bytes(CONTENT_TERMINATOR, n);
330        Ok(())
331    }
332}
333
334fn push_fields<B: BufMut>(buf: &mut B, framing: Framing, fields: &[(&[u8], &[u8])]) -> Result<()> {
335    if framing.known_len() {
336        push_fields_with_len(buf, fields)
337    } else {
338        push_fields_no_len(buf, fields)
339    }
340}
341
342fn push_fields_with_len<B: BufMut>(buf: &mut B, fields: &[(&[u8], &[u8])]) -> Result<()> {
343    let mut len = 0;
344    for (name, value) in fields.iter() {
345        len += VarInt::try_from(name.len())?.size();
346        len += name.len();
347        len += VarInt::try_from(value.len())?.size();
348        len += value.len();
349    }
350
351    let n = VarInt::try_from(len)?;
352    if buf.remaining_mut() < n.size() + len {
353        return Err(Error::ShortBuf);
354    }
355
356    n.compose(buf)?;
357
358    for (mut name, mut value) in fields.iter() {
359        compose_len_bytes(buf, &mut name)?;
360        compose_len_bytes(buf, &mut value)?;
361    }
362
363    Ok(())
364}
365
366fn push_fields_no_len<B: BufMut>(buf: &mut B, fields: &[(&[u8], &[u8])]) -> Result<()> {
367    for (mut name, mut value) in fields.iter() {
368        if name.is_empty() {
369            return Err(Error::InvalidInput);
370        }
371        compose_len_bytes(buf, &mut name)?;
372        compose_len_bytes(buf, &mut value)?;
373    }
374
375    if !buf.has_remaining_mut() {
376        return Err(Error::ShortBuf);
377    }
378    buf.put_u8(CONTENT_TERMINATOR);
379
380    Ok(())
381}
382
383// If data is empty, 1 byte of 0 will be pushed.
384fn compose_len_bytes<B: BufMut, T: Buf>(buf: &mut B, data: &mut T) -> Result<()> {
385    let len = data.remaining();
386    let n = VarInt::try_from(len)?;
387
388    if buf.remaining_mut() < n.size() + len {
389        return Err(Error::ShortBuf);
390    }
391
392    n.compose(buf)?;
393    buf.put(data);
394    Ok(())
395}
396
397#[cfg(test)]
398mod tests {
399    use super::super::tests::*;
400    use super::*;
401
402    #[test]
403    fn build_known_len_req() {
404        let mut buf = Vec::new();
405        Builder::new(&mut buf, Framing::KnownLenReq)
406            .push_ctrl(b"GET", b"https", b"", b"/hello.txt")
407            .unwrap()
408            .push_headers(&[
409                (
410                    &b"user-agent"[..],
411                    &b"curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3"[..],
412                ),
413                (&b"host"[..], &b"www.example.com"[..]),
414                (&b"accept-language"[..], &b"en, mi"[..]),
415            ])
416            .unwrap()
417            .push_content(&[])
418            .unwrap()
419            .push_tailers(&[])
420            .unwrap();
421        assert_eq!(EXAMPLE_KNOWN_LEN_REQ2, buf);
422    }
423
424    #[test]
425    fn build_ind_len_req() {
426        let mut buf = Vec::new();
427        Builder::new(&mut buf, Framing::IndLenReq)
428            .push_ctrl(b"GET", b"https", b"", b"/hello.txt")
429            .unwrap()
430            .push_headers(&[
431                (
432                    &b"user-agent"[..],
433                    &b"curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3"[..],
434                ),
435                (&b"host"[..], &b"www.example.com"[..]),
436                (&b"accept-language"[..], &b"en, mi"[..]),
437            ])
438            .unwrap()
439            .push_content(&[])
440            .unwrap()
441            .push_tailers(&[])
442            .unwrap()
443            .push_padding(10)
444            .unwrap();
445        assert_eq!(EXAMPLE_IND_LEN_REQ1, buf);
446    }
447
448    #[test]
449    fn build_known_len_res() {
450        let mut buf = Vec::new();
451        Builder::new(&mut buf, Framing::KnownLenRes)
452            .push_status(200)
453            .unwrap()
454            .next()
455            .unwrap()
456            .push_headers(&[])
457            .unwrap()
458            .push_content("This content contains CRLF.\r\n".as_bytes())
459            .unwrap()
460            .push_tailers(&[("trailer".as_bytes(), "text".as_bytes())])
461            .unwrap();
462        assert_eq!(EXAMPLE_KNOWN_LEN_RES1, buf);
463    }
464
465    #[test]
466    fn build_ind_len_res() {
467        let mut buf = Vec::new();
468        Builder::new(&mut buf, Framing::IndLenRes)
469            .push_status(102)
470            .unwrap()
471            .push_fields(&[("running".as_bytes(), r#""sleep 15""#.as_bytes())])
472            .unwrap()
473            .push_status(103)
474            .unwrap()
475            .push_fields(&[
476                (
477                    "link".as_bytes(),
478                    r#"</style.css>; rel=preload; as=style"#.as_bytes(),
479                ),
480                (
481                    "link".as_bytes(),
482                    r#"</script.js>; rel=preload; as=script"#.as_bytes(),
483                ),
484            ])
485            .unwrap()
486            .push_status(200)
487            .unwrap()
488            .next()
489            .unwrap()
490            .push_headers(&[
491                (
492                    "date".as_bytes(),
493                    r#"Mon, 27 Jul 2009 12:28:53 GMT"#.as_bytes(),
494                ),
495                ("server".as_bytes(), "Apache".as_bytes()),
496                (
497                    "last-modified".as_bytes(),
498                    "Wed, 22 Jul 2009 19:15:56 GMT".as_bytes(),
499                ),
500                ("etag".as_bytes(), r#""34aa387-d-1568eb00""#.as_bytes()),
501                ("accept-ranges".as_bytes(), "bytes".as_bytes()),
502                ("content-length".as_bytes(), "51".as_bytes()),
503                ("vary".as_bytes(), "Accept-Encoding".as_bytes()),
504                ("content-type".as_bytes(), "text/plain".as_bytes()),
505            ])
506            .unwrap()
507            .push_content("Hello World! My content includes a trailing CRLF.\r\n".as_bytes())
508            .unwrap()
509            .push_tailers(&[])
510            .unwrap();
511
512        assert_eq!(EXAMPLE_IND_LEN_RES1, &buf);
513    }
514}