multipart_async/server/field/mod.rs
1// Copyright 2017 `multipart-async` Crate Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7use futures::{Future, Stream, Async, Poll};
8use futures::Async::*;
9
10use mime::{self, Mime};
11
12use std::rc::Rc;
13use std::{io, mem, str};
14
15use server::boundary::BoundaryFinder;
16use server::{Internal, BodyChunk, StreamError, httparse};
17
18use helpers::*;
19
20
21use self::httparse::EMPTY_HEADER;
22
23mod collect;
24mod headers;
25
26pub use self::headers::{FieldHeaders, ReadHeaders};
27
28pub use self::collect::{ReadTextField, TextField};
29
30pub(super) fn new_field<S: Stream>(headers: FieldHeaders, internal: Rc<Internal<S>>) -> Field<S> {
31 let headers = Rc::new(headers);
32
33 Field {
34 headers: headers.clone(),
35 data: FieldData {
36 headers, internal
37 },
38 _priv: (),
39 }
40}
41
42/// A single field in a multipart stream.
43///
44/// The data of the field is provided as a `Stream` impl in the `data` field.
45///
46/// To avoid the next field being initialized before this one is done being read
47/// (in a linear stream), only one instance per `Multipart` instance is allowed at a time.
48/// A `Drop` implementation on `FieldData` is used to notify `Multipart` that this field is done
49/// being read, thus:
50///
51/// ### Warning About Leaks
52/// If this value or the contained `FieldData` is leaked (via `mem::forget()` or some
53/// other mechanism), then the parent `Multipart` will never be able to yield the next field in the
54/// stream. The task waiting on the `Multipart` will also never be notified, which, depending on the
55/// event loop/reactor/executor implementation, may cause a deadlock.
56pub struct Field<S: Stream> {
57 /// The headers of this field, including the name, filename, and `Content-Type`, if provided.
58 pub headers: Rc<FieldHeaders>,
59 /// The data of this field in the request, represented as a stream of chunks.
60 pub data: FieldData<S>,
61 _priv: (),
62}
63
64/// The data of a field in a multipart stream, as a stream of chunks.
65///
66/// It may be read to completion via the `Stream` impl, or collected to a string with `read_text()`.
67///
68/// To avoid the next field being initialized before this one is done being read
69/// (in a linear stream), only one instance per `Multipart` instance is allowed at a time.
70/// A `Drop` implementation on `FieldData` is used to notify `Multipart` that this field is done
71/// being read, thus:
72///
73/// ### Warning About Leaks
74/// If this value is leaked (via `mem::forget()` or some other mechanism), then the parent
75/// `Multipart` will never be able to yield the next field in the stream. The task waiting on the
76/// `Multipart` will also never be notified, which, depending on the event loop/reactor/executor
77/// implementation, may cause a deadlock.
78// N.B.: must **never** be Clone!
79pub struct FieldData<S: Stream> {
80 headers: Rc<FieldHeaders>,
81 internal: Rc<Internal<S>>,
82}
83
84impl<S: Stream> FieldData<S> where S::Item: BodyChunk, S::Error: StreamError {
85 /// Get a `Future` which attempts to read the field data to a string.
86 ///
87 /// If a field is meant to be read as text, it will either have no content-type or
88 /// will have a content-type that starts with "text"; `FieldHeaders::is_text()` is
89 /// provided to help determine this.
90 ///
91 /// A default length limit for the string, in bytes, is set to avoid potential DoS attacks from
92 /// attackers running the server out of memory. If an incoming chunk is expected to push the
93 /// string over this limit, an error is returned. The limit value can be inspected and changed
94 /// on `ReadTextField` if desired.
95 ///
96 /// ### Charset
97 /// For simplicity, the default UTF-8 character set is assumed, as defined in
98 /// [IETF RFC 7578 Section 5.1.2](https://tools.ietf.org/html/rfc7578#section-5.1.2).
99 /// If the field body cannot be decoded as UTF-8, an error is returned.
100 ///
101 /// Decoding text in a different charset (except ASCII which
102 /// is compatible with UTF-8) is, currently, beyond the scope of this crate. However, as a
103 /// convention, web browsers will send `multipart/form-data` requests in the same
104 /// charset as that of the document (page or frame) containing the form, so if you only serve
105 /// ASCII/UTF-8 pages then you won't have to worry too much about decoding strange charsets.
106 pub fn read_text(self) -> ReadTextField<S> {
107 if !self.headers.is_text() {
108 debug!("attempting to read a non-text field as text: {:?}", self.headers);
109 }
110
111 collect::read_text(self)
112 }
113
114 fn stream_mut(&mut self) -> &mut BoundaryFinder<S> {
115 debug_assert!(Rc::strong_count(&self.internal) <= 2,
116 "More than two copies of an `Rc<Internal>` at one time");
117
118 // This is safe as we have guaranteed exclusive access, the lifetime is tied to `self`,
119 // and is never null.
120 unsafe { &mut *self.internal.stream.as_ptr() }
121 }
122}
123
124impl<S: Stream> Stream for FieldData<S> where S::Item: BodyChunk, S::Error: StreamError {
125 type Item = S::Item;
126 type Error = S::Error;
127
128 fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
129 self.stream_mut().body_chunk()
130 }
131}
132
133/// Notifies a task waiting on the parent `Multipart` that another field is available.
134impl<S: Stream> Drop for FieldData<S> {
135 fn drop(&mut self) {
136 self.internal.notify_task();
137 }
138}