1 // Copyright 2016 `multipart` 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.
7 //! The client-side abstraction for multipart requests. Enabled with the `client` feature.
8 //!
9 //! Use this when sending POST requests with files to a server.
10 use mime::Mime;
11 
12 use std::borrow::Cow;
13 use std::fs::File;
14 use std::io;
15 use std::io::prelude::*;
16 
17 use std::path::Path;
18 
19 #[cfg(feature = "hyper")]
20 pub mod hyper;
21 
22 pub mod lazy;
23 
24 mod sized;
25 
26 pub use self::sized::SizedRequest;
27 
28 const BOUNDARY_LEN: usize = 16;
29 
30 macro_rules! map_self {
31     ($selff:expr, $try:expr) => {
32         match $try {
33             Ok(_) => Ok($selff),
34             Err(err) => Err(err.into()),
35         }
36     };
37 }
38 
39 /// The entry point of the client-side multipart API.
40 ///
41 /// Though they perform I/O, the `.write_*()` methods do not return `io::Result<_>` in order to
42 /// facilitate method chaining. Upon the first error, all subsequent API calls will be no-ops until
43 /// `.send()` is called, at which point the error will be reported.
44 pub struct Multipart<S> {
45     writer: MultipartWriter<'static, S>,
46 }
47 
48 impl Multipart<()> {
49     /// Create a new `Multipart` to wrap a request.
50     ///
51     /// ## Returns Error
52     /// If `req.open_stream()` returns an error.
from_request<R: HttpRequest>(req: R) -> Result<Multipart<R::Stream>, R::Error>53     pub fn from_request<R: HttpRequest>(req: R) -> Result<Multipart<R::Stream>, R::Error> {
54         let (boundary, stream) = open_stream(req, None)?;
55 
56         Ok(Multipart {
57             writer: MultipartWriter::new(stream, boundary),
58         })
59     }
60 }
61 
62 impl<S: HttpStream> Multipart<S> {
63     /// Write a text field to this multipart request.
64     /// `name` and `val` can be either owned `String` or `&str`.
65     ///
66     /// ## Errors
67     /// If something went wrong with the HTTP stream.
write_text<N: AsRef<str>, V: AsRef<str>>( &mut self, name: N, val: V, ) -> Result<&mut Self, S::Error>68     pub fn write_text<N: AsRef<str>, V: AsRef<str>>(
69         &mut self,
70         name: N,
71         val: V,
72     ) -> Result<&mut Self, S::Error> {
73         map_self!(self, self.writer.write_text(name.as_ref(), val.as_ref()))
74     }
75 
76     /// Open a file pointed to by `path` and write its contents to the multipart request,
77     /// supplying its filename and guessing its `Content-Type` from its extension.
78     ///
79     /// If you want to set these values manually, or use another type that implements `Read`,
80     /// use `.write_stream()`.
81     ///
82     /// `name` can be either `String` or `&str`, and `path` can be `PathBuf` or `&Path`.
83     ///
84     /// ## Errors
85     /// If there was a problem opening the file (was a directory or didn't exist),
86     /// or if something went wrong with the HTTP stream.
write_file<N: AsRef<str>, P: AsRef<Path>>( &mut self, name: N, path: P, ) -> Result<&mut Self, S::Error>87     pub fn write_file<N: AsRef<str>, P: AsRef<Path>>(
88         &mut self,
89         name: N,
90         path: P,
91     ) -> Result<&mut Self, S::Error> {
92         let name = name.as_ref();
93         let path = path.as_ref();
94 
95         map_self!(self, self.writer.write_file(name, path))
96     }
97 
98     /// Write a byte stream to the multipart request as a file field, supplying `filename` if given,
99     /// and `content_type` if given or `"application/octet-stream"` if not.
100     ///
101     /// `name` can be either `String` or `&str`, and `read` can take the `Read` by-value or
102     /// with an `&mut` borrow.
103     ///
104     /// ## Warning
105     /// The given `Read` **must** be able to read to EOF (end of file/no more data), meaning
106     /// `Read::read()` returns `Ok(0)`. If it never returns EOF it will be read to infinity
107     /// and the request will never be completed.
108     ///
109     /// When using `SizedRequest` this also can cause out-of-control memory usage as the
110     /// multipart data has to be written to an in-memory buffer so its size can be calculated.
111     ///
112     /// Use `Read::take()` if you wish to send data from a `Read`
113     /// that will never return EOF otherwise.
114     ///
115     /// ## Errors
116     /// If the reader returned an error, or if something went wrong with the HTTP stream.
117     // RFC: How to format this declaration?
write_stream<N: AsRef<str>, St: Read>( &mut self, name: N, stream: &mut St, filename: Option<&str>, content_type: Option<Mime>, ) -> Result<&mut Self, S::Error>118     pub fn write_stream<N: AsRef<str>, St: Read>(
119         &mut self,
120         name: N,
121         stream: &mut St,
122         filename: Option<&str>,
123         content_type: Option<Mime>,
124     ) -> Result<&mut Self, S::Error> {
125         let name = name.as_ref();
126 
127         map_self!(
128             self,
129             self.writer
130                 .write_stream(stream, name, filename, content_type)
131         )
132     }
133 
134     /// Finalize the request and return the response from the server, or the last error if set.
send(self) -> Result<S::Response, S::Error>135     pub fn send(self) -> Result<S::Response, S::Error> {
136         self.writer
137             .finish()
138             .map_err(io::Error::into)
139             .and_then(|body| body.finish())
140     }
141 }
142 
143 impl<R: HttpRequest> Multipart<SizedRequest<R>>
144 where
145     <R::Stream as HttpStream>::Error: From<R::Error>,
146 {
147     /// Create a new `Multipart` using the `SizedRequest` wrapper around `req`.
from_request_sized(req: R) -> Result<Self, R::Error>148     pub fn from_request_sized(req: R) -> Result<Self, R::Error> {
149         Multipart::from_request(SizedRequest::from_request(req))
150     }
151 }
152 
153 /// A trait describing an HTTP request that can be used to send multipart data.
154 pub trait HttpRequest {
155     /// The HTTP stream type that can be opend by this request, to which the multipart data will be
156     /// written.
157     type Stream: HttpStream;
158     /// The error type for this request.
159     /// Must be compatible with `io::Error` as well as `Self::HttpStream::Error`
160     type Error: From<io::Error> + Into<<Self::Stream as HttpStream>::Error>;
161 
162     /// Set the `Content-Type` header to `multipart/form-data` and supply the `boundary` value.
163     /// If `content_len` is given, set the `Content-Length` header to its value.
164     ///
165     /// Return `true` if any and all sanity checks passed and the stream is ready to be opened,
166     /// or `false` otherwise.
apply_headers(&mut self, boundary: &str, content_len: Option<u64>) -> bool167     fn apply_headers(&mut self, boundary: &str, content_len: Option<u64>) -> bool;
168 
169     /// Open the request stream and return it or any error otherwise.
open_stream(self) -> Result<Self::Stream, Self::Error>170     fn open_stream(self) -> Result<Self::Stream, Self::Error>;
171 }
172 
173 /// A trait describing an open HTTP stream that can be written to.
174 pub trait HttpStream: Write {
175     /// The request type that opened this stream.
176     type Request: HttpRequest;
177     /// The response type that will be returned after the request is completed.
178     type Response;
179     /// The error type for this stream.
180     /// Must be compatible with `io::Error` as well as `Self::Request::Error`.
181     type Error: From<io::Error> + From<<Self::Request as HttpRequest>::Error>;
182 
183     /// Finalize and close the stream and return the response object, or any error otherwise.
finish(self) -> Result<Self::Response, Self::Error>184     fn finish(self) -> Result<Self::Response, Self::Error>;
185 }
186 
187 impl HttpRequest for () {
188     type Stream = io::Sink;
189     type Error = io::Error;
190 
apply_headers(&mut self, _: &str, _: Option<u64>) -> bool191     fn apply_headers(&mut self, _: &str, _: Option<u64>) -> bool {
192         true
193     }
open_stream(self) -> Result<Self::Stream, Self::Error>194     fn open_stream(self) -> Result<Self::Stream, Self::Error> {
195         Ok(io::sink())
196     }
197 }
198 
199 impl HttpStream for io::Sink {
200     type Request = ();
201     type Response = ();
202     type Error = io::Error;
203 
finish(self) -> Result<Self::Response, Self::Error>204     fn finish(self) -> Result<Self::Response, Self::Error> {
205         Ok(())
206     }
207 }
208 
gen_boundary() -> String209 fn gen_boundary() -> String {
210     ::random_alphanumeric(BOUNDARY_LEN)
211 }
212 
open_stream<R: HttpRequest>( mut req: R, content_len: Option<u64>, ) -> Result<(String, R::Stream), R::Error>213 fn open_stream<R: HttpRequest>(
214     mut req: R,
215     content_len: Option<u64>,
216 ) -> Result<(String, R::Stream), R::Error> {
217     let boundary = gen_boundary();
218     req.apply_headers(&boundary, content_len);
219     req.open_stream().map(|stream| (boundary, stream))
220 }
221 
222 struct MultipartWriter<'a, W> {
223     inner: W,
224     boundary: Cow<'a, str>,
225     data_written: bool,
226 }
227 
228 impl<'a, W: Write> MultipartWriter<'a, W> {
new<B: Into<Cow<'a, str>>>(inner: W, boundary: B) -> Self229     fn new<B: Into<Cow<'a, str>>>(inner: W, boundary: B) -> Self {
230         MultipartWriter {
231             inner,
232             boundary: boundary.into(),
233             data_written: false,
234         }
235     }
236 
write_boundary(&mut self) -> io::Result<()>237     fn write_boundary(&mut self) -> io::Result<()> {
238         if self.data_written {
239             self.inner.write_all(b"\r\n")?;
240         }
241 
242         write!(self.inner, "--{}\r\n", self.boundary)
243     }
244 
write_text(&mut self, name: &str, text: &str) -> io::Result<()>245     fn write_text(&mut self, name: &str, text: &str) -> io::Result<()> {
246         chain_result! {
247             self.write_field_headers(name, None, None),
248             self.inner.write_all(text.as_bytes())
249         }
250     }
251 
write_file(&mut self, name: &str, path: &Path) -> io::Result<()>252     fn write_file(&mut self, name: &str, path: &Path) -> io::Result<()> {
253         let (content_type, filename) = mime_filename(path);
254         let mut file = File::open(path)?;
255         self.write_stream(&mut file, name, filename, Some(content_type))
256     }
257 
write_stream<S: Read>( &mut self, stream: &mut S, name: &str, filename: Option<&str>, content_type: Option<Mime>, ) -> io::Result<()>258     fn write_stream<S: Read>(
259         &mut self,
260         stream: &mut S,
261         name: &str,
262         filename: Option<&str>,
263         content_type: Option<Mime>,
264     ) -> io::Result<()> {
265         // This is necessary to make sure it is interpreted as a file on the server end.
266         let content_type = Some(content_type.unwrap_or(mime::APPLICATION_OCTET_STREAM));
267 
268         chain_result! {
269             self.write_field_headers(name, filename, content_type),
270             io::copy(stream, &mut self.inner),
271             Ok(())
272         }
273     }
274 
write_field_headers( &mut self, name: &str, filename: Option<&str>, content_type: Option<Mime>, ) -> io::Result<()>275     fn write_field_headers(
276         &mut self,
277         name: &str,
278         filename: Option<&str>,
279         content_type: Option<Mime>,
280     ) -> io::Result<()> {
281         chain_result! {
282             // Write the first boundary, or the boundary for the previous field.
283             self.write_boundary(),
284             { self.data_written = true; Ok(()) },
285             write!(self.inner, "Content-Disposition: form-data; name=\"{}\"", name),
286             filename.map(|filename| write!(self.inner, "; filename=\"{}\"", filename))
287                 .unwrap_or(Ok(())),
288             content_type.map(|content_type| write!(self.inner, "\r\nContent-Type: {}", content_type))
289                 .unwrap_or(Ok(())),
290             self.inner.write_all(b"\r\n\r\n")
291         }
292     }
293 
finish(mut self) -> io::Result<W>294     fn finish(mut self) -> io::Result<W> {
295         if self.data_written {
296             self.inner.write_all(b"\r\n")?;
297         }
298 
299         // always write the closing boundary, even for empty bodies
300         // trailing CRLF is optional but Actix requires it due to a naive implementation:
301         // https://github.com/actix/actix-web/issues/598
302         write!(self.inner, "--{}--\r\n", self.boundary)?;
303         Ok(self.inner)
304     }
305 }
306 
mime_filename(path: &Path) -> (Mime, Option<&str>)307 fn mime_filename(path: &Path) -> (Mime, Option<&str>) {
308     let content_type = ::mime_guess::from_path(path);
309     let filename = opt_filename(path);
310     (content_type.first_or_octet_stream(), filename)
311 }
312 
opt_filename(path: &Path) -> Option<&str>313 fn opt_filename(path: &Path) -> Option<&str> {
314     path.file_name().and_then(|filename| filename.to_str())
315 }
316