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 if len < b_size {
317 return Poll::Pending;
318 } else if &payload.buf[b_len..b_size] == boundary.as_bytes() {
319 return Poll::Ready(None);
321 }
322 }
323 }
324
325 loop {
326 return if let Some(idx) = memchr::memmem::find(&payload.buf[pos..], b"\r") {
327 let cur = pos + idx;
328
329 if cur + 4 > len {
331 if cur > 0 {
332 Poll::Ready(Some(Ok(payload.buf.split_to(cur).freeze())))
333 } else {
334 Poll::Pending
335 }
336 } else {
337 if (&payload.buf[cur..cur + 2] == b"\r\n"
339 && &payload.buf[cur + 2..cur + 4] == b"--")
340 || (&payload.buf[cur..=cur] == b"\r"
341 && &payload.buf[cur + 1..cur + 3] == b"--")
342 {
343 if cur != 0 {
344 Poll::Ready(Some(Ok(payload.buf.split_to(cur).freeze())))
346 } else {
347 pos = cur + 1;
348 continue;
349 }
350 } else {
351 pos = cur + 1;
353 continue;
354 }
355 }
356 } else {
357 Poll::Ready(Some(Ok(payload.buf.split().freeze())))
358 };
359 }
360 }
361
362 pub(crate) fn poll(&mut self, safety: &Safety) -> Poll<Option<Result<Bytes, Error>>> {
363 if self.payload.is_none() {
364 return Poll::Ready(None);
365 }
366
367 let Some(mut payload) = self
368 .payload
369 .as_ref()
370 .expect("Field should not be polled after completion")
371 .get_mut(safety)
372 else {
373 return Poll::Pending;
374 };
375
376 if !self.eof {
377 let res = if let Some(ref mut len) = self.length {
378 Self::read_len(&mut payload, len)
379 } else {
380 Self::read_stream(&mut payload, &self.boundary)
381 };
382
383 match ready!(res) {
384 Some(Ok(bytes)) => return Poll::Ready(Some(Ok(bytes))),
385 Some(Err(err)) => return Poll::Ready(Some(Err(err))),
386 None => self.eof = true,
387 }
388 }
389
390 let result = match payload.readline() {
391 Ok(None) => Poll::Pending,
392 Ok(Some(line)) => {
393 if line.as_ref() != b"\r\n" {
394 log::warn!("multipart field did not read all the data or it is malformed");
395 }
396 Poll::Ready(None)
397 }
398 Err(err) => Poll::Ready(Some(Err(err))),
399 };
400
401 drop(payload);
402
403 if let Poll::Ready(None) = result {
404 let _ = self.payload.take();
406 }
407
408 result
409 }
410}
411
412#[cfg(test)]
413mod tests {
414 use actix_http::h1;
415 use futures_util::{stream, FutureExt as _, StreamExt as _};
416
417 use super::*;
418 use crate::Multipart;
419
420 fn create_double_request_with_header() -> (Bytes, HeaderMap) {
422 let bytes = Bytes::from(
423 "testasdadsad\r\n\
424 --abbc761f78ff4d7cb7573b5a23f96ef0\r\n\
425 Content-Disposition: form-data; name=\"file\"; filename=\"fn.txt\"\r\n\
426 Content-Type: text/plain; charset=utf-8\r\n\
427 \r\n\
428 one+one+one\r\n\
429 --abbc761f78ff4d7cb7573b5a23f96ef0\r\n\
430 Content-Disposition: form-data; name=\"file\"; filename=\"fn.txt\"\r\n\
431 Content-Type: text/plain; charset=utf-8\r\n\
432 \r\n\
433 two+two+two\r\n\
434 --abbc761f78ff4d7cb7573b5a23f96ef0--\r\n",
435 );
436 let mut headers = HeaderMap::new();
437 headers.insert(
438 header::CONTENT_TYPE,
439 header::HeaderValue::from_static(
440 "multipart/mixed; boundary=\"abbc761f78ff4d7cb7573b5a23f96ef0\"",
441 ),
442 );
443 (bytes, headers)
444 }
445
446 #[actix_rt::test]
447 async fn bytes_unlimited() {
448 let (body, headers) = create_double_request_with_header();
449
450 let mut multipart = Multipart::new(&headers, stream::iter([Ok(body)]));
451
452 let field = multipart
453 .next()
454 .await
455 .expect("multipart should have two fields")
456 .expect("multipart body should be well formatted")
457 .bytes(usize::MAX)
458 .await
459 .expect("field data should not be size limited")
460 .expect("reading field data should not error");
461 assert_eq!(field, "one+one+one");
462
463 let field = multipart
464 .next()
465 .await
466 .expect("multipart should have two fields")
467 .expect("multipart body should be well formatted")
468 .bytes(usize::MAX)
469 .await
470 .expect("field data should not be size limited")
471 .expect("reading field data should not error");
472 assert_eq!(field, "two+two+two");
473 }
474
475 #[actix_rt::test]
476 async fn bytes_limited() {
477 let (body, headers) = create_double_request_with_header();
478
479 let mut multipart = Multipart::new(&headers, stream::iter([Ok(body)]));
480
481 multipart
482 .next()
483 .await
484 .expect("multipart should have two fields")
485 .expect("multipart body should be well formatted")
486 .bytes(8) .await
488 .expect_err("field data should be size limited");
489
490 let field = multipart
492 .next()
493 .await
494 .expect("multipart should have two fields")
495 .expect("multipart body should be well formatted")
496 .bytes(usize::MAX)
497 .await
498 .expect("field data should not be size limited")
499 .expect("reading field data should not error");
500 assert_eq!(field, "two+two+two");
501 }
502
503 #[actix_rt::test]
504 async fn boundary_marker_split_across_chunks() {
505 let (body, headers) = create_double_request_with_header();
506 let boundary_start = memchr::memmem::find_iter(&body, b"\r\n--")
507 .nth(1)
508 .expect("body should contain a boundary between its fields");
509 let boundary_marker_end = boundary_start + 4;
510
511 let (mut tx, rx) = h1::Payload::create(false);
512 tx.feed_data(body.slice(..boundary_start));
513 tx.feed_data(body.slice(boundary_start..boundary_marker_end));
514
515 let mut multipart = Multipart::new(&headers, rx);
516
517 let mut field = multipart
518 .next()
519 .await
520 .expect("multipart should have two fields")
521 .expect("multipart body should be well formatted");
522 assert_eq!(field.next().await.unwrap().unwrap(), "one+one+one");
523 let next = field.next().now_or_never();
524 assert!(
525 next.is_none(),
526 "partial boundary marker should not be emitted as field data: {next:?}",
527 );
528
529 tx.feed_data(body.slice(boundary_marker_end..));
530 assert!(field.next().await.is_none());
531 drop(field);
532
533 let field = multipart
534 .next()
535 .await
536 .expect("multipart should have two fields")
537 .expect("multipart body should be well formatted")
538 .bytes(usize::MAX)
539 .await
540 .expect("field data should not be size limited")
541 .expect("reading field data should not error");
542 assert_eq!(field, "two+two+two");
543 }
544}