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