| @@ -14,7 +14,7 @@ base64 = "0.9" | |||||||
| bytes = "0.4" | bytes = "0.4" | ||||||
| encoding_rs = "0.8" | encoding_rs = "0.8" | ||||||
| futures = "0.1.23" | futures = "0.1.23" | ||||||
| http = "0.1.5" | http = "0.1.10" | ||||||
| hyper = "0.12.7" | hyper = "0.12.7" | ||||||
| hyper-old-types = { version = "0.11", optional = true, features = ["compat"] } | hyper-old-types = { version = "0.11", optional = true, features = ["compat"] } | ||||||
| hyper-tls = "0.3" | hyper-tls = "0.3" | ||||||
|   | |||||||
							
								
								
									
										101
									
								
								src/body.rs
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								src/body.rs
									
									
									
									
									
								
							| @@ -3,6 +3,7 @@ use std::fmt; | |||||||
| use std::io::{self, Cursor, Read}; | use std::io::{self, Cursor, Read}; | ||||||
|  |  | ||||||
| use bytes::Bytes; | use bytes::Bytes; | ||||||
|  | use futures::Future; | ||||||
| use hyper::{self}; | use hyper::{self}; | ||||||
|  |  | ||||||
| use {async_impl}; | use {async_impl}; | ||||||
| @@ -77,6 +78,37 @@ impl Body { | |||||||
|             kind: Kind::Reader(Box::from(reader), Some(len)), |             kind: Kind::Reader(Box::from(reader), Some(len)), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     pub(crate) fn len(&self) -> Option<u64> { | ||||||
|  |         match self.kind { | ||||||
|  |             Kind::Reader(_, len) => len, | ||||||
|  |             Kind::Bytes(ref bytes) => Some(bytes.len() as u64), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub(crate) fn into_reader(self) -> Reader { | ||||||
|  |         match self.kind { | ||||||
|  |             Kind::Reader(r, _) => Reader::Reader(r), | ||||||
|  |             Kind::Bytes(b) => Reader::Bytes(Cursor::new(b)), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub(crate) fn into_async(self) -> (Option<Sender>, async_impl::Body, Option<u64>) { | ||||||
|  |         match self.kind { | ||||||
|  |             Kind::Reader(read, len) => { | ||||||
|  |                 let (tx, rx) = hyper::Body::channel(); | ||||||
|  |                 let tx = Sender { | ||||||
|  |                     body: (read, len), | ||||||
|  |                     tx: tx, | ||||||
|  |                 }; | ||||||
|  |                 (Some(tx), async_impl::body::wrap(rx), len) | ||||||
|  |             }, | ||||||
|  |             Kind::Bytes(chunk) => { | ||||||
|  |                 let len = chunk.len() as u64; | ||||||
|  |                 (None, async_impl::body::reusable(chunk), Some(len)) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -150,29 +182,11 @@ impl<'a> fmt::Debug for DebugLength<'a> { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | pub(crate) enum Reader { | ||||||
| // pub(crate) |  | ||||||
|  |  | ||||||
| pub fn len(body: &Body) -> Option<u64> { |  | ||||||
|     match body.kind { |  | ||||||
|         Kind::Reader(_, len) => len, |  | ||||||
|         Kind::Bytes(ref bytes) => Some(bytes.len() as u64), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub enum Reader { |  | ||||||
|     Reader(Box<Read + Send>), |     Reader(Box<Read + Send>), | ||||||
|     Bytes(Cursor<Bytes>), |     Bytes(Cursor<Bytes>), | ||||||
| } | } | ||||||
|  |  | ||||||
| #[inline] |  | ||||||
| pub fn reader(body: Body) -> Reader { |  | ||||||
|     match body.kind { |  | ||||||
|         Kind::Reader(r, _) => Reader::Reader(r), |  | ||||||
|         Kind::Bytes(b) => Reader::Bytes(Cursor::new(b)), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Read for Reader { | impl Read for Reader { | ||||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||||
|         match *self { |         match *self { | ||||||
| @@ -182,25 +196,37 @@ impl Read for Reader { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| pub struct Sender { | pub(crate) struct Sender { | ||||||
|     body: (Box<Read + Send>, Option<u64>), |     body: (Box<Read + Send>, Option<u64>), | ||||||
|     tx: hyper::body::Sender, |     tx: hyper::body::Sender, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Sender { | impl Sender { | ||||||
|     pub fn send(self) -> ::Result<()> { |     // A `Future` that may do blocking read calls. | ||||||
|  |     // As a `Future`, this integrates easily with `wait::timeout`. | ||||||
|  |     pub(crate) fn send(self) -> impl Future<Item=(), Error=::Error> { | ||||||
|         use std::cmp; |         use std::cmp; | ||||||
|         use bytes::{BufMut, BytesMut}; |         use bytes::{BufMut, BytesMut}; | ||||||
|  |         use futures::future; | ||||||
|  |  | ||||||
|         let cap = cmp::min(self.body.1.unwrap_or(8192), 8192); |         let cap = cmp::min(self.body.1.unwrap_or(8192), 8192); | ||||||
|         let mut buf = BytesMut::with_capacity(cap as usize); |         let mut buf = BytesMut::with_capacity(cap as usize); | ||||||
|         let mut body = self.body.0; |         let mut body = self.body.0; | ||||||
|         let mut tx = self.tx; |         // Put in an option so that it can be consumed on error to call abort() | ||||||
|         loop { |         let mut tx = Some(self.tx); | ||||||
|  |  | ||||||
|  |         future::poll_fn(move || loop { | ||||||
|  |             try_ready!(tx | ||||||
|  |                 .as_mut() | ||||||
|  |                 .expect("tx only taken on error") | ||||||
|  |                 .poll_ready() | ||||||
|  |                 .map_err(::error::from)); | ||||||
|  |  | ||||||
|             match body.read(unsafe { buf.bytes_mut() }) { |             match body.read(unsafe { buf.bytes_mut() }) { | ||||||
|                 Ok(0) => return Ok(()), |                 Ok(0) => return Ok(().into()), | ||||||
|                 Ok(n) => { |                 Ok(n) => { | ||||||
|                     unsafe { buf.advance_mut(n); } |                     unsafe { buf.advance_mut(n); } | ||||||
|  |                     let tx = tx.as_mut().expect("tx only taken on error"); | ||||||
|                     if let Err(_) = tx.send_data(buf.take().freeze().into()) { |                     if let Err(_) = tx.send_data(buf.take().freeze().into()) { | ||||||
|                         return Err(::error::timedout(None)); |                         return Err(::error::timedout(None)); | ||||||
|                     } |                     } | ||||||
| @@ -210,35 +236,20 @@ impl Sender { | |||||||
|                 } |                 } | ||||||
|                 Err(e) => { |                 Err(e) => { | ||||||
|                     let ret = io::Error::new(e.kind(), e.to_string()); |                     let ret = io::Error::new(e.kind(), e.to_string()); | ||||||
|                     tx.abort(); |                     tx | ||||||
|  |                         .take() | ||||||
|  |                         .expect("tx only taken on error") | ||||||
|  |                         .abort(); | ||||||
|                     return Err(::error::from(ret)); |                     return Err(::error::from(ret)); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         }) | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[inline] |  | ||||||
| pub fn async(body: Body) -> (Option<Sender>, async_impl::Body, Option<u64>) { |  | ||||||
|     match body.kind { |  | ||||||
|         Kind::Reader(read, len) => { |  | ||||||
|             let (tx, rx) = hyper::Body::channel(); |  | ||||||
|             let tx = Sender { |  | ||||||
|                 body: (read, len), |  | ||||||
|                 tx: tx, |  | ||||||
|             }; |  | ||||||
|             (Some(tx), async_impl::body::wrap(rx), len) |  | ||||||
|         }, |  | ||||||
|         Kind::Bytes(chunk) => { |  | ||||||
|             let len = chunk.len() as u64; |  | ||||||
|             (None, async_impl::body::reusable(chunk), Some(len)) |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| // useful for tests, but not publicly exposed | // useful for tests, but not publicly exposed | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| pub fn read_to_string(mut body: Body) -> io::Result<String> { | pub(crate) fn read_to_string(mut body: Body) -> io::Result<String> { | ||||||
|     let mut s = String::new(); |     let mut s = String::new(); | ||||||
|     match body.kind { |     match body.kind { | ||||||
|             Kind::Reader(ref mut reader, _) => reader.read_to_string(&mut s), |             Kind::Reader(ref mut reader, _) => reader.read_to_string(&mut s), | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ use std::time::Duration; | |||||||
| use std::thread; | use std::thread; | ||||||
|  |  | ||||||
| use futures::{Future, Stream}; | use futures::{Future, Stream}; | ||||||
|  | use futures::future::{self, Either}; | ||||||
| use futures::sync::{mpsc, oneshot}; | use futures::sync::{mpsc, oneshot}; | ||||||
|  |  | ||||||
| use request::{Request, RequestBuilder}; | use request::{Request, RequestBuilder}; | ||||||
| @@ -466,20 +467,29 @@ impl ClientHandle { | |||||||
|             .unbounded_send((req, tx)) |             .unbounded_send((req, tx)) | ||||||
|             .expect("core thread panicked"); |             .expect("core thread panicked"); | ||||||
|  |  | ||||||
|         if let Some(body) = body { |         let write = if let Some(body) = body { | ||||||
|             try_!(body.send(), &url); |             Either::A(body.send()) | ||||||
|         } |             //try_!(body.send(self.timeout.0), &url); | ||||||
|  |         } else { | ||||||
|  |             Either::B(future::ok(())) | ||||||
|  |         }; | ||||||
|  |  | ||||||
|         let res = match wait::timeout(rx, self.timeout.0) { |         let rx = rx.map_err(|_canceled| { | ||||||
|  |             // The only possible reason there would be a Canceled error | ||||||
|  |             // is if the thread running the event loop panicked. We could return | ||||||
|  |             // an Err here, like a BrokenPipe, but the Client is not | ||||||
|  |             // recoverable. Additionally, the panic in the other thread | ||||||
|  |             // is not normal, and should likely be propagated. | ||||||
|  |             panic!("event loop thread panicked"); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         let fut = write.join(rx).map(|((), res)| res); | ||||||
|  |  | ||||||
|  |         let res = match wait::timeout(fut, self.timeout.0) { | ||||||
|             Ok(res) => res, |             Ok(res) => res, | ||||||
|             Err(wait::Waited::TimedOut) => return Err(::error::timedout(Some(url))), |             Err(wait::Waited::TimedOut) => return Err(::error::timedout(Some(url))), | ||||||
|             Err(wait::Waited::Err(_canceled)) => { |             Err(wait::Waited::Err(err)) => { | ||||||
|                 // The only possible reason there would be a Cancelled error |                 return Err(err.with_url(url)); | ||||||
|                 // is if the thread running the Core panicked. We could return |  | ||||||
|                 // an Err here, like a BrokenPipe, but the Client is not |  | ||||||
|                 // recoverable. Additionally, the panic in the other thread |  | ||||||
|                 // is not normal, and should likely be propagated. |  | ||||||
|                 panic!("core thread panicked"); |  | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|         res.map(|res| { |         res.map(|res| { | ||||||
|   | |||||||
| @@ -82,6 +82,12 @@ impl Error { | |||||||
|         self.url.as_ref() |         self.url.as_ref() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     pub(crate) fn with_url(mut self, url: Url) -> Error { | ||||||
|  |         debug_assert_eq!(self.url, None, "with_url overriding existing url"); | ||||||
|  |         self.url = Some(url); | ||||||
|  |         self | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /// Returns a reference to the internal error, if available. |     /// Returns a reference to the internal error, if available. | ||||||
|     /// |     /// | ||||||
|     /// The `'static` bounds allows using `downcast_ref` to check the |     /// The `'static` bounds allows using `downcast_ref` to check the | ||||||
|   | |||||||
| @@ -93,7 +93,7 @@ impl Form { | |||||||
|     pub(crate) fn compute_length(&mut self) -> Option<u64> { |     pub(crate) fn compute_length(&mut self) -> Option<u64> { | ||||||
|         let mut length = 0u64; |         let mut length = 0u64; | ||||||
|         for &(ref name, ref field) in self.fields.iter() { |         for &(ref name, ref field) in self.fields.iter() { | ||||||
|             match ::body::len(&field.value) { |             match field.value.len() { | ||||||
|                 Some(value_length) => { |                 Some(value_length) => { | ||||||
|                     // We are constructing the header just to get its length. To not have to |                     // We are constructing the header just to get its length. To not have to | ||||||
|                     // construct it again when the request is sent we cache these headers. |                     // construct it again when the request is sent we cache these headers. | ||||||
| @@ -272,7 +272,7 @@ impl Reader { | |||||||
|             }); |             }); | ||||||
|             let reader = boundary |             let reader = boundary | ||||||
|                 .chain(header) |                 .chain(header) | ||||||
|                 .chain(::body::reader(field.value)) |                 .chain(field.value.into_reader()) | ||||||
|                 .chain(Cursor::new("\r\n")); |                 .chain(Cursor::new("\r\n")); | ||||||
|             // According to https://tools.ietf.org/html/rfc2046#section-5.1.1 |             // According to https://tools.ietf.org/html/rfc2046#section-5.1.1 | ||||||
|             // the very last field has a special boundary |             // the very last field has a special boundary | ||||||
|   | |||||||
| @@ -86,9 +86,9 @@ impl Request { | |||||||
|  |  | ||||||
|         let mut req_async = self.inner; |         let mut req_async = self.inner; | ||||||
|         let body = self.body.and_then(|body| { |         let body = self.body.and_then(|body| { | ||||||
|             let (tx, body, len) = body::async(body); |             let (tx, body, len) = body.into_async(); | ||||||
|             if let Some(len) = len { |             if let Some(len) = len { | ||||||
|                 req_async.headers_mut().insert(CONTENT_LENGTH, HeaderValue::from_str(len.to_string().as_str()).expect("")); |                 req_async.headers_mut().insert(CONTENT_LENGTH, len.into()); | ||||||
|             } |             } | ||||||
|             *req_async.body_mut() = Some(body); |             *req_async.body_mut() = Some(body); | ||||||
|             tx |             tx | ||||||
|   | |||||||
| @@ -49,3 +49,52 @@ fn test_multipart() { | |||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[test] | ||||||
|  | fn file() { | ||||||
|  |     let _ = env_logger::try_init(); | ||||||
|  |  | ||||||
|  |     let form = reqwest::multipart::Form::new() | ||||||
|  |         .file("foo", "Cargo.lock").unwrap(); | ||||||
|  |  | ||||||
|  |     let fcontents = ::std::fs::read_to_string("Cargo.lock").unwrap(); | ||||||
|  |  | ||||||
|  |     let expected_body = format!("\ | ||||||
|  |         --{0}\r\n\ | ||||||
|  |         Content-Disposition: form-data; name=\"foo\"; filename=\"Cargo.lock\"\r\n\ | ||||||
|  |         Content-Type: application/octet-stream\r\n\r\n\ | ||||||
|  |         {1}\r\n\ | ||||||
|  |         --{0}--\r\n\ | ||||||
|  |     ", form.boundary(), fcontents); | ||||||
|  |  | ||||||
|  |     let server = server! { | ||||||
|  |         request: format!("\ | ||||||
|  |             POST /multipart/2 HTTP/1.1\r\n\ | ||||||
|  |             user-agent: $USERAGENT\r\n\ | ||||||
|  |             accept: */*\r\n\ | ||||||
|  |             content-type: multipart/form-data; boundary={}\r\n\ | ||||||
|  |             content-length: {}\r\n\ | ||||||
|  |             accept-encoding: gzip\r\n\ | ||||||
|  |             host: $HOST\r\n\ | ||||||
|  |             \r\n\ | ||||||
|  |             {}\ | ||||||
|  |             ", form.boundary(), expected_body.len(), expected_body), | ||||||
|  |         response: b"\ | ||||||
|  |             HTTP/1.1 200 OK\r\n\ | ||||||
|  |             Server: multipart\r\n\ | ||||||
|  |             Content-Length: 0\r\n\ | ||||||
|  |             \r\n\ | ||||||
|  |             " | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     let url = format!("http://{}/multipart/2", server.addr()); | ||||||
|  |  | ||||||
|  |     let res = reqwest::Client::new() | ||||||
|  |         .post(&url) | ||||||
|  |         .multipart(form) | ||||||
|  |         .send() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|  |     assert_eq!(res.url().as_str(), &url); | ||||||
|  |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -32,7 +32,8 @@ static DEFAULT_USER_AGENT: &'static str = | |||||||
| pub fn spawn(txns: Vec<Txn>) -> Server { | pub fn spawn(txns: Vec<Txn>) -> Server { | ||||||
|     let listener = net::TcpListener::bind("127.0.0.1:0").unwrap(); |     let listener = net::TcpListener::bind("127.0.0.1:0").unwrap(); | ||||||
|     let addr = listener.local_addr().unwrap(); |     let addr = listener.local_addr().unwrap(); | ||||||
|     thread::spawn( |     let tname = format!("test({})-support-server", thread::current().name().unwrap_or("<unknown>")); | ||||||
|  |     thread::Builder::new().name(tname).spawn( | ||||||
|         move || for txn in txns { |         move || for txn in txns { | ||||||
|             let mut expected = txn.request; |             let mut expected = txn.request; | ||||||
|             let reply = txn.response; |             let reply = txn.response; | ||||||
| @@ -46,20 +47,41 @@ pub fn spawn(txns: Vec<Txn>) -> Server { | |||||||
|                 thread::park_timeout(dur); |                 thread::park_timeout(dur); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             let mut buf = [0; 4096]; |             let mut buf = vec![0; expected.len() + 256]; | ||||||
|             assert!(buf.len() >= expected.len()); |  | ||||||
|  |  | ||||||
|             let mut n = 0; |             let mut n = 0; | ||||||
|             while n < expected.len() { |             while n < expected.len() { | ||||||
|                 match socket.read(&mut buf[n..]) { |                 match socket.read(&mut buf[n..]) { | ||||||
|                     Ok(0) | Err(_) => break, |                     Ok(0) => break, | ||||||
|                     Ok(nread) => n += nread, |                     Ok(nread) => n += nread, | ||||||
|  |                     Err(err) => { | ||||||
|  |                         println!("server read error: {}", err); | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             match (::std::str::from_utf8(&expected), ::std::str::from_utf8(&buf[..n])) { |             match (::std::str::from_utf8(&expected), ::std::str::from_utf8(&buf[..n])) { | ||||||
|                 (Ok(expected), Ok(received)) => assert_eq!(expected, received), |                 (Ok(expected), Ok(received)) => { | ||||||
|                 _ => assert_eq!(expected, &buf[..n]), |                     assert_eq!( | ||||||
|  |                         expected.len(), | ||||||
|  |                         received.len(), | ||||||
|  |                         "expected len = {}, received len = {}", | ||||||
|  |                         expected.len(), | ||||||
|  |                         received.len(), | ||||||
|  |                     ); | ||||||
|  |                     assert_eq!(expected, received) | ||||||
|  |                 }, | ||||||
|  |                 _ => { | ||||||
|  |                     assert_eq!( | ||||||
|  |                         expected.len(), | ||||||
|  |                         n, | ||||||
|  |                         "expected len = {}, received len = {}", | ||||||
|  |                         expected.len(), | ||||||
|  |                         n, | ||||||
|  |                     ); | ||||||
|  |                     assert_eq!(expected, &buf[..n]) | ||||||
|  |                 }, | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if let Some(dur) = txn.response_timeout { |             if let Some(dur) = txn.response_timeout { | ||||||
| @@ -86,7 +108,7 @@ pub fn spawn(txns: Vec<Txn>) -> Server { | |||||||
|                 socket.write_all(&reply).unwrap(); |                 socket.write_all(&reply).unwrap(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     ); |     ).expect("server thread spawn"); | ||||||
|  |  | ||||||
|     Server { |     Server { | ||||||
|         addr: addr, |         addr: addr, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user