1use std::collections::HashSet;
2use std::fmt;
3use std::io::IoSlice;
4
5use bytes::buf::{Chain, Take};
6use bytes::{Buf, Bytes};
7use http::{
8 header::{
9 AUTHORIZATION, CACHE_CONTROL, CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_RANGE,
10 CONTENT_TYPE, HOST, MAX_FORWARDS, SET_COOKIE, TE, TRAILER, TRANSFER_ENCODING,
11 },
12 HeaderMap, HeaderName,
13};
14
15use super::io::WriteBuf;
16use super::role::{write_headers, write_headers_title_case};
17
18type StaticBuf = &'static [u8];
19
20#[derive(Debug, Clone, PartialEq)]
22pub(crate) struct Encoder {
23 kind: Kind,
24 is_last: bool,
25}
26
27#[derive(Debug)]
28pub(crate) struct EncodedBuf<B> {
29 kind: BufKind<B>,
30}
31
32#[derive(Debug)]
33pub(crate) struct NotEof(u64);
34
35#[derive(Debug, PartialEq, Clone)]
36enum Kind {
37 Chunked(Option<Vec<HeaderName>>),
39 Length(u64),
43 #[cfg(feature = "server")]
48 CloseDelimited,
49}
50
51#[derive(Debug)]
52enum BufKind<B> {
53 Exact(B),
54 Limited(Take<B>),
55 Chunked(Chain<Chain<ChunkSize, B>, StaticBuf>),
56 ChunkedEnd(StaticBuf),
57 Trailers(Chain<Chain<StaticBuf, Bytes>, StaticBuf>),
58}
59
60impl Encoder {
61 fn new(kind: Kind) -> Encoder {
62 Encoder {
63 kind,
64 is_last: false,
65 }
66 }
67 pub(crate) fn chunked() -> Encoder {
68 Encoder::new(Kind::Chunked(None))
69 }
70
71 pub(crate) fn length(len: u64) -> Encoder {
72 Encoder::new(Kind::Length(len))
73 }
74
75 #[cfg(feature = "server")]
76 pub(crate) fn close_delimited() -> Encoder {
77 Encoder::new(Kind::CloseDelimited)
78 }
79
80 pub(crate) fn into_chunked_with_trailing_fields(self, trailers: Vec<HeaderName>) -> Encoder {
81 match self.kind {
82 Kind::Chunked(_) => Encoder {
83 kind: Kind::Chunked(Some(trailers)),
84 is_last: self.is_last,
85 },
86 _ => self,
87 }
88 }
89
90 pub(crate) fn is_eof(&self) -> bool {
91 matches!(self.kind, Kind::Length(0))
92 }
93
94 #[cfg(feature = "server")]
95 pub(crate) fn set_last(mut self, is_last: bool) -> Self {
96 self.is_last = is_last;
97 self
98 }
99
100 pub(crate) fn is_last(&self) -> bool {
101 self.is_last
102 }
103
104 pub(crate) fn is_close_delimited(&self) -> bool {
105 match self.kind {
106 #[cfg(feature = "server")]
107 Kind::CloseDelimited => true,
108 _ => false,
109 }
110 }
111
112 pub(crate) fn is_chunked(&self) -> bool {
113 matches!(self.kind, Kind::Chunked(_))
114 }
115
116 pub(crate) fn end<B>(&self) -> Result<Option<EncodedBuf<B>>, NotEof> {
117 match self.kind {
118 Kind::Length(0) => Ok(None),
119 Kind::Chunked(_) => Ok(Some(EncodedBuf {
120 kind: BufKind::ChunkedEnd(b"0\r\n\r\n"),
121 })),
122 #[cfg(feature = "server")]
123 Kind::CloseDelimited => Ok(None),
124 Kind::Length(n) => Err(NotEof(n)),
125 }
126 }
127
128 pub(crate) fn encode<B>(&mut self, msg: B) -> EncodedBuf<B>
129 where
130 B: Buf,
131 {
132 let len = msg.remaining();
133 debug_assert!(len > 0, "encode() called with empty buf");
134
135 let kind = match &mut self.kind {
136 Kind::Chunked(_) => {
137 trace!("encoding chunked {}B", len);
138 let buf = ChunkSize::new(len)
139 .chain(msg)
140 .chain(b"\r\n" as &'static [u8]);
141 BufKind::Chunked(buf)
142 }
143 Kind::Length(remaining) => {
144 trace!("sized write, len = {}", len);
145 match usize::try_from(*remaining) {
146 Ok(limit) if limit < len => {
148 *remaining = 0;
149 BufKind::Limited(msg.take(limit))
150 }
151 Ok(_) | Err(_) => {
154 *remaining -= len as u64;
155 BufKind::Exact(msg)
156 }
157 }
158 }
159 #[cfg(feature = "server")]
160 Kind::CloseDelimited => {
161 trace!("close delimited write {}B", len);
162 BufKind::Exact(msg)
163 }
164 };
165 EncodedBuf { kind }
166 }
167
168 pub(crate) fn encode_trailers<B>(
169 &self,
170 trailers: HeaderMap,
171 title_case_headers: bool,
172 ) -> Option<EncodedBuf<B>> {
173 trace!("encoding trailers");
174 match &self.kind {
175 Kind::Chunked(Some(allowed_trailer_fields)) => {
176 let allowed_set: HashSet<&HeaderName> = allowed_trailer_fields.iter().collect();
177
178 let mut cur_name = None;
179 let mut allowed_trailers = HeaderMap::new();
180
181 for (opt_name, value) in trailers {
182 if let Some(n) = opt_name {
183 cur_name = Some(n);
184 }
185 let name = cur_name.as_ref().expect("current header name");
186
187 if allowed_set.contains(name) {
188 if is_valid_trailer_field(name) {
189 allowed_trailers.append(name, value);
190 } else {
191 debug!("trailer field is not valid: {}", &name);
192 }
193 } else {
194 debug!("trailer header name not found in trailer header: {}", &name);
195 }
196 }
197
198 let mut buf = Vec::new();
199 if title_case_headers {
200 write_headers_title_case(&allowed_trailers, &mut buf);
201 } else {
202 write_headers(&allowed_trailers, &mut buf);
203 }
204
205 if buf.is_empty() {
206 return None;
207 }
208
209 Some(EncodedBuf {
210 kind: BufKind::Trailers(b"0\r\n".chain(Bytes::from(buf)).chain(b"\r\n")),
211 })
212 }
213 Kind::Chunked(None) => {
214 debug!("attempted to encode trailers, but the trailer header is not set");
215 None
216 }
217 _ => {
218 debug!("attempted to encode trailers for non-chunked response");
219 None
220 }
221 }
222 }
223
224 pub(super) fn encode_and_end<B>(&self, msg: B, dst: &mut WriteBuf<EncodedBuf<B>>) -> bool
225 where
226 B: Buf,
227 {
228 let len = msg.remaining();
229 debug_assert!(len > 0, "encode() called with empty buf");
230
231 match self.kind {
232 Kind::Chunked(_) => {
233 trace!("encoding chunked {}B", len);
234 let buf = ChunkSize::new(len)
235 .chain(msg)
236 .chain(b"\r\n0\r\n\r\n" as &'static [u8]);
237 dst.buffer(buf);
238 !self.is_last
239 }
240 Kind::Length(remaining) => {
241 use std::cmp::Ordering;
242
243 trace!("sized write, len = {}", len);
244 match (len as u64).cmp(&remaining) {
245 Ordering::Equal => {
246 dst.buffer(msg);
247 !self.is_last
248 }
249 #[allow(clippy::cast_possible_truncation)]
250 Ordering::Greater => {
251 dst.buffer(msg.take(remaining as usize));
252 !self.is_last
253 }
254 Ordering::Less => {
255 dst.buffer(msg);
256 false
257 }
258 }
259 }
260 #[cfg(feature = "server")]
261 Kind::CloseDelimited => {
262 trace!("close delimited write {}B", len);
263 dst.buffer(msg);
264 false
265 }
266 }
267 }
268}
269
270fn is_valid_trailer_field(name: &HeaderName) -> bool {
271 !matches!(
272 *name,
273 AUTHORIZATION
274 | CACHE_CONTROL
275 | CONTENT_ENCODING
276 | CONTENT_LENGTH
277 | CONTENT_RANGE
278 | CONTENT_TYPE
279 | HOST
280 | MAX_FORWARDS
281 | SET_COOKIE
282 | TRAILER
283 | TRANSFER_ENCODING
284 | TE
285 )
286}
287
288impl<B> Buf for EncodedBuf<B>
289where
290 B: Buf,
291{
292 #[inline]
293 fn remaining(&self) -> usize {
294 match &self.kind {
295 BufKind::Exact(b) => b.remaining(),
296 BufKind::Limited(b) => b.remaining(),
297 BufKind::Chunked(b) => b.remaining(),
298 BufKind::ChunkedEnd(b) => b.remaining(),
299 BufKind::Trailers(b) => b.remaining(),
300 }
301 }
302
303 #[inline]
304 fn chunk(&self) -> &[u8] {
305 match &self.kind {
306 BufKind::Exact(b) => b.chunk(),
307 BufKind::Limited(b) => b.chunk(),
308 BufKind::Chunked(b) => b.chunk(),
309 BufKind::ChunkedEnd(b) => b.chunk(),
310 BufKind::Trailers(b) => b.chunk(),
311 }
312 }
313
314 #[inline]
315 fn advance(&mut self, cnt: usize) {
316 match &mut self.kind {
317 BufKind::Exact(b) => b.advance(cnt),
318 BufKind::Limited(b) => b.advance(cnt),
319 BufKind::Chunked(b) => b.advance(cnt),
320 BufKind::ChunkedEnd(b) => b.advance(cnt),
321 BufKind::Trailers(b) => b.advance(cnt),
322 }
323 }
324
325 #[inline]
326 fn chunks_vectored<'data>(&'data self, dst: &mut [IoSlice<'data>]) -> usize {
327 match &self.kind {
328 BufKind::Exact(b) => b.chunks_vectored(dst),
329 BufKind::Limited(b) => b.chunks_vectored(dst),
330 BufKind::Chunked(b) => b.chunks_vectored(dst),
331 BufKind::ChunkedEnd(b) => b.chunks_vectored(dst),
332 BufKind::Trailers(b) => b.chunks_vectored(dst),
333 }
334 }
335}
336
337const USIZE_BYTES: usize = std::mem::size_of::<usize>();
338
339const CHUNK_SIZE_MAX_BYTES: usize = USIZE_BYTES * 2;
341
342#[derive(Clone, Copy)]
343struct ChunkSize {
344 bytes: [u8; CHUNK_SIZE_MAX_BYTES + 2],
345 pos: u8,
346 len: u8,
347}
348
349impl ChunkSize {
350 fn new(len: usize) -> ChunkSize {
351 use std::fmt::Write;
352 let mut size = ChunkSize {
353 bytes: [0; CHUNK_SIZE_MAX_BYTES + 2],
354 pos: 0,
355 len: 0,
356 };
357 write!(&mut size, "{len:X}\r\n").expect("CHUNK_SIZE_MAX_BYTES should fit any usize");
358 size
359 }
360}
361
362impl Buf for ChunkSize {
363 #[inline]
364 fn remaining(&self) -> usize {
365 (self.len - self.pos).into()
366 }
367
368 #[inline]
369 fn chunk(&self) -> &[u8] {
370 &self.bytes[self.pos.into()..self.len.into()]
371 }
372
373 #[inline]
374 #[allow(clippy::cast_possible_truncation)]
375 fn advance(&mut self, cnt: usize) {
376 assert!(cnt <= self.remaining());
377 self.pos += cnt as u8; }
379}
380
381impl fmt::Debug for ChunkSize {
382 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383 f.debug_struct("ChunkSize")
384 .field("bytes", &&self.bytes[..self.len.into()])
385 .field("pos", &self.pos)
386 .finish()
387 }
388}
389
390impl fmt::Write for ChunkSize {
391 #[allow(clippy::cast_possible_truncation)]
392 fn write_str(&mut self, num: &str) -> fmt::Result {
393 use std::io::Write;
394 (&mut self.bytes[self.len.into()..])
395 .write_all(num.as_bytes())
396 .expect("&mut [u8].write() cannot error");
397 debug_assert!(u8::try_from(num.len()).is_ok());
398 self.len += num.len() as u8; Ok(())
400 }
401}
402
403impl<B: Buf> From<B> for EncodedBuf<B> {
404 fn from(buf: B) -> Self {
405 EncodedBuf {
406 kind: BufKind::Exact(buf),
407 }
408 }
409}
410
411impl<B: Buf> From<Take<B>> for EncodedBuf<B> {
412 fn from(buf: Take<B>) -> Self {
413 EncodedBuf {
414 kind: BufKind::Limited(buf),
415 }
416 }
417}
418
419impl<B: Buf> From<Chain<Chain<ChunkSize, B>, StaticBuf>> for EncodedBuf<B> {
420 fn from(buf: Chain<Chain<ChunkSize, B>, StaticBuf>) -> Self {
421 EncodedBuf {
422 kind: BufKind::Chunked(buf),
423 }
424 }
425}
426
427impl fmt::Display for NotEof {
428 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
429 write!(f, "early end, expected {} more bytes", self.0)
430 }
431}
432
433impl std::error::Error for NotEof {}
434
435#[cfg(test)]
436mod tests {
437 use bytes::BufMut;
438 use http::{
439 header::{
440 AUTHORIZATION, CACHE_CONTROL, CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_RANGE,
441 CONTENT_TYPE, HOST, MAX_FORWARDS, SET_COOKIE, TE, TRAILER, TRANSFER_ENCODING,
442 },
443 HeaderMap, HeaderName, HeaderValue,
444 };
445
446 use super::super::io::Cursor;
447 use super::Encoder;
448
449 #[test]
450 fn chunked() {
451 let mut encoder = Encoder::chunked();
452 let mut dst = Vec::new();
453
454 let msg1 = b"foo bar".as_ref();
455 let buf1 = encoder.encode(msg1);
456 dst.put(buf1);
457 assert_eq!(dst, b"7\r\nfoo bar\r\n");
458
459 let msg2 = b"baz quux herp".as_ref();
460 let buf2 = encoder.encode(msg2);
461 dst.put(buf2);
462
463 assert_eq!(dst, b"7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n");
464
465 let end = encoder.end::<Cursor<Vec<u8>>>().unwrap().unwrap();
466 dst.put(end);
467
468 assert_eq!(
469 dst,
470 b"7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n0\r\n\r\n".as_ref()
471 );
472 }
473
474 #[test]
475 fn length() {
476 let max_len = 8;
477 let mut encoder = Encoder::length(max_len as u64);
478 let mut dst = Vec::new();
479
480 let msg1 = b"foo bar".as_ref();
481 let buf1 = encoder.encode(msg1);
482 dst.put(buf1);
483
484 assert_eq!(dst, b"foo bar");
485 assert!(!encoder.is_eof());
486 encoder.end::<()>().unwrap_err();
487
488 let msg2 = b"baz".as_ref();
489 let buf2 = encoder.encode(msg2);
490 dst.put(buf2);
491
492 assert_eq!(dst.len(), max_len);
493 assert_eq!(dst, b"foo barb");
494 assert!(encoder.is_eof());
495 assert!(encoder.end::<()>().unwrap().is_none());
496 }
497
498 #[cfg(feature = "server")]
499 #[test]
500 fn eof() {
501 let mut encoder = Encoder::close_delimited();
502 let mut dst = Vec::new();
503
504 let msg1 = b"foo bar".as_ref();
505 let buf1 = encoder.encode(msg1);
506 dst.put(buf1);
507
508 assert_eq!(dst, b"foo bar");
509 assert!(!encoder.is_eof());
510 encoder.end::<()>().unwrap();
511
512 let msg2 = b"baz".as_ref();
513 let buf2 = encoder.encode(msg2);
514 dst.put(buf2);
515
516 assert_eq!(dst, b"foo barbaz");
517 assert!(!encoder.is_eof());
518 encoder.end::<()>().unwrap();
519 }
520
521 #[test]
522 fn chunked_with_valid_trailers() {
523 let encoder = Encoder::chunked();
524 let trailers = vec![HeaderName::from_static("chunky-trailer")];
525 let encoder = encoder.into_chunked_with_trailing_fields(trailers);
526
527 let headers = HeaderMap::from_iter(vec![
528 (
529 HeaderName::from_static("chunky-trailer"),
530 HeaderValue::from_static("header data"),
531 ),
532 (
533 HeaderName::from_static("should-not-be-included"),
534 HeaderValue::from_static("oops"),
535 ),
536 ]);
537
538 let buf1 = encoder.encode_trailers::<&[u8]>(headers, false).unwrap();
539
540 let mut dst = Vec::new();
541 dst.put(buf1);
542 assert_eq!(dst, b"0\r\nchunky-trailer: header data\r\n\r\n");
543 }
544
545 #[test]
546 fn chunked_with_multiple_trailer_headers() {
547 let encoder = Encoder::chunked();
548 let trailers = vec![
549 HeaderName::from_static("chunky-trailer"),
550 HeaderName::from_static("chunky-trailer-2"),
551 ];
552 let encoder = encoder.into_chunked_with_trailing_fields(trailers);
553
554 let headers = HeaderMap::from_iter(vec![
555 (
556 HeaderName::from_static("chunky-trailer"),
557 HeaderValue::from_static("header data"),
558 ),
559 (
560 HeaderName::from_static("chunky-trailer-2"),
561 HeaderValue::from_static("more header data"),
562 ),
563 ]);
564
565 let buf1 = encoder.encode_trailers::<&[u8]>(headers, false).unwrap();
566
567 let mut dst = Vec::new();
568 dst.put(buf1);
569 assert_eq!(
570 dst,
571 b"0\r\nchunky-trailer: header data\r\nchunky-trailer-2: more header data\r\n\r\n"
572 );
573 }
574
575 #[test]
576 fn chunked_with_duplicate_trailer_values() {
577 let encoder = Encoder::chunked();
578 let trailers = vec![HeaderName::from_static("chunky-trailer")];
579 let encoder = encoder.into_chunked_with_trailing_fields(trailers);
580
581 let mut headers = HeaderMap::new();
582 headers.append(
583 HeaderName::from_static("chunky-trailer"),
584 HeaderValue::from_static("first"),
585 );
586 headers.append(
587 HeaderName::from_static("chunky-trailer"),
588 HeaderValue::from_static("second"),
589 );
590
591 let buf1 = encoder.encode_trailers::<&[u8]>(headers, false).unwrap();
592
593 let mut dst = Vec::new();
594 dst.put(buf1);
595 assert_eq!(
596 dst,
597 b"0\r\nchunky-trailer: first\r\nchunky-trailer: second\r\n\r\n"
598 );
599 }
600
601 #[test]
602 fn chunked_with_no_trailer_header() {
603 let encoder = Encoder::chunked();
604
605 let headers = HeaderMap::from_iter(vec![(
606 HeaderName::from_static("chunky-trailer"),
607 HeaderValue::from_static("header data"),
608 )]);
609
610 assert!(encoder
611 .encode_trailers::<&[u8]>(headers.clone(), false)
612 .is_none());
613
614 let trailers = vec![];
615 let encoder = encoder.into_chunked_with_trailing_fields(trailers);
616
617 assert!(encoder.encode_trailers::<&[u8]>(headers, false).is_none());
618 }
619
620 #[test]
621 fn chunked_with_invalid_trailers() {
622 let encoder = Encoder::chunked();
623
624 let trailers = vec![
625 AUTHORIZATION,
626 CACHE_CONTROL,
627 CONTENT_ENCODING,
628 CONTENT_LENGTH,
629 CONTENT_RANGE,
630 CONTENT_TYPE,
631 HOST,
632 MAX_FORWARDS,
633 SET_COOKIE,
634 TRAILER,
635 TRANSFER_ENCODING,
636 TE,
637 ];
638 let encoder = encoder.into_chunked_with_trailing_fields(trailers);
639
640 let mut headers = HeaderMap::new();
641 headers.insert(AUTHORIZATION, HeaderValue::from_static("header data"));
642 headers.insert(CACHE_CONTROL, HeaderValue::from_static("header data"));
643 headers.insert(CONTENT_ENCODING, HeaderValue::from_static("header data"));
644 headers.insert(CONTENT_LENGTH, HeaderValue::from_static("header data"));
645 headers.insert(CONTENT_RANGE, HeaderValue::from_static("header data"));
646 headers.insert(CONTENT_TYPE, HeaderValue::from_static("header data"));
647 headers.insert(HOST, HeaderValue::from_static("header data"));
648 headers.insert(MAX_FORWARDS, HeaderValue::from_static("header data"));
649 headers.insert(SET_COOKIE, HeaderValue::from_static("header data"));
650 headers.insert(TRAILER, HeaderValue::from_static("header data"));
651 headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("header data"));
652 headers.insert(TE, HeaderValue::from_static("header data"));
653
654 assert!(encoder.encode_trailers::<&[u8]>(headers, true).is_none());
655 }
656
657 #[test]
658 fn chunked_with_title_case_headers() {
659 let encoder = Encoder::chunked();
660 let trailers = vec![HeaderName::from_static("chunky-trailer")];
661 let encoder = encoder.into_chunked_with_trailing_fields(trailers);
662
663 let headers = HeaderMap::from_iter(vec![(
664 HeaderName::from_static("chunky-trailer"),
665 HeaderValue::from_static("header data"),
666 )]);
667 let buf1 = encoder.encode_trailers::<&[u8]>(headers, true).unwrap();
668
669 let mut dst = Vec::new();
670 dst.put(buf1);
671 assert_eq!(dst, b"0\r\nChunky-Trailer: header data\r\n\r\n");
672 }
673
674 #[test]
675 fn chunked_trailers_case_insensitive_matching() {
676 let encoder = Encoder::chunked();
689 let trailers = vec![HeaderName::from_static("chunky-trailer")];
690 let encoder = encoder.into_chunked_with_trailing_fields(trailers);
691
692 let headers = HeaderMap::from_iter(vec![(
694 HeaderName::from_static("chunky-trailer"),
695 HeaderValue::from_static("trailer value"),
696 )]);
697
698 let buf = encoder.encode_trailers::<&[u8]>(headers, false).unwrap();
699 let mut dst = Vec::new();
700 dst.put(buf);
701 assert_eq!(dst, b"0\r\nchunky-trailer: trailer value\r\n\r\n");
702 }
703}