1use std::{
2 cell::RefCell,
3 cmp, fmt,
4 future::poll_fn,
5 mem,
6 pin::Pin,
7 rc::Rc,
8 task::{ready, Context, Poll},
9};
10
11use actix_web::{
12 error::PayloadError,
13 http::header::{self, ContentDisposition, HeaderMap},
14 web::{Bytes, BytesMut},
15};
16use derive_more::{Display, Error};
17use futures_core::Stream;
18use mime::Mime;
19
20use crate::{
21 error::Error,
22 payload::{PayloadBuffer, PayloadRef},
23 safety::Safety,
24};
25
26#[derive(Debug, Display, Error)]
28#[display("size limit exceeded while collecting field data")]
29#[non_exhaustive]
30pub struct LimitExceeded;
31
32pub struct Field {
34 content_type: Option<Mime>,
36
37 content_disposition: Option<ContentDisposition>,
39
40 pub(crate) form_field_name: String,
47
48 headers: HeaderMap,
50
51 safety: Safety,
52 inner: Rc<RefCell<InnerField>>,
53}
54
55impl Field {
56 pub(crate) fn new(
57 content_type: Option<Mime>,
58 content_disposition: Option<ContentDisposition>,
59 form_field_name: Option<String>,
60 headers: HeaderMap,
61 safety: Safety,
62 inner: Rc<RefCell<InnerField>>,
63 ) -> Self {
64 Field {
65 content_type,
66 content_disposition,
67 form_field_name: form_field_name.unwrap_or_default(),
68 headers,
69 inner,
70 safety,
71 }
72 }
73
74 pub fn headers(&self) -> &HeaderMap {
76 &self.headers
77 }
78
79 pub fn content_type(&self) -> Option<&Mime> {
85 self.content_type.as_ref()
86 }
87
88 pub fn content_disposition(&self) -> Option<&ContentDisposition> {
105 self.content_disposition.as_ref()
106 }
107
108 pub fn name(&self) -> Option<&str> {
113 self.content_disposition()?.get_name()
114 }
115
116 pub async fn bytes(&mut self, limit: usize) -> Result<Result<Bytes, Error>, LimitExceeded> {
127 const INITIAL_ALLOC_BYTES: usize = 2 * 1024;
129
130 let mut exceeded_limit = false;
131 let mut buf = BytesMut::with_capacity(INITIAL_ALLOC_BYTES);
132
133 let mut field = Pin::new(self);
134
135 match poll_fn(|cx| loop {
136 match ready!(field.as_mut().poll_next(cx)) {
137 Some(Ok(_chunk)) if exceeded_limit => {}
139
140 Some(Ok(chunk)) if buf.len() + chunk.len() > limit => {
142 exceeded_limit = true;
143 let _ = mem::take(&mut buf);
145 }
146
147 Some(Ok(chunk)) => buf.extend_from_slice(&chunk),
148
149 None => return Poll::Ready(Ok(())),
150 Some(Err(err)) => return Poll::Ready(Err(err)),
151 }
152 })
153 .await
154 {
155 Err(err) => Ok(Err(err)),
157
158 Ok(()) if exceeded_limit => Err(LimitExceeded),
160
161 Ok(()) => Ok(Ok(buf.freeze())),
163 }
164 }
165}
166
167impl Stream for Field {
168 type Item = Result<Bytes, Error>;
169
170 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
171 let this = self.get_mut();
172 let mut inner = this.inner.borrow_mut();
173
174 if let Some(mut buffer) = inner
175 .payload
176 .as_ref()
177 .expect("Field should not be polled after completion")
178 .get_mut(&this.safety)
179 {
180 buffer.poll_stream(cx)?;
182 } else if !this.safety.is_clean() {
183 return Poll::Ready(Some(Err(Error::NotConsumed)));
185 } else {
186 return Poll::Pending;
187 }
188
189 inner.poll(&this.safety)
190 }
191}
192
193impl fmt::Debug for Field {
194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195 if let Some(ct) = &self.content_type {
196 writeln!(f, "\nField: {}", ct)?;
197 } else {
198 writeln!(f, "\nField:")?;
199 }
200 writeln!(f, " boundary: {}", self.inner.borrow().boundary)?;
201 writeln!(f, " headers:")?;
202 for (key, val) in self.headers.iter() {
203 writeln!(f, " {:?}: {:?}", key, val)?;
204 }
205 Ok(())
206 }
207}
208
209pub(crate) struct InnerField {
210 payload: Option<PayloadRef>,
212
213 boundary: String,
215
216 eof: bool,
218
219 length: Option<u64>,
221}
222
223impl InnerField {
224 pub(crate) fn new_in_rc(
225 payload: PayloadRef,
226 boundary: String,
227 headers: &HeaderMap,
228 ) -> Result<Rc<RefCell<InnerField>>, PayloadError> {
229 Self::new(payload, boundary, headers).map(|this| Rc::new(RefCell::new(this)))
230 }
231
232 pub(crate) fn new(
233 payload: PayloadRef,
234 boundary: String,
235 headers: &HeaderMap,
236 ) -> Result<InnerField, PayloadError> {
237 let len = if let Some(len) = headers.get(&header::CONTENT_LENGTH) {
238 match len.to_str().ok().and_then(|len| len.parse::<u64>().ok()) {
239 Some(len) => Some(len),
240 None => return Err(PayloadError::Incomplete(None)),
241 }
242 } else {
243 None
244 };
245
246 Ok(InnerField {
247 boundary,
248 payload: Some(payload),
249 eof: false,
250 length: len,
251 })
252 }
253
254 pub(crate) fn read_len(
258 payload: &mut PayloadBuffer,
259 size: &mut u64,
260 ) -> Poll<Option<Result<Bytes, Error>>> {
261 if *size == 0 {
262 Poll::Ready(None)
263 } else {
264 match payload.read_max(*size)? {
265 Some(mut chunk) => {
266 let len = cmp::min(chunk.len() as u64, *size);
267 *size -= len;
268 let ch = chunk.split_to(len as usize);
269 if !chunk.is_empty() {
270 payload.unprocessed(chunk);
271 }
272 Poll::Ready(Some(Ok(ch)))
273 }
274 None => {
275 if payload.eof && (*size != 0) {
276 Poll::Ready(Some(Err(Error::Incomplete)))
277 } else {
278 Poll::Pending
279 }
280 }
281 }
282 }
283 }
284
285 pub(crate) fn read_stream(
289 payload: &mut PayloadBuffer,
290 boundary: &str,
291 ) -> Poll<Option<Result<Bytes, Error>>> {
292 let mut pos = 0;
293
294 let len = payload.buf.len();
295
296 if len == 0 {
297 return if payload.eof {
298 Poll::Ready(Some(Err(Error::Incomplete)))
299 } else {
300 Poll::Pending
301 };
302 }
303
304 if len >= 4 && payload.buf[0] == b'\r' {
306 let b_len = if payload.buf.starts_with(b"\r\n") && &payload.buf[2..4] == b"--" {
307 Some(4)
308 } else if &payload.buf[1..3] == b"--" {
309 Some(3)
310 } else {
311 None
312 };
313
314 if let Some(b_len) = b_len {
315 let b_size = boundary.len() + b_len;
316 let available = len - b_len;
317 let check_len = cmp::min(available, boundary.len());
318
319 if payload.buf[b_len..b_len + check_len] == boundary.as_bytes()[..check_len] {
320 match (len >= b_size, payload.eof) {
321 (true, _) => return Poll::Ready(None),
323 (false, false) => return Poll::Pending,
325 (false, true) => return Poll::Ready(Some(Err(Error::Incomplete))),
327 }
328 }
329 }
330 }
331
332 loop {
333 return if let Some(idx) = memchr::memmem::find(&payload.buf[pos..], b"\r") {
334 let cur = pos + idx;
335
336 if cur + 4 > len {
338 if cur > 0 {
339 Poll::Ready(Some(Ok(payload.buf.split_to(cur).freeze())))
340 } else if payload.eof {
341 Poll::Ready(Some(Err(Error::Incomplete)))
342 } else {
343 Poll::Pending
344 }
345 } else {
346 if (&payload.buf[cur..cur + 2] == b"\r\n"
348 && &payload.buf[cur + 2..cur + 4] == b"--")
349 || (&payload.buf[cur..=cur] == b"\r"
350 && &payload.buf[cur + 1..cur + 3] == b"--")
351 {
352 if cur != 0 {
353 Poll::Ready(Some(Ok(payload.buf.split_to(cur).freeze())))
355 } else {
356 pos = cur + 1;
357 continue;
358 }
359 } else {
360 pos = cur + 1;
362 continue;
363 }
364 }
365 } else {
366 Poll::Ready(Some(Ok(payload.buf.split().freeze())))
367 };
368 }
369 }
370
371 pub(crate) fn poll(&mut self, safety: &Safety) -> Poll<Option<Result<Bytes, Error>>> {
372 if self.payload.is_none() {
373 return Poll::Ready(None);
374 }
375
376 let Some(mut payload) = self
377 .payload
378 .as_ref()
379 .expect("Field should not be polled after completion")
380 .get_mut(safety)
381 else {
382 return Poll::Pending;
383 };
384
385 if !self.eof {
386 let res = if let Some(ref mut len) = self.length {
387 Self::read_len(&mut payload, len)
388 } else {
389 Self::read_stream(&mut payload, &self.boundary)
390 };
391
392 match ready!(res) {
393 Some(Ok(bytes)) => return Poll::Ready(Some(Ok(bytes))),
394 Some(Err(err)) => return Poll::Ready(Some(Err(err))),
395 None => self.eof = true,
396 }
397 }
398
399 let result = match payload.readline() {
400 Ok(None) => Poll::Pending,
401 Ok(Some(line)) => {
402 if line.as_ref() != b"\r\n" {
403 log::warn!("multipart field did not read all the data or it is malformed");
404 }
405 Poll::Ready(None)
406 }
407 Err(err) => Poll::Ready(Some(Err(err))),
408 };
409
410 drop(payload);
411
412 if let Poll::Ready(None) = result {
413 let _ = self.payload.take();
415 }
416
417 result
418 }
419}
420
421#[cfg(test)]
422mod tests {
423 use actix_http::h1;
424 use futures_util::{stream, FutureExt as _, StreamExt as _};
425
426 use super::*;
427 use crate::Multipart;
428
429 fn create_double_request_with_header() -> (Bytes, HeaderMap) {
431 let bytes = Bytes::from(
432 "testasdadsad\r\n\
433 --abbc761f78ff4d7cb7573b5a23f96ef0\r\n\
434 Content-Disposition: form-data; name=\"file\"; filename=\"fn.txt\"\r\n\
435 Content-Type: text/plain; charset=utf-8\r\n\
436 \r\n\
437 one+one+one\r\n\
438 --abbc761f78ff4d7cb7573b5a23f96ef0\r\n\
439 Content-Disposition: form-data; name=\"file\"; filename=\"fn.txt\"\r\n\
440 Content-Type: text/plain; charset=utf-8\r\n\
441 \r\n\
442 two+two+two\r\n\
443 --abbc761f78ff4d7cb7573b5a23f96ef0--\r\n",
444 );
445 let mut headers = HeaderMap::new();
446 headers.insert(
447 header::CONTENT_TYPE,
448 header::HeaderValue::from_static(
449 "multipart/mixed; boundary=\"abbc761f78ff4d7cb7573b5a23f96ef0\"",
450 ),
451 );
452 (bytes, headers)
453 }
454
455 #[actix_rt::test]
456 async fn bytes_unlimited() {
457 let (body, headers) = create_double_request_with_header();
458
459 let mut multipart = Multipart::new(&headers, stream::iter([Ok(body)]));
460
461 let field = multipart
462 .next()
463 .await
464 .expect("multipart should have two fields")
465 .expect("multipart body should be well formatted")
466 .bytes(usize::MAX)
467 .await
468 .expect("field data should not be size limited")
469 .expect("reading field data should not error");
470 assert_eq!(field, "one+one+one");
471
472 let field = multipart
473 .next()
474 .await
475 .expect("multipart should have two fields")
476 .expect("multipart body should be well formatted")
477 .bytes(usize::MAX)
478 .await
479 .expect("field data should not be size limited")
480 .expect("reading field data should not error");
481 assert_eq!(field, "two+two+two");
482 }
483
484 #[actix_rt::test]
485 async fn bytes_limited() {
486 let (body, headers) = create_double_request_with_header();
487
488 let mut multipart = Multipart::new(&headers, stream::iter([Ok(body)]));
489
490 multipart
491 .next()
492 .await
493 .expect("multipart should have two fields")
494 .expect("multipart body should be well formatted")
495 .bytes(8) .await
497 .expect_err("field data should be size limited");
498
499 let field = multipart
501 .next()
502 .await
503 .expect("multipart should have two fields")
504 .expect("multipart body should be well formatted")
505 .bytes(usize::MAX)
506 .await
507 .expect("field data should not be size limited")
508 .expect("reading field data should not error");
509 assert_eq!(field, "two+two+two");
510 }
511
512 #[actix_rt::test]
513 async fn boundary_marker_split_across_chunks() {
514 let (body, headers) = create_double_request_with_header();
515 let boundary_start = memchr::memmem::find_iter(&body, b"\r\n--")
516 .nth(1)
517 .expect("body should contain a boundary between its fields");
518 let boundary_marker_end = boundary_start + 4;
519
520 let (mut tx, rx) = h1::Payload::create(false);
521 tx.feed_data(body.slice(..boundary_start));
522 tx.feed_data(body.slice(boundary_start..boundary_marker_end));
523
524 let mut multipart = Multipart::new(&headers, rx);
525
526 let mut field = multipart
527 .next()
528 .await
529 .expect("multipart should have two fields")
530 .expect("multipart body should be well formatted");
531 assert_eq!(field.next().await.unwrap().unwrap(), "one+one+one");
532 let next = field.next().now_or_never();
533 assert!(
534 next.is_none(),
535 "partial boundary marker should not be emitted as field data: {next:?}",
536 );
537
538 tx.feed_data(body.slice(boundary_marker_end..));
539 assert!(field.next().await.is_none());
540 drop(field);
541
542 let field = multipart
543 .next()
544 .await
545 .expect("multipart should have two fields")
546 .expect("multipart body should be well formatted")
547 .bytes(usize::MAX)
548 .await
549 .expect("field data should not be size limited")
550 .expect("reading field data should not error");
551 assert_eq!(field, "two+two+two");
552 }
553
554 #[test]
555 fn mismatching_boundary_prefix_is_yielded_without_more_input() {
556 let mut headers = HeaderMap::new();
557 headers.insert(
558 header::CONTENT_TYPE,
559 header::HeaderValue::from_static("multipart/form-data; boundary=abc"),
560 );
561
562 let (mut tx, rx) = h1::Payload::create(false);
563 tx.feed_data(Bytes::from_static(
564 b"--abc\r\nContent-Disposition: form-data; name=\"field\"\r\n\r\n\r\n--ax",
565 ));
566
567 let mut multipart = Multipart::new(&headers, rx);
568 let mut field = multipart
569 .next()
570 .now_or_never()
571 .expect("field headers should be ready")
572 .expect("multipart should contain a field")
573 .expect("field headers should be valid");
574
575 let next = field.next().now_or_never();
576 assert!(
577 matches!(&next, Some(Some(Ok(bytes))) if bytes.as_ref() == b"\r\n--ax"),
578 "a mismatching boundary prefix should be yielded without more input: {next:?}",
579 );
580
581 tx.feed_eof();
582
583 let next = field.next().now_or_never();
584 assert!(
585 matches!(next, Some(Some(Err(crate::error::Error::Incomplete)))),
586 "EOF without a closing boundary should report an incomplete message: {next:?}",
587 );
588 }
589
590 #[test]
591 fn mismatching_boundary_prefix_is_yielded_before_eof_incomplete() {
592 let mut headers = HeaderMap::new();
593 headers.insert(
594 header::CONTENT_TYPE,
595 header::HeaderValue::from_static("multipart/form-data; boundary=abc"),
596 );
597
598 let (mut tx, rx) = h1::Payload::create(false);
599 tx.feed_data(Bytes::from_static(
600 b"--abc\r\nContent-Disposition: form-data; name=\"field\"\r\n\r\n\r\n--ax",
601 ));
602 tx.feed_eof();
603
604 let mut multipart = Multipart::new(&headers, rx);
605 let mut field = multipart
606 .next()
607 .now_or_never()
608 .expect("field headers should be ready")
609 .expect("multipart should contain a field")
610 .expect("field headers should be valid");
611
612 let next = field.next().now_or_never();
613 assert!(
614 matches!(&next, Some(Some(Ok(bytes))) if bytes.as_ref() == b"\r\n--ax"),
615 "a mismatching boundary prefix should be yielded before Incomplete: {next:?}",
616 );
617
618 let next = field.next().now_or_never();
619 assert!(
620 matches!(next, Some(Some(Err(crate::error::Error::Incomplete)))),
621 "EOF after yielding mismatch should report an incomplete message: {next:?}",
622 );
623 }
624}