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_reusable(&self) -> bool {
206 !self.poisoned
207 && self
208 .previous
209 .as_ref()
210 .is_none_or(|x| x.is_complete() && !x.is_tunnel() && !x.is_abandoned())
211 }
212
213 fn available(&self) -> &[u8] {
214 &self.buffer[self.start..self.end]
215 }
216
217 async fn fill(&mut self, cancellation: &Cancellation) -> Result<bool, Error> {
218 if self.start != 0 {
219 self.buffer.copy_within(self.start..self.end, 0);
220 self.end -= self.start;
221 self.start = 0;
222 }
223 let limit = self.end + self.options.read_size;
225 if self.buffer.len() < limit {
226 self.buffer.resize(limit, 0);
229 }
230 let n = self
231 .reader
232 .read_into(&mut self.buffer[self.end..limit], cancellation)
233 .await?;
234 self.end += n;
235 Ok(n != 0)
236 }
237
238 fn take(&mut self, n: usize) -> Vec<u8> {
239 let data = self.available()[..n].to_vec();
240 self.start += n;
241 data
242 }
243
244 pub(crate) async fn read_section<T>(
249 &mut self,
250 cancellation: &Cancellation,
251 limit: &'static str,
252 parse: impl Fn(&[u8]) -> Result<Option<(T, usize)>, Error>,
253 ) -> Result<Option<T>, Error> {
254 let max_head_bytes = self.options.max_head_bytes;
255 let mut scanned = None;
256 loop {
257 let available = self.available();
258 let window = &available[..available.len().min(max_head_bytes)];
259 let terminated = window.starts_with(b"\r\n")
261 || scanned.is_none_or(|scanned: usize| {
262 window[scanned..].windows(4).any(|w| w == b"\r\n\r\n")
263 });
264 if terminated && !window.is_empty() {
265 if let Some((section, n)) = parse(window)? {
266 self.start += n;
267 return Ok(Some(section));
268 }
269 }
270 if available.len() >= max_head_bytes {
271 return Err(Error::Limit(limit));
272 }
273 if !available.is_empty() {
274 scanned = Some(available.len().saturating_sub(3));
275 }
276 if !self.fill(cancellation).await? {
277 if self.available().is_empty() {
278 return Ok(None);
279 }
280 return Err(Error::Truncated);
281 }
282 }
283 }
284
285 pub async fn read_request(
287 &mut self,
288 cancellation: &Cancellation,
289 ) -> Result<Option<(Request<IncomingBody<'_, R>>, Exchange)>, Error> {
290 cancellation.check()?;
291 if !self.is_reusable() {
292 return Err(Error::Invalid("request input is not reusable"));
293 }
294 self.poisoned = true;
299 let max_head_bytes = self.options.max_head_bytes;
300 let mut scanned = None;
301 let (head, facts) = loop {
302 let available = self.available();
303 let window = &available[..available.len().min(max_head_bytes)];
304 let terminated = scanned.is_none_or(|scanned: usize| {
305 window[scanned..].windows(4).any(|w| w == b"\r\n\r\n")
306 });
307 if terminated && !window.is_empty() {
308 if let Some((head, facts, n)) = wire::request(window, self.options.max_fields)? {
309 self.start += n;
310 break (head, facts);
311 }
312 }
313 if available.len() >= max_head_bytes {
314 return Err(Error::Limit("message head"));
315 }
316 if !available.is_empty() {
317 scanned = Some(available.len().saturating_sub(3));
318 }
319 if !self.fill(cancellation).await? {
320 if self.available().is_empty() {
321 self.poisoned = false;
322 return Ok(None);
323 }
324 return Err(Error::Truncated);
325 }
326 };
327 let length = facts.length;
328 let chunked = facts.chunked;
329 if chunked && head.version != Some(Version::Http11) {
330 return Err(Error::Invalid("HTTP/1.0 transfer-encoding"));
331 }
332 let framing = if chunked {
333 Framing::CHUNKED
334 } else {
335 Framing::Fixed(length.unwrap_or(0))
336 };
337 let exchange = Exchange::from_request(&head, facts);
338 self.previous = Some(exchange.clone());
339 self.poisoned = false;
340 let body = IncomingBody::new(
341 self,
342 exchange.clone(),
343 framing,
344 if chunked {
345 None
346 } else {
347 Some(length.unwrap_or(0))
348 },
349 false,
350 false,
351 );
352 Ok(Some((Request::from_parts(head, body), exchange)))
353 }
354
355 pub async fn close(&mut self) -> io::Result<()> {
357 self.poisoned = true;
358 self.reader.close().await
359 }
360
361 async fn line(&mut self, limit: usize, cancellation: &Cancellation) -> Result<Vec<u8>, Error> {
362 loop {
363 if let Some(end) = self.available().windows(2).position(|w| w == b"\r\n") {
364 if end + 2 > limit {
365 return Err(Error::Limit("line length"));
366 }
367 return Ok(self.take(end + 2));
368 }
369 if self.available().len() >= limit {
370 return Err(Error::Limit("line length"));
371 }
372 if !self.fill(cancellation).await? {
373 return Err(Error::Truncated);
374 }
375 }
376 }
377
378 async fn trailers(&mut self, cancellation: &Cancellation) -> Result<Fields, Error> {
379 let max_fields = self.options.max_fields;
380 let fields = self
381 .read_section(cancellation, "trailer section", |bytes| {
382 wire::trailers(bytes, max_fields)
383 })
384 .await?
385 .ok_or(Error::Truncated)?;
386 crate::output::validate_trailer_fields(fields.pairs())?;
387 Ok(fields)
388 }
389}
390
391pub(crate) enum Framing {
392 Fixed(u64),
393 Chunked {
397 remaining: u64,
398 delimiter: bool,
399 trailers: bool,
400 },
401}
402
403impl Framing {
404 pub(crate) const CHUNKED: Self = Self::Chunked {
405 remaining: 0,
406 delimiter: false,
407 trailers: false,
408 };
409}
410
411pub struct IncomingBody<'a, R> {
420 input: &'a mut RequestReader<R>,
421 exchange: Exchange,
422 framing: Framing,
423 length: Option<u64>,
424 complete: bool,
425 closed: bool,
426 raw: bool,
427 forbid_content: bool,
428}
429
430impl<'a, R: ReadableStream> IncomingBody<'a, R> {
431 pub(crate) fn new(
432 input: &'a mut RequestReader<R>,
433 exchange: Exchange,
434 framing: Framing,
435 length: Option<u64>,
436 raw: bool,
437 forbid_content: bool,
438 ) -> Self {
439 let complete = !raw && matches!(framing, Framing::Fixed(0));
440 exchange.set_complete(complete);
441 Self {
442 input,
443 exchange,
444 framing,
445 length,
446 complete,
447 closed: false,
448 raw,
449 forbid_content,
450 }
451 }
452 async fn read_inner(
453 &mut self,
454 cancellation: &Cancellation,
455 ) -> Result<BodyFrame<Vec<u8>>, Error> {
456 if self.exchange.is_tunnel() && !self.raw {
457 self.raw = true;
458 self.complete = false;
459 }
460 if self.complete || self.closed {
461 return Ok(BodyFrame::End);
462 }
463 loop {
464 if self.raw {
465 if self.input.available().is_empty() && !self.input.fill(cancellation).await? {
466 self.complete = true;
467 return Ok(BodyFrame::End);
468 }
469 return Ok(BodyFrame::Data {
470 data: self.input.take(self.input.available().len()),
471 end: false,
472 });
473 }
474 match &mut self.framing {
475 Framing::Fixed(remaining) => {
476 if self.input.available().is_empty() && !self.input.fill(cancellation).await? {
477 return Err(Error::Truncated);
478 }
479 let n = (*remaining).min(self.input.available().len() as u64) as usize;
480 *remaining -= n as u64;
481 let data = self.input.take(n);
482 if *remaining == 0 {
483 self.complete = true;
484 }
485 return Ok(BodyFrame::Data {
486 data,
487 end: self.complete,
488 });
489 }
490 Framing::Chunked {
491 remaining,
492 delimiter,
493 trailers,
494 } => {
495 if *remaining != 0 {
496 if self.input.available().is_empty()
497 && !self.input.fill(cancellation).await?
498 {
499 return Err(Error::Truncated);
500 }
501 let n = (*remaining).min(self.input.available().len() as u64) as usize;
502 *remaining -= n as u64;
503 return Ok(BodyFrame::Data {
504 data: self.input.take(n),
505 end: false,
506 });
507 }
508 if !*trailers {
512 if *delimiter {
513 while self.input.available().len() < 2 {
514 if !self.input.fill(cancellation).await? {
515 return Err(Error::Truncated);
516 }
517 }
518 if !self.input.available().starts_with(b"\r\n") {
519 return Err(Error::Invalid("missing chunk delimiter"));
520 }
521 self.input.start += 2;
522 *delimiter = false;
523 }
524 let line = self
525 .input
526 .line(self.input.options.max_chunk_line_bytes, cancellation)
527 .await?;
528 let size = chunk_size(&line)?;
529 if size != 0 {
530 *remaining = size;
531 *delimiter = true;
532 continue;
533 }
534 *trailers = true;
535 }
536 let mut fields = self.input.trailers(cancellation).await?;
537 self.exchange.filter_trailers(&mut fields);
538 self.complete = true;
539 return Ok(BodyFrame::Headers { fields, end: true });
540 }
541 }
542 }
543 }
544}
545
546impl<R: ReadableStream> ReadableBody for IncomingBody<'_, R> {
547 type Data = Vec<u8>;
548 type Error = Error;
549 fn may_have_trailers(&self) -> bool {
550 !self.raw && !self.exchange.is_tunnel() && matches!(self.framing, Framing::Chunked { .. })
551 }
552 fn is_ready(&self) -> bool {
553 !self.input.available().is_empty() || self.complete || self.closed
554 }
555 fn is_empty(&self) -> bool {
556 (self.complete && !self.exchange.is_tunnel()) || self.closed
557 }
558 fn length(&self) -> Option<u64> {
559 if self.exchange.is_tunnel() {
560 None
561 } else {
562 self.length
563 }
564 }
565 fn close(&mut self, _: Option<Error>) {
566 if !self.complete {
567 self.exchange.abandon();
568 }
569 self.closed = true;
570 }
571 async fn read(&mut self, cancellation: &Cancellation) -> Result<BodyFrame<Vec<u8>>, Error> {
572 cancellation.check()?;
573 if self.input.poisoned {
574 return Err(Error::Invalid("previous body read failed"));
575 }
576 let result = self.read_inner(cancellation).await.and_then(|chunk| {
580 if self.forbid_content
581 && matches!(&chunk, BodyFrame::Data { data, .. } if !data.is_empty())
582 {
583 Err(Error::Invalid("content on 205"))
584 } else {
585 Ok(chunk)
586 }
587 });
588 match &result {
589 Ok(_) => self.exchange.set_complete(self.complete),
590 Err(Error::Cancelled) => {}
591 Err(_) => self.input.poisoned = true,
592 }
593 result
594 }
595}
596
597impl<R> Drop for IncomingBody<'_, R> {
598 fn drop(&mut self) {
599 if !self.complete {
600 self.exchange.abandon();
601 }
602 }
603}
604
605fn chunk_size(line: &[u8]) -> Result<u64, Error> {
608 let bytes = &line[..line.len() - 2];
609 let end = bytes
610 .iter()
611 .position(|b| !b.is_ascii_hexdigit())
612 .unwrap_or(bytes.len());
613 if end == 0 {
614 return Err(Error::Invalid("invalid chunk size"));
615 }
616 let size = u64::from_str_radix(std::str::from_utf8(&bytes[..end]).unwrap(), 16)
617 .map_err(|_| Error::Invalid("chunk size overflow"))?;
618 let mut rest = &bytes[end..];
619 while !rest.is_empty() {
620 rest = wire::trim(rest);
621 if rest.first() != Some(&b';') {
622 return Err(Error::Invalid("invalid chunk extension"));
623 }
624 rest = &rest[1..];
625 rest = wire::trim(rest);
626 let end = rest
627 .iter()
628 .position(|b| !wire::token(&[*b]))
629 .unwrap_or(rest.len());
630 if end == 0 {
631 return Err(Error::Invalid("missing chunk extension name"));
632 }
633 rest = wire::trim(&rest[end..]);
634 if rest.first() == Some(&b'=') {
635 rest = wire::trim(&rest[1..]);
636 if rest.first() == Some(&b'"') {
637 rest = &rest[1..];
638 loop {
639 let b = *rest
640 .first()
641 .ok_or(Error::Invalid("unterminated chunk extension"))?;
642 rest = &rest[1..];
643 if b == b'"' {
644 break;
645 }
646 if b == b'\\' {
647 let b = *rest
648 .first()
649 .ok_or(Error::Invalid("invalid quoted escape"))?;
650 rest = &rest[1..];
651 if b < 32 && b != b'\t' || b == 127 {
652 return Err(Error::Invalid("invalid quoted escape"));
653 }
654 } else if b < 32 && b != b'\t' || b == 127 {
655 return Err(Error::Invalid("invalid chunk extension"));
656 }
657 }
658 } else {
659 let end = rest
660 .iter()
661 .position(|b| !wire::token(&[*b]))
662 .unwrap_or(rest.len());
663 if end == 0 {
664 return Err(Error::Invalid("missing chunk extension value"));
665 }
666 rest = &rest[end..];
667 }
668 }
669 }
670 Ok(size)
671}
672
673#[cfg(test)]
674mod tests {
675 use super::chunk_size;
676
677 #[test]
678 fn chunk_lines_reject_stray_line_controls() {
679 assert_eq!(chunk_size(b"1a;name=\"v\\\"\";flag\r\n").unwrap(), 26);
680 for line in [
681 &b"1\r;x\r\n"[..],
682 b"1\n\r\n",
683 b"1;x\r\r\n",
684 b"1;x=\r\r\n",
685 b"1;x=\"\r\"\r\n",
686 b"1;x=\"\\\n\"\r\n",
687 b"1;x\0\r\n",
688 ] {
689 assert!(chunk_size(line).is_err(), "{line:?}");
690 }
691 }
692
693 #[test]
694 fn chunk_lines_accept_token_extension_values_and_reject_malformed_ones() {
695 assert_eq!(chunk_size(b"ff ; name = value ; flag\r\n").unwrap(), 255);
696 for (line, reason) in [
697 (&b"11111111111111111\r\n"[..], "chunk size overflow"),
698 (b"1;x=\"open\r\n", "unterminated chunk extension"),
699 (b"1;x=\"\\\r\n", "invalid quoted escape"),
700 ] {
701 assert!(
702 matches!(chunk_size(line), Err(crate::Error::Invalid(r)) if r == reason),
703 "{line:?}"
704 );
705 }
706 }
707}