1use crate::{Error, Options, wire};
5use base_endpoint::stream::ReadableStream;
6use base_http::{
7 Fields, Method, Request, RequestMetadata, Version,
8 body::{BodyFrame, ReadableBody},
9};
10use base_scheduler::Cancellation;
11use std::{
12 io,
13 sync::{
14 Arc,
15 atomic::{AtomicBool, Ordering},
16 },
17};
18
19#[derive(Default)]
20struct ExchangeState {
21 upgrade: Option<Box<[u8]>>,
22 trailer_options: Vec<Vec<u8>>,
23 complete: AtomicBool,
24 tunnel: AtomicBool,
25 abandoned: AtomicBool,
26}
27
28#[derive(Clone, Copy)]
29struct ExchangeFacts {
30 version: Version,
31 head: bool,
32 connect: bool,
33 persistent: bool,
34 expect_continue: bool,
35}
36
37#[derive(Clone)]
39pub struct Exchange {
40 facts: ExchangeFacts,
41 state: Option<Arc<ExchangeState>>,
42}
43
44pub(crate) fn request_persistent(head: &RequestMetadata) -> bool {
45 head.persistent != Some(false)
46 && !wire::has_token(&head.fields, "connection", b"close")
47 && (head.version.unwrap_or(Version::Http11) == Version::Http11
48 || head.persistent == Some(true)
49 || wire::has_token(&head.fields, "connection", b"keep-alive"))
50}
51
52impl Exchange {
53 pub(crate) fn new(head: &RequestMetadata, trailer_options: Vec<Vec<u8>>) -> Self {
54 Self {
56 facts: ExchangeFacts {
57 version: head.version.unwrap_or(Version::Http11),
58 head: head.method == Method::HEAD,
59 connect: head.method == Method::CONNECT,
60 persistent: request_persistent(head),
61 expect_continue: wire::has_token(&head.fields, "expect", b"100-continue"),
62 },
63 state: Some(Arc::new(ExchangeState {
66 trailer_options,
67 ..Default::default()
68 })),
69 }
70 }
71 fn from_request(head: &RequestMetadata, framing: wire::RequestFraming) -> Self {
72 let facts = ExchangeFacts {
73 version: head.version.unwrap_or(Version::Http11),
74 head: head.method == Method::HEAD,
75 connect: head.method == Method::CONNECT,
76 persistent: framing.persistent,
77 expect_continue: framing.expect_continue,
78 };
79 let mutable = facts.connect
82 || framing.upgrade.is_some()
83 || framing.chunked
84 || framing.length.is_some_and(|n| n != 0);
85 Self {
86 facts,
87 state: mutable.then(|| {
88 Arc::new(ExchangeState {
89 upgrade: framing.upgrade,
90 trailer_options: framing.trailer_options,
91 ..Default::default()
92 })
93 }),
94 }
95 }
96
97 fn filter_trailers(&self, fields: &mut Fields) {
98 let state = self
99 .state
100 .as_ref()
101 .expect("chunked input retains shared state");
102 fields.retain(|field| {
103 !state
104 .trailer_options
105 .iter()
106 .any(|option| field.name().as_bytes().eq_ignore_ascii_case(option))
107 });
108 }
109
110 pub fn version(&self) -> Version {
111 self.facts.version
112 }
113 pub fn is_head(&self) -> bool {
114 self.facts.head
115 }
116 pub fn is_connect(&self) -> bool {
117 self.facts.connect
118 }
119 pub fn expects_continue(&self) -> bool {
120 self.facts.expect_continue
121 }
122 pub(crate) fn has_upgrade(&self) -> bool {
123 self.state
124 .as_ref()
125 .is_some_and(|state| state.upgrade.is_some())
126 }
127 pub(crate) fn accepts_upgrade(&self, protocol: &[u8]) -> bool {
128 self.state
129 .as_ref()
130 .and_then(|state| state.upgrade.as_ref())
131 .is_some_and(|offers| {
132 offers
133 .split(|byte| *byte == b',')
134 .any(|offer| wire::trim(offer).eq_ignore_ascii_case(protocol))
135 })
136 }
137 pub fn is_complete(&self) -> bool {
138 self.state
139 .as_ref()
140 .is_none_or(|state| state.complete.load(Ordering::Acquire))
141 }
142 pub fn is_tunnel(&self) -> bool {
143 self.state
144 .as_ref()
145 .is_some_and(|state| state.tunnel.load(Ordering::Acquire))
146 }
147 pub fn is_abandoned(&self) -> bool {
149 self.state
150 .as_ref()
151 .is_some_and(|state| state.abandoned.load(Ordering::Acquire))
152 }
153 pub fn is_persistent(&self) -> bool {
154 self.facts.persistent
155 }
156 pub(crate) fn switch(&self) {
157 self.state
158 .as_ref()
159 .expect("switches retain shared state")
160 .tunnel
161 .store(true, Ordering::Release);
162 }
163 fn set_complete(&self, complete: bool) {
164 if let Some(state) = &self.state {
165 state.complete.store(complete, Ordering::Release);
166 }
167 }
168 fn abandon(&self) {
169 self.state
171 .as_ref()
172 .expect("incomplete input retains shared state")
173 .abandoned
174 .store(true, Ordering::Release);
175 }
176}
177
178pub struct RequestReader<R> {
182 reader: R,
183 buffer: Vec<u8>,
184 start: usize,
185 end: usize,
186 pub(crate) options: Options,
187 pub(crate) poisoned: bool,
188 pub(crate) previous: Option<Exchange>,
189}
190
191impl<R: ReadableStream> RequestReader<R> {
192 pub fn new(reader: R, options: Options) -> Result<Self, Error> {
193 options.validate()?;
194 Ok(Self {
195 reader,
196 buffer: vec![0; options.read_size],
197 start: 0,
198 end: 0,
199 options,
200 poisoned: false,
201 previous: None,
202 })
203 }
204
205 pub fn is_poisoned(&self) -> bool {
207 self.poisoned
208 }
209
210 pub fn is_reusable(&self) -> bool {
211 !self.poisoned
212 && self
213 .previous
214 .as_ref()
215 .is_none_or(|x| x.is_complete() && !x.is_tunnel() && !x.is_abandoned())
216 }
217
218 fn available(&self) -> &[u8] {
219 &self.buffer[self.start..self.end]
220 }
221
222 async fn fill(&mut self, cancellation: &Cancellation) -> Result<bool, Error> {
223 let spare = self.spare();
224 let n = self
225 .reader
226 .read_into(&mut self.buffer[spare], cancellation)
227 .await?;
228 self.end += n;
229 Ok(n != 0)
230 }
231
232 fn spare(&mut self) -> std::ops::Range<usize> {
235 if self.start != 0 {
236 self.buffer.copy_within(self.start..self.end, 0);
237 self.end -= self.start;
238 self.start = 0;
239 }
240 let limit = self.end + self.options.read_size;
242 if self.buffer.len() < limit {
243 self.buffer.resize(limit, 0);
246 }
247 self.end..limit
248 }
249
250 pub(crate) fn probe_idle(&mut self) -> bool {
259 if !self.is_reusable() {
260 return false;
261 }
262 if self.previous.is_none() {
263 return true;
264 }
265 if self.available().is_empty() {
266 let spare = self.spare();
267 match self.reader.try_read_into(&mut self.buffer[spare]) {
268 Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => return true,
269 Ok(n) => self.end += n,
270 Err(_) => {}
271 }
272 }
273 self.poisoned = true;
274 false
275 }
276
277 fn take(&mut self, n: usize) -> Vec<u8> {
278 let data = self.available()[..n].to_vec();
279 self.start += n;
280 data
281 }
282
283 pub(crate) async fn read_section<T>(
288 &mut self,
289 cancellation: &Cancellation,
290 limit: &'static str,
291 parse: impl Fn(&[u8]) -> Result<Option<(T, usize)>, Error>,
292 ) -> Result<Option<T>, Error> {
293 let max_head_bytes = self.options.max_head_bytes;
294 let mut scanned = None;
295 loop {
296 let available = self.available();
297 let window = &available[..available.len().min(max_head_bytes)];
298 let terminated = window.starts_with(b"\r\n")
300 || scanned.is_none_or(|scanned: usize| {
301 window[scanned..].windows(4).any(|w| w == b"\r\n\r\n")
302 });
303 if terminated && !window.is_empty() {
304 if let Some((section, n)) = parse(window)? {
305 self.start += n;
306 return Ok(Some(section));
307 }
308 }
309 if available.len() >= max_head_bytes {
310 return Err(Error::Limit(limit));
311 }
312 if !available.is_empty() {
313 scanned = Some(available.len().saturating_sub(3));
314 }
315 if !self.fill(cancellation).await? {
316 if self.available().is_empty() {
317 return Ok(None);
318 }
319 return Err(Error::Truncated);
320 }
321 }
322 }
323
324 pub async fn read_request(
326 &mut self,
327 cancellation: &Cancellation,
328 ) -> Result<Option<(Request<IncomingBody<'_, R>>, Exchange)>, Error> {
329 cancellation.check()?;
330 if !self.is_reusable() {
331 return Err(Error::Invalid("request input is not reusable"));
332 }
333 self.poisoned = true;
338 let max_head_bytes = self.options.max_head_bytes;
339 let mut scanned = None;
340 let (head, facts) = loop {
341 let available = self.available();
342 let window = &available[..available.len().min(max_head_bytes)];
343 let terminated = scanned.is_none_or(|scanned: usize| {
344 window[scanned..].windows(4).any(|w| w == b"\r\n\r\n")
345 });
346 if terminated && !window.is_empty() {
347 if let Some((head, facts, n)) = wire::request(window, self.options.max_fields)? {
348 self.start += n;
349 break (head, facts);
350 }
351 }
352 if available.len() >= max_head_bytes {
353 return Err(Error::Limit("message head"));
354 }
355 if !available.is_empty() {
356 scanned = Some(available.len().saturating_sub(3));
357 }
358 if !self.fill(cancellation).await? {
359 if self.available().is_empty() {
360 self.poisoned = false;
361 return Ok(None);
362 }
363 return Err(Error::Truncated);
364 }
365 };
366 let length = facts.length;
367 let chunked = facts.chunked;
368 if chunked && head.version != Some(Version::Http11) {
369 return Err(Error::Invalid("HTTP/1.0 transfer-encoding"));
370 }
371 let framing = if chunked {
372 Framing::CHUNKED
373 } else {
374 Framing::Fixed(length.unwrap_or(0))
375 };
376 let exchange = Exchange::from_request(&head, facts);
377 self.previous = Some(exchange.clone());
378 self.poisoned = false;
379 let body = IncomingBody::new(
380 self,
381 exchange.clone(),
382 framing,
383 if chunked {
384 None
385 } else {
386 Some(length.unwrap_or(0))
387 },
388 false,
389 false,
390 );
391 Ok(Some((Request::from_parts(head, body), exchange)))
392 }
393
394 pub async fn close(&mut self) -> io::Result<()> {
396 self.poisoned = true;
397 self.reader.close().await
398 }
399
400 async fn line(&mut self, limit: usize, cancellation: &Cancellation) -> Result<Vec<u8>, Error> {
401 loop {
402 if let Some(end) = self.available().windows(2).position(|w| w == b"\r\n") {
403 if end + 2 > limit {
404 return Err(Error::Limit("line length"));
405 }
406 return Ok(self.take(end + 2));
407 }
408 if self.available().len() >= limit {
409 return Err(Error::Limit("line length"));
410 }
411 if !self.fill(cancellation).await? {
412 return Err(Error::Truncated);
413 }
414 }
415 }
416
417 async fn trailers(&mut self, cancellation: &Cancellation) -> Result<Fields, Error> {
418 let max_fields = self.options.max_fields;
419 let fields = self
420 .read_section(cancellation, "trailer section", |bytes| {
421 wire::trailers(bytes, max_fields)
422 })
423 .await?
424 .ok_or(Error::Truncated)?;
425 crate::output::validate_trailer_fields(fields.pairs())?;
426 Ok(fields)
427 }
428}
429
430pub(crate) enum Framing {
431 Fixed(u64),
432 Chunked {
436 remaining: u64,
437 delimiter: bool,
438 trailers: bool,
439 },
440}
441
442impl Framing {
443 pub(crate) const CHUNKED: Self = Self::Chunked {
444 remaining: 0,
445 delimiter: false,
446 trailers: false,
447 };
448}
449
450pub struct IncomingBody<'a, R> {
459 input: &'a mut RequestReader<R>,
460 exchange: Exchange,
461 framing: Framing,
462 length: Option<u64>,
463 complete: bool,
464 closed: bool,
465 raw: bool,
466 forbid_content: bool,
467}
468
469impl<'a, R: ReadableStream> IncomingBody<'a, R> {
470 pub(crate) fn new(
471 input: &'a mut RequestReader<R>,
472 exchange: Exchange,
473 framing: Framing,
474 length: Option<u64>,
475 raw: bool,
476 forbid_content: bool,
477 ) -> Self {
478 let complete = !raw && matches!(framing, Framing::Fixed(0));
479 exchange.set_complete(complete);
480 Self {
481 input,
482 exchange,
483 framing,
484 length,
485 complete,
486 closed: false,
487 raw,
488 forbid_content,
489 }
490 }
491 async fn read_inner(
492 &mut self,
493 cancellation: &Cancellation,
494 ) -> Result<BodyFrame<Vec<u8>>, Error> {
495 if self.exchange.is_tunnel() && !self.raw {
496 self.raw = true;
497 self.complete = false;
498 }
499 if self.complete || self.closed {
500 return Ok(BodyFrame::End);
501 }
502 loop {
503 if self.raw {
504 if self.input.available().is_empty() && !self.input.fill(cancellation).await? {
505 self.complete = true;
506 return Ok(BodyFrame::End);
507 }
508 return Ok(BodyFrame::Data {
509 data: self.input.take(self.input.available().len()),
510 end: false,
511 });
512 }
513 match &mut self.framing {
514 Framing::Fixed(remaining) => {
515 if self.input.available().is_empty() && !self.input.fill(cancellation).await? {
516 return Err(Error::Truncated);
517 }
518 let n = (*remaining).min(self.input.available().len() as u64) as usize;
519 *remaining -= n as u64;
520 let data = self.input.take(n);
521 if *remaining == 0 {
522 self.complete = true;
523 }
524 return Ok(BodyFrame::Data {
525 data,
526 end: self.complete,
527 });
528 }
529 Framing::Chunked {
530 remaining,
531 delimiter,
532 trailers,
533 } => {
534 if *remaining != 0 {
535 if self.input.available().is_empty()
536 && !self.input.fill(cancellation).await?
537 {
538 return Err(Error::Truncated);
539 }
540 let n = (*remaining).min(self.input.available().len() as u64) as usize;
541 *remaining -= n as u64;
542 return Ok(BodyFrame::Data {
543 data: self.input.take(n),
544 end: false,
545 });
546 }
547 if !*trailers {
551 if *delimiter {
552 while self.input.available().len() < 2 {
553 if !self.input.fill(cancellation).await? {
554 return Err(Error::Truncated);
555 }
556 }
557 if !self.input.available().starts_with(b"\r\n") {
558 return Err(Error::Invalid("missing chunk delimiter"));
559 }
560 self.input.start += 2;
561 *delimiter = false;
562 }
563 let line = self
564 .input
565 .line(self.input.options.max_chunk_line_bytes, cancellation)
566 .await?;
567 let size = chunk_size(&line)?;
568 if size != 0 {
569 *remaining = size;
570 *delimiter = true;
571 continue;
572 }
573 *trailers = true;
574 }
575 let mut fields = self.input.trailers(cancellation).await?;
576 self.exchange.filter_trailers(&mut fields);
577 self.complete = true;
578 return Ok(BodyFrame::Headers { fields, end: true });
579 }
580 }
581 }
582 }
583}
584
585impl<R: ReadableStream> ReadableBody for IncomingBody<'_, R> {
586 type Data = Vec<u8>;
587 type Error = Error;
588 fn may_have_trailers(&self) -> bool {
589 !self.raw && !self.exchange.is_tunnel() && matches!(self.framing, Framing::Chunked { .. })
590 }
591 fn is_ready(&self) -> bool {
592 !self.input.available().is_empty() || self.complete || self.closed
593 }
594 fn is_empty(&self) -> bool {
595 (self.complete && !self.exchange.is_tunnel()) || self.closed
596 }
597 fn length(&self) -> Option<u64> {
598 if self.exchange.is_tunnel() {
599 None
600 } else {
601 self.length
602 }
603 }
604 fn close(&mut self, _: Option<Error>) {
605 if !self.complete {
606 self.exchange.abandon();
607 }
608 self.closed = true;
609 }
610 async fn read(&mut self, cancellation: &Cancellation) -> Result<BodyFrame<Vec<u8>>, Error> {
611 cancellation.check()?;
612 if self.input.poisoned {
613 return Err(Error::Invalid("previous body read failed"));
614 }
615 let result = self.read_inner(cancellation).await.and_then(|chunk| {
619 if self.forbid_content
620 && matches!(&chunk, BodyFrame::Data { data, .. } if !data.is_empty())
621 {
622 Err(Error::Invalid("content on 205"))
623 } else {
624 Ok(chunk)
625 }
626 });
627 match &result {
628 Ok(_) => self.exchange.set_complete(self.complete),
629 Err(Error::Cancelled) => {}
630 Err(_) => self.input.poisoned = true,
631 }
632 result
633 }
634}
635
636impl<R> Drop for IncomingBody<'_, R> {
637 fn drop(&mut self) {
638 if !self.complete {
639 self.exchange.abandon();
640 }
641 }
642}
643
644fn chunk_size(line: &[u8]) -> Result<u64, Error> {
647 let bytes = &line[..line.len() - 2];
648 let end = bytes
649 .iter()
650 .position(|b| !b.is_ascii_hexdigit())
651 .unwrap_or(bytes.len());
652 if end == 0 {
653 return Err(Error::Invalid("invalid chunk size"));
654 }
655 let size = u64::from_str_radix(std::str::from_utf8(&bytes[..end]).unwrap(), 16)
656 .map_err(|_| Error::Invalid("chunk size overflow"))?;
657 let mut rest = &bytes[end..];
658 while !rest.is_empty() {
659 rest = wire::trim(rest);
660 if rest.first() != Some(&b';') {
661 return Err(Error::Invalid("invalid chunk extension"));
662 }
663 rest = &rest[1..];
664 rest = wire::trim(rest);
665 let end = rest
666 .iter()
667 .position(|b| !wire::token(&[*b]))
668 .unwrap_or(rest.len());
669 if end == 0 {
670 return Err(Error::Invalid("missing chunk extension name"));
671 }
672 rest = wire::trim(&rest[end..]);
673 if rest.first() == Some(&b'=') {
674 rest = wire::trim(&rest[1..]);
675 if rest.first() == Some(&b'"') {
676 rest = &rest[1..];
677 loop {
678 let b = *rest
679 .first()
680 .ok_or(Error::Invalid("unterminated chunk extension"))?;
681 rest = &rest[1..];
682 if b == b'"' {
683 break;
684 }
685 if b == b'\\' {
686 let b = *rest
687 .first()
688 .ok_or(Error::Invalid("invalid quoted escape"))?;
689 rest = &rest[1..];
690 if b < 32 && b != b'\t' || b == 127 {
691 return Err(Error::Invalid("invalid quoted escape"));
692 }
693 } else if b < 32 && b != b'\t' || b == 127 {
694 return Err(Error::Invalid("invalid chunk extension"));
695 }
696 }
697 } else {
698 let end = rest
699 .iter()
700 .position(|b| !wire::token(&[*b]))
701 .unwrap_or(rest.len());
702 if end == 0 {
703 return Err(Error::Invalid("missing chunk extension value"));
704 }
705 rest = &rest[end..];
706 }
707 }
708 }
709 Ok(size)
710}
711
712#[cfg(test)]
713mod tests {
714 use super::chunk_size;
715
716 #[test]
717 fn chunk_lines_reject_stray_line_controls() {
718 assert_eq!(chunk_size(b"1a;name=\"v\\\"\";flag\r\n").unwrap(), 26);
719 for line in [
720 &b"1\r;x\r\n"[..],
721 b"1\n\r\n",
722 b"1;x\r\r\n",
723 b"1;x=\r\r\n",
724 b"1;x=\"\r\"\r\n",
725 b"1;x=\"\\\n\"\r\n",
726 b"1;x\0\r\n",
727 ] {
728 assert!(chunk_size(line).is_err(), "{line:?}");
729 }
730 }
731
732 #[test]
733 fn chunk_lines_accept_token_extension_values_and_reject_malformed_ones() {
734 assert_eq!(chunk_size(b"ff ; name = value ; flag\r\n").unwrap(), 255);
735 for (line, reason) in [
736 (&b"11111111111111111\r\n"[..], "chunk size overflow"),
737 (b"1;x=\"open\r\n", "unterminated chunk extension"),
738 (b"1;x=\"\\\r\n", "invalid quoted escape"),
739 ] {
740 assert!(
741 matches!(chunk_size(line), Err(crate::Error::Invalid(r)) if r == reason),
742 "{line:?}"
743 );
744 }
745 }
746}