refactor(lib): rename internal http module to proto
This commit is contained in:
		
							
								
								
									
										163
									
								
								src/proto/body.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								src/proto/body.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,163 @@ | ||||
| use bytes::Bytes; | ||||
| use futures::{Poll, Stream}; | ||||
| use futures::sync::mpsc; | ||||
| use tokio_proto; | ||||
| use std::borrow::Cow; | ||||
|  | ||||
| use super::Chunk; | ||||
|  | ||||
| pub type TokioBody = tokio_proto::streaming::Body<Chunk, ::Error>; | ||||
|  | ||||
| /// A `Stream` for `Chunk`s used in requests and responses. | ||||
| #[must_use = "streams do nothing unless polled"] | ||||
| #[derive(Debug)] | ||||
| pub struct Body(TokioBody); | ||||
|  | ||||
| impl Body { | ||||
|     /// Return an empty body stream | ||||
|     #[inline] | ||||
|     pub fn empty() -> Body { | ||||
|         Body(TokioBody::empty()) | ||||
|     } | ||||
|  | ||||
|     /// Return a body stream with an associated sender half | ||||
|     #[inline] | ||||
|     pub fn pair() -> (mpsc::Sender<Result<Chunk, ::Error>>, Body) { | ||||
|         let (tx, rx) = TokioBody::pair(); | ||||
|         let rx = Body(rx); | ||||
|         (tx, rx) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for Body { | ||||
|     #[inline] | ||||
|     fn default() -> Body { | ||||
|         Body::empty() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Stream for Body { | ||||
|     type Item = Chunk; | ||||
|     type Error = ::Error; | ||||
|  | ||||
|     #[inline] | ||||
|     fn poll(&mut self) -> Poll<Option<Chunk>, ::Error> { | ||||
|         self.0.poll() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Body> for tokio_proto::streaming::Body<Chunk, ::Error> { | ||||
|     #[inline] | ||||
|     fn from(b: Body) -> tokio_proto::streaming::Body<Chunk, ::Error> { | ||||
|         b.0 | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<tokio_proto::streaming::Body<Chunk, ::Error>> for Body { | ||||
|     #[inline] | ||||
|     fn from(tokio_body: tokio_proto::streaming::Body<Chunk, ::Error>) -> Body { | ||||
|         Body(tokio_body) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<mpsc::Receiver<Result<Chunk, ::Error>>> for Body { | ||||
|     #[inline] | ||||
|     fn from(src: mpsc::Receiver<Result<Chunk, ::Error>>) -> Body { | ||||
|         Body(src.into()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Chunk> for Body { | ||||
|     #[inline] | ||||
|     fn from (chunk: Chunk) -> Body { | ||||
|         Body(TokioBody::from(chunk)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Bytes> for Body { | ||||
|     #[inline] | ||||
|     fn from (bytes: Bytes) -> Body { | ||||
|         Body(TokioBody::from(Chunk::from(bytes))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Vec<u8>> for Body { | ||||
|     #[inline] | ||||
|     fn from (vec: Vec<u8>) -> Body { | ||||
|         Body(TokioBody::from(Chunk::from(vec))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<&'static [u8]> for Body { | ||||
|     #[inline] | ||||
|     fn from (slice: &'static [u8]) -> Body { | ||||
|         Body(TokioBody::from(Chunk::from(slice))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Cow<'static, [u8]>> for Body { | ||||
|     #[inline] | ||||
|     fn from (cow: Cow<'static, [u8]>) -> Body { | ||||
|         if let Cow::Borrowed(value) = cow { | ||||
|             Body::from(value) | ||||
|         } else { | ||||
|             Body::from(cow.to_owned()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<String> for Body { | ||||
|     #[inline] | ||||
|     fn from (s: String) -> Body { | ||||
|         Body(TokioBody::from(Chunk::from(s.into_bytes()))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<&'static str> for Body { | ||||
|     #[inline] | ||||
|     fn from(slice: &'static str) -> Body { | ||||
|         Body(TokioBody::from(Chunk::from(slice.as_bytes()))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Cow<'static, str>> for Body { | ||||
|     #[inline] | ||||
|     fn from(cow: Cow<'static, str>) -> Body { | ||||
|         if let Cow::Borrowed(value) = cow { | ||||
|             Body::from(value) | ||||
|         } else { | ||||
|             Body::from(cow.to_owned()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Option<Body>> for Body { | ||||
|     #[inline] | ||||
|     fn from (body: Option<Body>) -> Body { | ||||
|         body.unwrap_or_default() | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn _assert_send_sync() { | ||||
|     fn _assert_send<T: Send>() {} | ||||
|     fn _assert_sync<T: Sync>() {} | ||||
|  | ||||
|     _assert_send::<Body>(); | ||||
|     _assert_send::<Chunk>(); | ||||
|     _assert_sync::<Chunk>(); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn test_body_stream_concat() { | ||||
|     use futures::{Sink, Stream, Future}; | ||||
|     let (tx, body) = Body::pair(); | ||||
|  | ||||
|     ::std::thread::spawn(move || { | ||||
|         let tx = tx.send(Ok("hello ".into())).wait().unwrap(); | ||||
|         tx.send(Ok("world".into())).wait().unwrap(); | ||||
|     }); | ||||
|  | ||||
|     let total = body.concat2().wait().unwrap(); | ||||
|     assert_eq!(total.as_ref(), b"hello world"); | ||||
|  | ||||
| } | ||||
							
								
								
									
										108
									
								
								src/proto/chunk.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/proto/chunk.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| use std::fmt; | ||||
| //use std::mem; | ||||
|  | ||||
| use bytes::Bytes; | ||||
|  | ||||
| /// A piece of a message body. | ||||
| pub struct Chunk(Inner); | ||||
|  | ||||
| enum Inner { | ||||
|     Shared(Bytes), | ||||
| } | ||||
|  | ||||
| impl From<Vec<u8>> for Chunk { | ||||
|     #[inline] | ||||
|     fn from(v: Vec<u8>) -> Chunk { | ||||
|         Chunk::from(Bytes::from(v)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<&'static [u8]> for Chunk { | ||||
|     #[inline] | ||||
|     fn from(slice: &'static [u8]) -> Chunk { | ||||
|         Chunk::from(Bytes::from_static(slice)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<String> for Chunk { | ||||
|     #[inline] | ||||
|     fn from(s: String) -> Chunk { | ||||
|         s.into_bytes().into() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<&'static str> for Chunk { | ||||
|     #[inline] | ||||
|     fn from(slice: &'static str) -> Chunk { | ||||
|         slice.as_bytes().into() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Bytes> for Chunk { | ||||
|     #[inline] | ||||
|     fn from(mem: Bytes) -> Chunk { | ||||
|         Chunk(Inner::Shared(mem)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Chunk> for Bytes { | ||||
|     #[inline] | ||||
|     fn from(chunk: Chunk) -> Bytes { | ||||
|         match chunk.0 { | ||||
|             Inner::Shared(bytes) => bytes, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ::std::ops::Deref for Chunk { | ||||
|     type Target = [u8]; | ||||
|  | ||||
|     #[inline] | ||||
|     fn deref(&self) -> &Self::Target { | ||||
|         self.as_ref() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl AsRef<[u8]> for Chunk { | ||||
|     #[inline] | ||||
|     fn as_ref(&self) -> &[u8] { | ||||
|         match self.0 { | ||||
|             Inner::Shared(ref slice) => slice, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for Chunk { | ||||
|     #[inline] | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         fmt::Debug::fmt(self.as_ref(), f) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for Chunk { | ||||
|     #[inline] | ||||
|     fn default() -> Chunk { | ||||
|         Chunk(Inner::Shared(Bytes::new())) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl IntoIterator for Chunk { | ||||
|     type Item = u8; | ||||
|     type IntoIter = <Bytes as IntoIterator>::IntoIter; | ||||
|  | ||||
|     #[inline] | ||||
|     fn into_iter(self) -> Self::IntoIter { | ||||
|         match self.0 { | ||||
|             Inner::Shared(bytes) => bytes.into_iter(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Extend<u8> for Chunk { | ||||
|     #[inline] | ||||
|     fn extend<T>(&mut self, iter: T) where T: IntoIterator<Item=u8> { | ||||
|         match self.0 { | ||||
|             Inner::Shared(ref mut bytes) => bytes.extend(iter) | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										974
									
								
								src/proto/conn.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										974
									
								
								src/proto/conn.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,974 @@ | ||||
| use std::fmt; | ||||
| use std::io::{self, Write}; | ||||
| use std::marker::PhantomData; | ||||
|  | ||||
| use futures::{Poll, Async, AsyncSink, Stream, Sink, StartSend}; | ||||
| use futures::task::Task; | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
| use tokio_proto::streaming::pipeline::{Frame, Transport}; | ||||
|  | ||||
| use proto::{Http1Transaction}; | ||||
| use super::io::{Cursor, Buffered}; | ||||
| use super::h1::{Encoder, Decoder}; | ||||
| use method::Method; | ||||
| use version::HttpVersion; | ||||
|  | ||||
|  | ||||
| /// This handles a connection, which will have been established over an | ||||
| /// `AsyncRead + AsyncWrite` (like a socket), and will likely include multiple | ||||
| /// `Transaction`s over HTTP. | ||||
| /// | ||||
| /// The connection will determine when a message begins and ends as well as | ||||
| /// determine if this  connection can be kept alive after the message, | ||||
| /// or if it is complete. | ||||
| pub struct Conn<I, B, T, K = KA> { | ||||
|     io: Buffered<I>, | ||||
|     state: State<B, K>, | ||||
|     _marker: PhantomData<T> | ||||
| } | ||||
|  | ||||
| impl<I, B, T, K> Conn<I, B, T, K> | ||||
| where I: AsyncRead + AsyncWrite, | ||||
|       B: AsRef<[u8]>, | ||||
|       T: Http1Transaction, | ||||
|       K: KeepAlive | ||||
| { | ||||
|     pub fn new(io: I, keep_alive: K) -> Conn<I, B, T, K> { | ||||
|         Conn { | ||||
|             io: Buffered::new(io), | ||||
|             state: State { | ||||
|                 keep_alive: keep_alive, | ||||
|                 method: None, | ||||
|                 read_task: None, | ||||
|                 reading: Reading::Init, | ||||
|                 writing: Writing::Init, | ||||
|             }, | ||||
|             _marker: PhantomData, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn set_flush_pipeline(&mut self, enabled: bool) { | ||||
|         self.io.set_flush_pipeline(enabled); | ||||
|     } | ||||
|  | ||||
|     fn poll2(&mut self) -> Poll<Option<Frame<super::MessageHead<T::Incoming>, super::Chunk, ::Error>>, io::Error> { | ||||
|         trace!("Conn::poll()"); | ||||
|  | ||||
|         loop { | ||||
|             if self.is_read_closed() { | ||||
|                 trace!("Conn::poll when closed"); | ||||
|                 return Ok(Async::Ready(None)); | ||||
|             } else if self.can_read_head() { | ||||
|                 return self.read_head(); | ||||
|             } else if self.can_write_continue() { | ||||
|                 try_nb!(self.flush()); | ||||
|             } else if self.can_read_body() { | ||||
|                 return self.read_body() | ||||
|                     .map(|async| async.map(|chunk| Some(Frame::Body { | ||||
|                         chunk: chunk | ||||
|                     }))) | ||||
|                     .or_else(|err| { | ||||
|                         self.state.close_read(); | ||||
|                         Ok(Async::Ready(Some(Frame::Error { error: err.into() }))) | ||||
|                     }); | ||||
|             } else { | ||||
|                 trace!("poll when on keep-alive"); | ||||
|                 self.maybe_park_read(); | ||||
|                 return Ok(Async::NotReady); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn is_read_closed(&self) -> bool { | ||||
|         self.state.is_read_closed() | ||||
|     } | ||||
|  | ||||
|     #[allow(unused)] | ||||
|     fn is_write_closed(&self) -> bool { | ||||
|         self.state.is_write_closed() | ||||
|     } | ||||
|  | ||||
|     fn can_read_head(&self) -> bool { | ||||
|         match self.state.reading { | ||||
|             Reading::Init => true, | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn can_write_continue(&self) -> bool { | ||||
|         match self.state.writing { | ||||
|             Writing::Continue(..) => true, | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn can_read_body(&self) -> bool { | ||||
|         match self.state.reading { | ||||
|             Reading::Body(..) => true, | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn read_head(&mut self) -> Poll<Option<Frame<super::MessageHead<T::Incoming>, super::Chunk, ::Error>>, io::Error> { | ||||
|         debug_assert!(self.can_read_head()); | ||||
|         trace!("Conn::read_head"); | ||||
|  | ||||
|         let (version, head) = match self.io.parse::<T>() { | ||||
|             Ok(Async::Ready(head)) => (head.version, head), | ||||
|             Ok(Async::NotReady) => return Ok(Async::NotReady), | ||||
|             Err(e) => { | ||||
|                 let must_respond_with_error = !self.state.is_idle(); | ||||
|                 self.state.close_read(); | ||||
|                 self.io.consume_leading_lines(); | ||||
|                 let was_mid_parse = !self.io.read_buf().is_empty(); | ||||
|                 return if was_mid_parse || must_respond_with_error { | ||||
|                     debug!("parse error ({}) with {} bytes", e, self.io.read_buf().len()); | ||||
|                     Ok(Async::Ready(Some(Frame::Error { error: e }))) | ||||
|                 } else { | ||||
|                     debug!("read eof"); | ||||
|                     Ok(Async::Ready(None)) | ||||
|                 }; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         match version { | ||||
|             HttpVersion::Http10 | HttpVersion::Http11 => { | ||||
|                 let decoder = match T::decoder(&head, &mut self.state.method) { | ||||
|                     Ok(d) => d, | ||||
|                     Err(e) => { | ||||
|                         debug!("decoder error = {:?}", e); | ||||
|                         self.state.close_read(); | ||||
|                         return Ok(Async::Ready(Some(Frame::Error { error: e }))); | ||||
|                     } | ||||
|                 }; | ||||
|                 self.state.busy(); | ||||
|                 if head.expecting_continue() { | ||||
|                     let msg = b"HTTP/1.1 100 Continue\r\n\r\n"; | ||||
|                     self.state.writing = Writing::Continue(Cursor::new(msg)); | ||||
|                 } | ||||
|                 let wants_keep_alive = head.should_keep_alive(); | ||||
|                 self.state.keep_alive &= wants_keep_alive; | ||||
|                 let (body, reading) = if decoder.is_eof() { | ||||
|                     (false, Reading::KeepAlive) | ||||
|                 } else { | ||||
|                     (true, Reading::Body(decoder)) | ||||
|                 }; | ||||
|                 self.state.reading = reading; | ||||
|                 Ok(Async::Ready(Some(Frame::Message { message: head, body: body }))) | ||||
|             }, | ||||
|             _ => { | ||||
|                 error!("unimplemented HTTP Version = {:?}", version); | ||||
|                 self.state.close_read(); | ||||
|                 Ok(Async::Ready(Some(Frame::Error { error: ::Error::Version }))) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn read_body(&mut self) -> Poll<Option<super::Chunk>, io::Error> { | ||||
|         debug_assert!(self.can_read_body()); | ||||
|  | ||||
|         trace!("Conn::read_body"); | ||||
|  | ||||
|         let (reading, ret) = match self.state.reading { | ||||
|             Reading::Body(ref mut decoder) => { | ||||
|                 let slice = try_ready!(decoder.decode(&mut self.io)); | ||||
|                 if !slice.is_empty() { | ||||
|                     return Ok(Async::Ready(Some(super::Chunk::from(slice)))); | ||||
|                 } else if decoder.is_eof() { | ||||
|                     (Reading::KeepAlive, Ok(Async::Ready(None))) | ||||
|                 } else { | ||||
|                     (Reading::Closed, Ok(Async::Ready(None))) | ||||
|                 } | ||||
|  | ||||
|             }, | ||||
|             Reading::Init | Reading::KeepAlive | Reading::Closed => unreachable!() | ||||
|         }; | ||||
|         self.state.reading = reading; | ||||
|         ret | ||||
|     } | ||||
|  | ||||
|     fn maybe_park_read(&mut self) { | ||||
|         if !self.io.is_read_blocked() { | ||||
|             // the Io object is ready to read, which means it will never alert | ||||
|             // us that it is ready until we drain it. However, we're currently | ||||
|             // finished reading, so we need to park the task to be able to | ||||
|             // wake back up later when more reading should happen. | ||||
|             let park = self.state.read_task.as_ref() | ||||
|                 .map(|t| !t.will_notify_current()) | ||||
|                 .unwrap_or(true); | ||||
|             if park { | ||||
|                 trace!("parking current task"); | ||||
|                 self.state.read_task = Some(::futures::task::current()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn maybe_notify(&mut self) { | ||||
|         // its possible that we returned NotReady from poll() without having | ||||
|         // exhausted the underlying Io. We would have done this when we | ||||
|         // determined we couldn't keep reading until we knew how writing | ||||
|         // would finish. | ||||
|         // | ||||
|         // When writing finishes, we need to wake the task up in case there | ||||
|         // is more reading that can be done, to start a new message. | ||||
|         let wants_read = match self.state.reading { | ||||
|             Reading::Body(..) | | ||||
|             Reading::KeepAlive => return, | ||||
|             Reading::Init => true, | ||||
|             Reading::Closed => false, | ||||
|         }; | ||||
|  | ||||
|         match self.state.writing { | ||||
|             Writing::Continue(..) | | ||||
|             Writing::Body(..) | | ||||
|             Writing::Ending(..) => return, | ||||
|             Writing::Init | | ||||
|             Writing::KeepAlive | | ||||
|             Writing::Closed => (), | ||||
|         } | ||||
|  | ||||
|         if !self.io.is_read_blocked() { | ||||
|             if wants_read && self.io.read_buf().is_empty() { | ||||
|                 match self.io.read_from_io() { | ||||
|                     Ok(Async::Ready(_)) => (), | ||||
|                     Ok(Async::NotReady) => { | ||||
|                         trace!("maybe_notify; read_from_io blocked"); | ||||
|                         return | ||||
|                     }, | ||||
|                     Err(e) => { | ||||
|                         trace!("maybe_notify read_from_io error: {}", e); | ||||
|                         self.state.close(); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if let Some(ref task) = self.state.read_task { | ||||
|                 task.notify(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn try_keep_alive(&mut self) { | ||||
|         self.state.try_keep_alive(); | ||||
|         self.maybe_notify(); | ||||
|     } | ||||
|  | ||||
|     fn can_write_head(&self) -> bool { | ||||
|         match self.state.writing { | ||||
|             Writing::Continue(..) | Writing::Init => true, | ||||
|             _ => false | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn can_write_body(&self) -> bool { | ||||
|         match self.state.writing { | ||||
|             Writing::Body(..) => true, | ||||
|             Writing::Continue(..) | | ||||
|             Writing::Init | | ||||
|             Writing::Ending(..) | | ||||
|             Writing::KeepAlive | | ||||
|             Writing::Closed => false, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn has_queued_body(&self) -> bool { | ||||
|         match self.state.writing { | ||||
|             Writing::Body(_, Some(_)) => true, | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn write_head(&mut self, head: super::MessageHead<T::Outgoing>, body: bool) { | ||||
|         debug_assert!(self.can_write_head()); | ||||
|  | ||||
|         let wants_keep_alive = head.should_keep_alive(); | ||||
|         self.state.keep_alive &= wants_keep_alive; | ||||
|         let buf = self.io.write_buf_mut(); | ||||
|         // if a 100-continue has started but not finished sending, tack the | ||||
|         // remainder on to the start of the buffer. | ||||
|         if let Writing::Continue(ref pending) = self.state.writing { | ||||
|             if pending.has_started() { | ||||
|                 buf.extend_from_slice(pending.buf()); | ||||
|             } | ||||
|         } | ||||
|         let encoder = T::encode(head, body, &mut self.state.method, buf); | ||||
|         self.state.writing = if !encoder.is_eof() { | ||||
|             Writing::Body(encoder, None) | ||||
|         } else { | ||||
|             Writing::KeepAlive | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     fn write_body(&mut self, chunk: Option<B>) -> StartSend<Option<B>, io::Error> { | ||||
|         debug_assert!(self.can_write_body()); | ||||
|  | ||||
|         if self.has_queued_body() { | ||||
|             try!(self.flush()); | ||||
|         } | ||||
|  | ||||
|         let state = match self.state.writing { | ||||
|             Writing::Body(ref mut encoder, ref mut queued) => { | ||||
|                 if queued.is_some() { | ||||
|                     return Ok(AsyncSink::NotReady(chunk)); | ||||
|                 } | ||||
|                 if let Some(chunk) = chunk { | ||||
|                     if chunk.as_ref().is_empty() { | ||||
|                         return Ok(AsyncSink::Ready); | ||||
|                     } | ||||
|  | ||||
|                     let mut cursor = Cursor::new(chunk); | ||||
|                     match encoder.encode(&mut self.io, cursor.buf()) { | ||||
|                         Ok(n) => { | ||||
|                             cursor.consume(n); | ||||
|  | ||||
|                             if !cursor.is_written() { | ||||
|                                 trace!("Conn::start_send frame not written, queued"); | ||||
|                                 *queued = Some(cursor); | ||||
|                             } | ||||
|                         }, | ||||
|                         Err(e) => match e.kind() { | ||||
|                             io::ErrorKind::WouldBlock => { | ||||
|                                 trace!("Conn::start_send frame not written, queued"); | ||||
|                                 *queued = Some(cursor); | ||||
|                             }, | ||||
|                             _ => return Err(e) | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     if encoder.is_eof() { | ||||
|                         Writing::KeepAlive | ||||
|                     } else { | ||||
|                         return Ok(AsyncSink::Ready); | ||||
|                     } | ||||
|                 } else { | ||||
|                     // end of stream, that means we should try to eof | ||||
|                     match encoder.eof() { | ||||
|                         Ok(Some(end)) => Writing::Ending(Cursor::new(end)), | ||||
|                         Ok(None) => Writing::KeepAlive, | ||||
|                         Err(_not_eof) => Writing::Closed, | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             _ => unreachable!(), | ||||
|         }; | ||||
|         self.state.writing = state; | ||||
|         Ok(AsyncSink::Ready) | ||||
|     } | ||||
|  | ||||
|     fn write_queued(&mut self) -> Poll<(), io::Error> { | ||||
|         trace!("Conn::write_queued()"); | ||||
|         let state = match self.state.writing { | ||||
|             Writing::Continue(ref mut queued) => { | ||||
|                 let n = self.io.buffer(queued.buf()); | ||||
|                 queued.consume(n); | ||||
|                 if queued.is_written() { | ||||
|                     Writing::Init | ||||
|                 } else { | ||||
|                     return Ok(Async::NotReady); | ||||
|                 } | ||||
|             } | ||||
|             Writing::Body(ref mut encoder, ref mut queued) => { | ||||
|                 let complete = if let Some(chunk) = queued.as_mut() { | ||||
|                     let n = try_nb!(encoder.encode(&mut self.io, chunk.buf())); | ||||
|                     chunk.consume(n); | ||||
|                     chunk.is_written() | ||||
|                 } else { | ||||
|                     true | ||||
|                 }; | ||||
|                 trace!("Conn::write_queued complete = {}", complete); | ||||
|                 return if complete { | ||||
|                     *queued = None; | ||||
|                     Ok(Async::Ready(())) | ||||
|                 } else { | ||||
|                     Ok(Async::NotReady) | ||||
|                 }; | ||||
|             }, | ||||
|             Writing::Ending(ref mut ending) => { | ||||
|                 let n = self.io.buffer(ending.buf()); | ||||
|                 ending.consume(n); | ||||
|                 if ending.is_written() { | ||||
|                     Writing::KeepAlive | ||||
|                 } else { | ||||
|                     return Ok(Async::NotReady); | ||||
|                 } | ||||
|             }, | ||||
|             _ => return Ok(Async::Ready(())), | ||||
|         }; | ||||
|         self.state.writing = state; | ||||
|         Ok(Async::Ready(())) | ||||
|     } | ||||
|  | ||||
|     fn flush(&mut self) -> Poll<(), io::Error> { | ||||
|         loop { | ||||
|             let queue_finished = try!(self.write_queued()).is_ready(); | ||||
|             try_nb!(self.io.flush()); | ||||
|             if queue_finished { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         self.try_keep_alive(); | ||||
|         trace!("flushed {:?}", self.state); | ||||
|         Ok(Async::Ready(())) | ||||
|  | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<I, B, T, K> Stream for Conn<I, B, T, K> | ||||
| where I: AsyncRead + AsyncWrite, | ||||
|       B: AsRef<[u8]>, | ||||
|       T: Http1Transaction, | ||||
|       K: KeepAlive, | ||||
|       T::Outgoing: fmt::Debug { | ||||
|     type Item = Frame<super::MessageHead<T::Incoming>, super::Chunk, ::Error>; | ||||
|     type Error = io::Error; | ||||
|  | ||||
|     #[inline] | ||||
|     fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { | ||||
|         self.poll2().map_err(|err| { | ||||
|             debug!("poll error: {}", err); | ||||
|             err | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<I, B, T, K> Sink for Conn<I, B, T, K> | ||||
| where I: AsyncRead + AsyncWrite, | ||||
|       B: AsRef<[u8]>, | ||||
|       T: Http1Transaction, | ||||
|       K: KeepAlive, | ||||
|       T::Outgoing: fmt::Debug { | ||||
|     type SinkItem = Frame<super::MessageHead<T::Outgoing>, B, ::Error>; | ||||
|     type SinkError = io::Error; | ||||
|  | ||||
|     #[inline] | ||||
|     fn start_send(&mut self, frame: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> { | ||||
|         trace!("Conn::start_send( frame={:?} )", DebugFrame(&frame)); | ||||
|  | ||||
|         let frame: Self::SinkItem = match frame { | ||||
|             Frame::Message { message: head, body } => { | ||||
|                 if self.can_write_head() { | ||||
|                     self.write_head(head, body); | ||||
|                     return Ok(AsyncSink::Ready); | ||||
|                 } else { | ||||
|                     Frame::Message { message: head, body: body } | ||||
|                 } | ||||
|             }, | ||||
|             Frame::Body { chunk } => { | ||||
|                 if self.can_write_body() { | ||||
|                     return self.write_body(chunk) | ||||
|                         .map(|async| { | ||||
|                             match async { | ||||
|                                 AsyncSink::Ready => AsyncSink::Ready, | ||||
|                                 AsyncSink::NotReady(chunk) => AsyncSink::NotReady(Frame::Body { | ||||
|                                     chunk: chunk, | ||||
|                                 }) | ||||
|                             } | ||||
|                         }); | ||||
|                 // This allows when chunk is `None`, or `Some([])`. | ||||
|                 } else if chunk.as_ref().map(|c| c.as_ref().len()).unwrap_or(0) == 0 { | ||||
|                     return Ok(AsyncSink::Ready); | ||||
|                 } else { | ||||
|                     Frame::Body { chunk: chunk } | ||||
|                 } | ||||
|             }, | ||||
|             Frame::Error { error } => { | ||||
|                 debug!("received error, closing: {:?}", error); | ||||
|                 self.state.close(); | ||||
|                 return Ok(AsyncSink::Ready); | ||||
|             }, | ||||
|         }; | ||||
|  | ||||
|         error!("writing illegal frame; state={:?}, frame={:?}", self.state.writing, DebugFrame(&frame)); | ||||
|         Err(io::Error::new(io::ErrorKind::InvalidInput, "illegal frame")) | ||||
|  | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { | ||||
|         trace!("Conn::poll_complete()"); | ||||
|         self.flush().map_err(|err| { | ||||
|             debug!("error writing: {}", err); | ||||
|             err | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn close(&mut self) -> Poll<(), Self::SinkError> { | ||||
|         try_ready!(self.poll_complete()); | ||||
|         self.io.io_mut().shutdown().map_err(|err| { | ||||
|             debug!("error closing: {}", err); | ||||
|             err | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<I, B, T, K> Transport for Conn<I, B, T, K> | ||||
| where I: AsyncRead + AsyncWrite + 'static, | ||||
|       B: AsRef<[u8]> + 'static, | ||||
|       T: Http1Transaction + 'static, | ||||
|       K: KeepAlive + 'static, | ||||
|       T::Outgoing: fmt::Debug {} | ||||
|  | ||||
| impl<I, B: AsRef<[u8]>, T, K: fmt::Debug> fmt::Debug for Conn<I, B, T, K> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Conn") | ||||
|             .field("state", &self.state) | ||||
|             .field("io", &self.io) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct State<B, K> { | ||||
|     keep_alive: K, | ||||
|     method: Option<Method>, | ||||
|     read_task: Option<Task>, | ||||
|     reading: Reading, | ||||
|     writing: Writing<B>, | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| enum Reading { | ||||
|     Init, | ||||
|     Body(Decoder), | ||||
|     KeepAlive, | ||||
|     Closed, | ||||
| } | ||||
|  | ||||
| enum Writing<B> { | ||||
|     Continue(Cursor<&'static [u8]>), | ||||
|     Init, | ||||
|     Body(Encoder, Option<Cursor<B>>), | ||||
|     Ending(Cursor<&'static [u8]>), | ||||
|     KeepAlive, | ||||
|     Closed, | ||||
| } | ||||
|  | ||||
| impl<B: AsRef<[u8]>, K: fmt::Debug> fmt::Debug for State<B, K> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("State") | ||||
|             .field("reading", &self.reading) | ||||
|             .field("writing", &self.writing) | ||||
|             .field("keep_alive", &self.keep_alive) | ||||
|             .field("method", &self.method) | ||||
|             .field("read_task", &self.read_task) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<B: AsRef<[u8]>> fmt::Debug for Writing<B> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         match *self { | ||||
|             Writing::Continue(ref buf) => f.debug_tuple("Continue") | ||||
|                 .field(buf) | ||||
|                 .finish(), | ||||
|             Writing::Init => f.write_str("Init"), | ||||
|             Writing::Body(ref enc, ref queued) => f.debug_tuple("Body") | ||||
|                 .field(enc) | ||||
|                 .field(queued) | ||||
|                 .finish(), | ||||
|             Writing::Ending(ref ending) => f.debug_tuple("Ending") | ||||
|                 .field(ending) | ||||
|                 .finish(), | ||||
|             Writing::KeepAlive => f.write_str("KeepAlive"), | ||||
|             Writing::Closed => f.write_str("Closed"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ::std::ops::BitAndAssign<bool> for KA { | ||||
|     fn bitand_assign(&mut self, enabled: bool) { | ||||
|         if !enabled { | ||||
|             *self = KA::Disabled; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub trait KeepAlive: fmt::Debug + ::std::ops::BitAndAssign<bool> { | ||||
|     fn busy(&mut self); | ||||
|     fn disable(&mut self); | ||||
|     fn idle(&mut self); | ||||
|     fn status(&self) -> KA; | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy, Debug)] | ||||
| pub enum KA { | ||||
|     Idle, | ||||
|     Busy, | ||||
|     Disabled, | ||||
| } | ||||
|  | ||||
| impl Default for KA { | ||||
|     fn default() -> KA { | ||||
|         KA::Busy | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl KeepAlive for KA { | ||||
|     fn idle(&mut self) { | ||||
|         *self = KA::Idle; | ||||
|     } | ||||
|  | ||||
|     fn busy(&mut self) { | ||||
|         *self = KA::Busy; | ||||
|     } | ||||
|  | ||||
|     fn disable(&mut self) { | ||||
|         *self = KA::Disabled; | ||||
|     } | ||||
|  | ||||
|     fn status(&self) -> KA { | ||||
|         *self | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<B, K: KeepAlive> State<B, K> { | ||||
|     fn close(&mut self) { | ||||
|         trace!("State::close()"); | ||||
|         self.reading = Reading::Closed; | ||||
|         self.writing = Writing::Closed; | ||||
|         self.keep_alive.disable(); | ||||
|     } | ||||
|  | ||||
|     fn close_read(&mut self) { | ||||
|         trace!("State::close_read()"); | ||||
|         self.reading = Reading::Closed; | ||||
|         self.read_task = None; | ||||
|         self.keep_alive.disable(); | ||||
|     } | ||||
|  | ||||
|     fn try_keep_alive(&mut self) { | ||||
|         match (&self.reading, &self.writing) { | ||||
|             (&Reading::KeepAlive, &Writing::KeepAlive) => { | ||||
|                 if let KA::Busy = self.keep_alive.status() { | ||||
|                     self.idle(); | ||||
|                 } else { | ||||
|                     self.close(); | ||||
|                 } | ||||
|             }, | ||||
|             (&Reading::Closed, &Writing::KeepAlive) | | ||||
|             (&Reading::KeepAlive, &Writing::Closed) => { | ||||
|                 self.close() | ||||
|             } | ||||
|             _ => () | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn is_idle(&self) -> bool { | ||||
|         if let KA::Idle = self.keep_alive.status() { | ||||
|             true | ||||
|         } else { | ||||
|             false | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn busy(&mut self) { | ||||
|         if let KA::Disabled = self.keep_alive.status() { | ||||
|             return; | ||||
|         } | ||||
|         self.keep_alive.busy(); | ||||
|     } | ||||
|  | ||||
|     fn idle(&mut self) { | ||||
|         self.method = None; | ||||
|         self.reading = Reading::Init; | ||||
|         self.writing = Writing::Init; | ||||
|         self.keep_alive.idle(); | ||||
|     } | ||||
|  | ||||
|     fn is_read_closed(&self) -> bool { | ||||
|         match self.reading { | ||||
|             Reading::Closed => true, | ||||
|             _ => false | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[allow(unused)] | ||||
|     fn is_write_closed(&self) -> bool { | ||||
|         match self.writing { | ||||
|             Writing::Closed => true, | ||||
|             _ => false | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| // The DebugFrame and DebugChunk are simple Debug implementations that allow | ||||
| // us to dump the frame into logs, without logging the entirety of the bytes. | ||||
| struct DebugFrame<'a, T: fmt::Debug + 'a, B: AsRef<[u8]> + 'a>(&'a Frame<super::MessageHead<T>, B, ::Error>); | ||||
|  | ||||
| impl<'a, T: fmt::Debug + 'a, B: AsRef<[u8]> + 'a> fmt::Debug for DebugFrame<'a, T, B> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         match *self.0 { | ||||
|             Frame::Message { ref body, .. } => { | ||||
|                 f.debug_struct("Message") | ||||
|                     .field("body", body) | ||||
|                     .finish() | ||||
|             }, | ||||
|             Frame::Body { chunk: Some(ref chunk) } => { | ||||
|                 f.debug_struct("Body") | ||||
|                     .field("bytes", &chunk.as_ref().len()) | ||||
|                     .finish() | ||||
|             }, | ||||
|             Frame::Body { chunk: None } => { | ||||
|                 f.debug_struct("Body") | ||||
|                     .field("bytes", &None::<()>) | ||||
|                     .finish() | ||||
|             }, | ||||
|             Frame::Error { ref error } => { | ||||
|                 f.debug_struct("Error") | ||||
|                     .field("error", error) | ||||
|                     .finish() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use futures::{Async, Future, Stream, Sink}; | ||||
|     use futures::future; | ||||
|     use tokio_proto::streaming::pipeline::Frame; | ||||
|  | ||||
|     use proto::{self, MessageHead, ServerTransaction}; | ||||
|     use super::super::h1::Encoder; | ||||
|     use mock::AsyncIo; | ||||
|  | ||||
|     use super::{Conn, Reading, Writing}; | ||||
|     use ::uri::Uri; | ||||
|  | ||||
|     use std::str::FromStr; | ||||
|  | ||||
|     impl<T> Writing<T> { | ||||
|         fn is_queued(&self) -> bool { | ||||
|             match *self { | ||||
|                 Writing::Body(_, Some(_)) => true, | ||||
|                 _ => false, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_conn_init_read() { | ||||
|         let good_message = b"GET / HTTP/1.1\r\n\r\n".to_vec(); | ||||
|         let len = good_message.len(); | ||||
|         let io = AsyncIo::new_buf(good_message, len); | ||||
|         let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default()); | ||||
|  | ||||
|         match conn.poll().unwrap() { | ||||
|             Async::Ready(Some(Frame::Message { message, body: false })) => { | ||||
|                 assert_eq!(message, MessageHead { | ||||
|                     subject: ::proto::RequestLine(::Get, Uri::from_str("/").unwrap()), | ||||
|                     .. MessageHead::default() | ||||
|                 }) | ||||
|             }, | ||||
|             f => panic!("frame is not Frame::Message: {:?}", f) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_conn_parse_partial() { | ||||
|         let _: Result<(), ()> = future::lazy(|| { | ||||
|             let good_message = b"GET / HTTP/1.1\r\nHost: foo.bar\r\n\r\n".to_vec(); | ||||
|             let io = AsyncIo::new_buf(good_message, 10); | ||||
|             let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default()); | ||||
|             assert!(conn.poll().unwrap().is_not_ready()); | ||||
|             conn.io.io_mut().block_in(50); | ||||
|             let async = conn.poll().unwrap(); | ||||
|             assert!(async.is_ready()); | ||||
|             match async { | ||||
|                 Async::Ready(Some(Frame::Message { .. })) => (), | ||||
|                 f => panic!("frame is not Message: {:?}", f), | ||||
|             } | ||||
|             Ok(()) | ||||
|         }).wait(); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_conn_init_read_eof_idle() { | ||||
|         let io = AsyncIo::new_buf(vec![], 1); | ||||
|         let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default()); | ||||
|         conn.state.idle(); | ||||
|  | ||||
|         match conn.poll().unwrap() { | ||||
|             Async::Ready(None) => {}, | ||||
|             other => panic!("frame is not None: {:?}", other) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_conn_init_read_eof_idle_partial_parse() { | ||||
|         let io = AsyncIo::new_buf(b"GET / HTTP/1.1".to_vec(), 100); | ||||
|         let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default()); | ||||
|         conn.state.idle(); | ||||
|  | ||||
|         match conn.poll().unwrap() { | ||||
|             Async::Ready(Some(Frame::Error { .. })) => {}, | ||||
|             other => panic!("frame is not Error: {:?}", other) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_conn_init_read_eof_busy() { | ||||
|         let io = AsyncIo::new_buf(vec![], 1); | ||||
|         let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default()); | ||||
|         conn.state.busy(); | ||||
|  | ||||
|         match conn.poll().unwrap() { | ||||
|             Async::Ready(Some(Frame::Error { .. })) => {}, | ||||
|             other => panic!("frame is not Error: {:?}", other) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_conn_closed_read() { | ||||
|         let io = AsyncIo::new_buf(vec![], 0); | ||||
|         let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default()); | ||||
|         conn.state.close(); | ||||
|  | ||||
|         match conn.poll().unwrap() { | ||||
|             Async::Ready(None) => {}, | ||||
|             other => panic!("frame is not None: {:?}", other) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_conn_body_write_length() { | ||||
|         extern crate pretty_env_logger; | ||||
|         let _ = pretty_env_logger::init(); | ||||
|         let _: Result<(), ()> = future::lazy(|| { | ||||
|             let io = AsyncIo::new_buf(vec![], 0); | ||||
|             let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default()); | ||||
|             let max = ::proto::io::MAX_BUFFER_SIZE + 4096; | ||||
|             conn.state.writing = Writing::Body(Encoder::length((max * 2) as u64), None); | ||||
|  | ||||
|             assert!(conn.start_send(Frame::Body { chunk: Some(vec![b'a'; 1024 * 8].into()) }).unwrap().is_ready()); | ||||
|             assert!(!conn.state.writing.is_queued()); | ||||
|  | ||||
|             assert!(conn.start_send(Frame::Body { chunk: Some(vec![b'b'; max].into()) }).unwrap().is_ready()); | ||||
|             assert!(conn.state.writing.is_queued()); | ||||
|  | ||||
|             assert!(conn.start_send(Frame::Body { chunk: Some(vec![b'b'; 1024 * 8].into()) }).unwrap().is_not_ready()); | ||||
|  | ||||
|             conn.io.io_mut().block_in(1024 * 3); | ||||
|             assert!(conn.poll_complete().unwrap().is_not_ready()); | ||||
|             conn.io.io_mut().block_in(1024 * 3); | ||||
|             assert!(conn.poll_complete().unwrap().is_not_ready()); | ||||
|             conn.io.io_mut().block_in(max * 2); | ||||
|             assert!(conn.poll_complete().unwrap().is_ready()); | ||||
|  | ||||
|             assert!(conn.start_send(Frame::Body { chunk: Some(vec![b'c'; 1024 * 8].into()) }).unwrap().is_ready()); | ||||
|             Ok(()) | ||||
|         }).wait(); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_conn_body_write_chunked() { | ||||
|         let _: Result<(), ()> = future::lazy(|| { | ||||
|             let io = AsyncIo::new_buf(vec![], 4096); | ||||
|             let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default()); | ||||
|             conn.state.writing = Writing::Body(Encoder::chunked(), None); | ||||
|  | ||||
|             assert!(conn.start_send(Frame::Body { chunk: Some("headers".into()) }).unwrap().is_ready()); | ||||
|             assert!(conn.start_send(Frame::Body { chunk: Some(vec![b'x'; 8192].into()) }).unwrap().is_ready()); | ||||
|             Ok(()) | ||||
|         }).wait(); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_conn_body_flush() { | ||||
|         let _: Result<(), ()> = future::lazy(|| { | ||||
|             let io = AsyncIo::new_buf(vec![], 1024 * 1024 * 5); | ||||
|             let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default()); | ||||
|             conn.state.writing = Writing::Body(Encoder::length(1024 * 1024), None); | ||||
|             assert!(conn.start_send(Frame::Body { chunk: Some(vec![b'a'; 1024 * 1024].into()) }).unwrap().is_ready()); | ||||
|             assert!(conn.state.writing.is_queued()); | ||||
|             assert!(conn.poll_complete().unwrap().is_ready()); | ||||
|             assert!(!conn.state.writing.is_queued()); | ||||
|             assert!(conn.io.io_mut().flushed()); | ||||
|  | ||||
|             Ok(()) | ||||
|         }).wait(); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_conn_parking() { | ||||
|         use std::sync::Arc; | ||||
|         use futures::executor::Notify; | ||||
|         use futures::executor::NotifyHandle; | ||||
|  | ||||
|         struct Car { | ||||
|             permit: bool, | ||||
|         } | ||||
|         impl Notify for Car { | ||||
|             fn notify(&self, _id: usize) { | ||||
|                 assert!(self.permit, "unparked without permit"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         fn car(permit: bool) -> NotifyHandle { | ||||
|             Arc::new(Car { | ||||
|                 permit: permit, | ||||
|             }).into() | ||||
|         } | ||||
|  | ||||
|         // test that once writing is done, unparks | ||||
|         let f = future::lazy(|| { | ||||
|             let io = AsyncIo::new_buf(vec![], 4096); | ||||
|             let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default()); | ||||
|             conn.state.reading = Reading::KeepAlive; | ||||
|             assert!(conn.poll().unwrap().is_not_ready()); | ||||
|  | ||||
|             conn.state.writing = Writing::KeepAlive; | ||||
|             assert!(conn.poll_complete().unwrap().is_ready()); | ||||
|             Ok::<(), ()>(()) | ||||
|         }); | ||||
|         ::futures::executor::spawn(f).poll_future_notify(&car(true), 0).unwrap(); | ||||
|  | ||||
|  | ||||
|         // test that flushing when not waiting on read doesn't unpark | ||||
|         let f = future::lazy(|| { | ||||
|             let io = AsyncIo::new_buf(vec![], 4096); | ||||
|             let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default()); | ||||
|             conn.state.writing = Writing::KeepAlive; | ||||
|             assert!(conn.poll_complete().unwrap().is_ready()); | ||||
|             Ok::<(), ()>(()) | ||||
|         }); | ||||
|         ::futures::executor::spawn(f).poll_future_notify(&car(false), 0).unwrap(); | ||||
|  | ||||
|  | ||||
|         // test that flushing and writing isn't done doesn't unpark | ||||
|         let f = future::lazy(|| { | ||||
|             let io = AsyncIo::new_buf(vec![], 4096); | ||||
|             let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default()); | ||||
|             conn.state.reading = Reading::KeepAlive; | ||||
|             assert!(conn.poll().unwrap().is_not_ready()); | ||||
|             conn.state.writing = Writing::Body(Encoder::length(5_000), None); | ||||
|             assert!(conn.poll_complete().unwrap().is_ready()); | ||||
|             Ok::<(), ()>(()) | ||||
|         }); | ||||
|         ::futures::executor::spawn(f).poll_future_notify(&car(false), 0).unwrap(); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_conn_closed_write() { | ||||
|         let io = AsyncIo::new_buf(vec![], 0); | ||||
|         let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default()); | ||||
|         conn.state.close(); | ||||
|  | ||||
|         match conn.start_send(Frame::Body { chunk: Some(b"foobar".to_vec().into()) }) { | ||||
|             Err(_e) => {}, | ||||
|             other => panic!("did not return Err: {:?}", other) | ||||
|         } | ||||
|  | ||||
|         assert!(conn.state.is_write_closed()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_conn_write_empty_chunk() { | ||||
|         let io = AsyncIo::new_buf(vec![], 0); | ||||
|         let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default()); | ||||
|         conn.state.writing = Writing::KeepAlive; | ||||
|  | ||||
|         assert!(conn.start_send(Frame::Body { chunk: None }).unwrap().is_ready()); | ||||
|         assert!(conn.start_send(Frame::Body { chunk: Some(Vec::new().into()) }).unwrap().is_ready()); | ||||
|         conn.start_send(Frame::Body { chunk: Some(vec![b'a'].into()) }).unwrap_err(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										59
									
								
								src/proto/h1/date.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/proto/h1/date.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| use std::cell::RefCell; | ||||
| use std::fmt::{self, Write}; | ||||
| use std::str; | ||||
|  | ||||
| use time::{self, Duration}; | ||||
|  | ||||
| // "Sun, 06 Nov 1994 08:49:37 GMT".len() | ||||
| pub const DATE_VALUE_LENGTH: usize = 29; | ||||
|  | ||||
| pub fn extend(dst: &mut Vec<u8>) { | ||||
|     CACHED.with(|cache| { | ||||
|         let mut cache = cache.borrow_mut(); | ||||
|         let now = time::get_time(); | ||||
|         if now > cache.next_update { | ||||
|             cache.update(now); | ||||
|         } | ||||
|         dst.extend_from_slice(cache.buffer()); | ||||
|     }) | ||||
| } | ||||
|  | ||||
| struct CachedDate { | ||||
|     bytes: [u8; DATE_VALUE_LENGTH], | ||||
|     pos: usize, | ||||
|     next_update: time::Timespec, | ||||
| } | ||||
|  | ||||
| thread_local!(static CACHED: RefCell<CachedDate> = RefCell::new(CachedDate { | ||||
|     bytes: [0; DATE_VALUE_LENGTH], | ||||
|     pos: 0, | ||||
|     next_update: time::Timespec::new(0, 0), | ||||
| })); | ||||
|  | ||||
| impl CachedDate { | ||||
|     fn buffer(&self) -> &[u8] { | ||||
|         &self.bytes[..] | ||||
|     } | ||||
|  | ||||
|     fn update(&mut self, now: time::Timespec) { | ||||
|         self.pos = 0; | ||||
|         write!(self, "{}", time::at_utc(now).rfc822()).unwrap(); | ||||
|         assert!(self.pos == DATE_VALUE_LENGTH); | ||||
|         self.next_update = now + Duration::seconds(1); | ||||
|         self.next_update.nsec = 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Write for CachedDate { | ||||
|     fn write_str(&mut self, s: &str) -> fmt::Result { | ||||
|         let len = s.len(); | ||||
|         self.bytes[self.pos..self.pos + len].copy_from_slice(s.as_bytes()); | ||||
|         self.pos += len; | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn test_date_len() { | ||||
|     assert_eq!(DATE_VALUE_LENGTH, "Sun, 06 Nov 1994 08:49:37 GMT".len()); | ||||
| } | ||||
							
								
								
									
										499
									
								
								src/proto/h1/decode.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										499
									
								
								src/proto/h1/decode.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,499 @@ | ||||
| use std::usize; | ||||
| use std::io; | ||||
|  | ||||
| use futures::{Async, Poll}; | ||||
| use bytes::Bytes; | ||||
| use proto::io::MemRead; | ||||
|  | ||||
| use self::Kind::{Length, Chunked, Eof}; | ||||
|  | ||||
| /// Decoders to handle different Transfer-Encodings. | ||||
| /// | ||||
| /// If a message body does not include a Transfer-Encoding, it *should* | ||||
| /// include a Content-Length header. | ||||
| #[derive(Debug, Clone, PartialEq)] | ||||
| pub struct Decoder { | ||||
|     kind: Kind, | ||||
| } | ||||
|  | ||||
| impl Decoder { | ||||
|     pub fn length(x: u64) -> Decoder { | ||||
|         Decoder { kind: Kind::Length(x) } | ||||
|     } | ||||
|  | ||||
|     pub fn chunked() -> Decoder { | ||||
|         Decoder { kind: Kind::Chunked(ChunkedState::Size, 0) } | ||||
|     } | ||||
|  | ||||
|     pub fn eof() -> Decoder { | ||||
|         Decoder { kind: Kind::Eof(false) } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, PartialEq)] | ||||
| enum Kind { | ||||
|     /// A Reader used when a Content-Length header is passed with a positive integer. | ||||
|     Length(u64), | ||||
|     /// A Reader used when Transfer-Encoding is `chunked`. | ||||
|     Chunked(ChunkedState, u64), | ||||
|     /// A Reader used for responses that don't indicate a length or chunked. | ||||
|     /// | ||||
|     /// Note: This should only used for `Response`s. It is illegal for a | ||||
|     /// `Request` to be made with both `Content-Length` and | ||||
|     /// `Transfer-Encoding: chunked` missing, as explained from the spec: | ||||
|     /// | ||||
|     /// > If a Transfer-Encoding header field is present in a response and | ||||
|     /// > the chunked transfer coding is not the final encoding, the | ||||
|     /// > message body length is determined by reading the connection until | ||||
|     /// > it is closed by the server.  If a Transfer-Encoding header field | ||||
|     /// > is present in a request and the chunked transfer coding is not | ||||
|     /// > the final encoding, the message body length cannot be determined | ||||
|     /// > reliably; the server MUST respond with the 400 (Bad Request) | ||||
|     /// > status code and then close the connection. | ||||
|     Eof(bool), | ||||
| } | ||||
|  | ||||
| #[derive(Debug, PartialEq, Clone)] | ||||
| enum ChunkedState { | ||||
|     Size, | ||||
|     SizeLws, | ||||
|     Extension, | ||||
|     SizeLf, | ||||
|     Body, | ||||
|     BodyCr, | ||||
|     BodyLf, | ||||
|     EndCr, | ||||
|     EndLf, | ||||
|     End, | ||||
| } | ||||
|  | ||||
| impl Decoder { | ||||
|     pub fn is_eof(&self) -> bool { | ||||
|         trace!("is_eof? {:?}", self); | ||||
|         match self.kind { | ||||
|             Length(0) | | ||||
|             Chunked(ChunkedState::End, _) | | ||||
|             Eof(true) => true, | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Decoder { | ||||
|     pub fn decode<R: MemRead>(&mut self, body: &mut R) -> Poll<Bytes, io::Error> { | ||||
|         match self.kind { | ||||
|             Length(ref mut remaining) => { | ||||
|                 trace!("Sized read, remaining={:?}", remaining); | ||||
|                 if *remaining == 0 { | ||||
|                     Ok(Async::Ready(Bytes::new())) | ||||
|                 } else { | ||||
|                     let to_read = *remaining as usize; | ||||
|                     let buf = try_ready!(body.read_mem(to_read)); | ||||
|                     let num = buf.as_ref().len() as u64; | ||||
|                     trace!("Length read: {}", num); | ||||
|                     if num > *remaining { | ||||
|                         *remaining = 0; | ||||
|                     } else if num == 0 { | ||||
|                         return Err(io::Error::new(io::ErrorKind::Other, "early eof")); | ||||
|                     } else { | ||||
|                         *remaining -= num; | ||||
|                     } | ||||
|                     Ok(Async::Ready(buf)) | ||||
|                 } | ||||
|             } | ||||
|             Chunked(ref mut state, ref mut size) => { | ||||
|                 loop { | ||||
|                     let mut buf = None; | ||||
|                     // advances the chunked state | ||||
|                     *state = try_ready!(state.step(body, size, &mut buf)); | ||||
|                     if *state == ChunkedState::End { | ||||
|                         trace!("end of chunked"); | ||||
|                         return Ok(Async::Ready(Bytes::new())); | ||||
|                     } | ||||
|                     if let Some(buf) = buf { | ||||
|                         return Ok(Async::Ready(buf)); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             Eof(ref mut is_eof) => { | ||||
|                 if *is_eof { | ||||
|                     Ok(Async::Ready(Bytes::new())) | ||||
|                 } else { | ||||
|                     // 8192 chosen because its about 2 packets, there probably | ||||
|                     // won't be that much available, so don't have MemReaders | ||||
|                     // allocate buffers to big | ||||
|                     let slice = try_ready!(body.read_mem(8192)); | ||||
|                     *is_eof = slice.is_empty(); | ||||
|                     Ok(Async::Ready(slice)) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| macro_rules! byte ( | ||||
|     ($rdr:ident) => ({ | ||||
|         let buf = try_ready!($rdr.read_mem(1)); | ||||
|         if !buf.is_empty() { | ||||
|             buf[0] | ||||
|         } else { | ||||
|             return Err(io::Error::new(io::ErrorKind::UnexpectedEof, | ||||
|                                       "Unexpected eof during chunk size line")); | ||||
|         } | ||||
|     }) | ||||
| ); | ||||
|  | ||||
| impl ChunkedState { | ||||
|     fn step<R: MemRead>(&self, | ||||
|                         body: &mut R, | ||||
|                         size: &mut u64, | ||||
|                         buf: &mut Option<Bytes>) | ||||
|                         -> Poll<ChunkedState, io::Error> { | ||||
|         use self::ChunkedState::*; | ||||
|         match *self { | ||||
|             Size => ChunkedState::read_size(body, size), | ||||
|             SizeLws => ChunkedState::read_size_lws(body), | ||||
|             Extension => ChunkedState::read_extension(body), | ||||
|             SizeLf => ChunkedState::read_size_lf(body, size), | ||||
|             Body => ChunkedState::read_body(body, size, buf), | ||||
|             BodyCr => ChunkedState::read_body_cr(body), | ||||
|             BodyLf => ChunkedState::read_body_lf(body), | ||||
|             EndCr => ChunkedState::read_end_cr(body), | ||||
|             EndLf => ChunkedState::read_end_lf(body), | ||||
|             End => Ok(Async::Ready(ChunkedState::End)), | ||||
|         } | ||||
|     } | ||||
|     fn read_size<R: MemRead>(rdr: &mut R, size: &mut u64) -> Poll<ChunkedState, io::Error> { | ||||
|         trace!("Read chunk hex size"); | ||||
|         let radix = 16; | ||||
|         match byte!(rdr) { | ||||
|             b @ b'0'...b'9' => { | ||||
|                 *size *= radix; | ||||
|                 *size += (b - b'0') as u64; | ||||
|             } | ||||
|             b @ b'a'...b'f' => { | ||||
|                 *size *= radix; | ||||
|                 *size += (b + 10 - b'a') as u64; | ||||
|             } | ||||
|             b @ b'A'...b'F' => { | ||||
|                 *size *= radix; | ||||
|                 *size += (b + 10 - b'A') as u64; | ||||
|             } | ||||
|             b'\t' | b' ' => return Ok(Async::Ready(ChunkedState::SizeLws)), | ||||
|             b';' => return Ok(Async::Ready(ChunkedState::Extension)), | ||||
|             b'\r' => return Ok(Async::Ready(ChunkedState::SizeLf)), | ||||
|             _ => { | ||||
|                 return Err(io::Error::new(io::ErrorKind::InvalidInput, | ||||
|                                           "Invalid chunk size line: Invalid Size")); | ||||
|             } | ||||
|         } | ||||
|         Ok(Async::Ready(ChunkedState::Size)) | ||||
|     } | ||||
|     fn read_size_lws<R: MemRead>(rdr: &mut R) -> Poll<ChunkedState, io::Error> { | ||||
|         trace!("read_size_lws"); | ||||
|         match byte!(rdr) { | ||||
|             // LWS can follow the chunk size, but no more digits can come | ||||
|             b'\t' | b' ' => Ok(Async::Ready(ChunkedState::SizeLws)), | ||||
|             b';' => Ok(Async::Ready(ChunkedState::Extension)), | ||||
|             b'\r' => Ok(Async::Ready(ChunkedState::SizeLf)), | ||||
|             _ => { | ||||
|                 Err(io::Error::new(io::ErrorKind::InvalidInput, | ||||
|                                    "Invalid chunk size linear white space")) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     fn read_extension<R: MemRead>(rdr: &mut R) -> Poll<ChunkedState, io::Error> { | ||||
|         trace!("read_extension"); | ||||
|         match byte!(rdr) { | ||||
|             b'\r' => Ok(Async::Ready(ChunkedState::SizeLf)), | ||||
|             _ => Ok(Async::Ready(ChunkedState::Extension)), // no supported extensions | ||||
|         } | ||||
|     } | ||||
|     fn read_size_lf<R: MemRead>(rdr: &mut R, size: &mut u64) -> Poll<ChunkedState, io::Error> { | ||||
|         trace!("Chunk size is {:?}", size); | ||||
|         match byte!(rdr) { | ||||
|             b'\n' if *size > 0 => Ok(Async::Ready(ChunkedState::Body)), | ||||
|             b'\n' if *size == 0 => Ok(Async::Ready(ChunkedState::EndCr)), | ||||
|             _ => Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk size LF")), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn read_body<R: MemRead>(rdr: &mut R, | ||||
|                           rem: &mut u64, | ||||
|                           buf: &mut Option<Bytes>) | ||||
|                           -> Poll<ChunkedState, io::Error> { | ||||
|         trace!("Chunked read, remaining={:?}", rem); | ||||
|  | ||||
|         // cap remaining bytes at the max capacity of usize | ||||
|         let rem_cap = match *rem { | ||||
|             r if r > usize::MAX as u64 => usize::MAX, | ||||
|             r => r as usize, | ||||
|         }; | ||||
|  | ||||
|         let to_read = rem_cap; | ||||
|         let slice = try_ready!(rdr.read_mem(to_read)); | ||||
|         let count = slice.len(); | ||||
|  | ||||
|         if count == 0 { | ||||
|             *rem = 0; | ||||
|             return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "early eof")); | ||||
|         } | ||||
|         *buf = Some(slice); | ||||
|         *rem -= count as u64; | ||||
|  | ||||
|         if *rem > 0 { | ||||
|             Ok(Async::Ready(ChunkedState::Body)) | ||||
|         } else { | ||||
|             Ok(Async::Ready(ChunkedState::BodyCr)) | ||||
|         } | ||||
|     } | ||||
|     fn read_body_cr<R: MemRead>(rdr: &mut R) -> Poll<ChunkedState, io::Error> { | ||||
|         match byte!(rdr) { | ||||
|             b'\r' => Ok(Async::Ready(ChunkedState::BodyLf)), | ||||
|             _ => Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk body CR")), | ||||
|         } | ||||
|     } | ||||
|     fn read_body_lf<R: MemRead>(rdr: &mut R) -> Poll<ChunkedState, io::Error> { | ||||
|         match byte!(rdr) { | ||||
|             b'\n' => Ok(Async::Ready(ChunkedState::Size)), | ||||
|             _ => Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk body LF")), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn read_end_cr<R: MemRead>(rdr: &mut R) -> Poll<ChunkedState, io::Error> { | ||||
|         match byte!(rdr) { | ||||
|             b'\r' => Ok(Async::Ready(ChunkedState::EndLf)), | ||||
|             _ => Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk end CR")), | ||||
|         } | ||||
|     } | ||||
|     fn read_end_lf<R: MemRead>(rdr: &mut R) -> Poll<ChunkedState, io::Error> { | ||||
|         match byte!(rdr) { | ||||
|             b'\n' => Ok(Async::Ready(ChunkedState::End)), | ||||
|             _ => Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk end LF")), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use std::error::Error; | ||||
|     use std::io; | ||||
|     use std::io::Write; | ||||
|     use super::Decoder; | ||||
|     use super::ChunkedState; | ||||
|     use proto::io::MemRead; | ||||
|     use futures::{Async, Poll}; | ||||
|     use bytes::{BytesMut, Bytes}; | ||||
|     use mock::AsyncIo; | ||||
|  | ||||
|     impl<'a> MemRead for &'a [u8] { | ||||
|         fn read_mem(&mut self, len: usize) -> Poll<Bytes, io::Error> { | ||||
|             let n = ::std::cmp::min(len, self.len()); | ||||
|             if n > 0 { | ||||
|                 let (a, b) = self.split_at(n); | ||||
|                 let mut buf = BytesMut::from(a); | ||||
|                 *self = b; | ||||
|                 Ok(Async::Ready(buf.split_to(n).freeze())) | ||||
|             } else { | ||||
|                 Ok(Async::Ready(Bytes::new())) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     trait HelpUnwrap<T> { | ||||
|         fn unwrap(self) -> T; | ||||
|     } | ||||
|     impl HelpUnwrap<Bytes> for Async<Bytes> { | ||||
|         fn unwrap(self) -> Bytes { | ||||
|             match self { | ||||
|                 Async::Ready(bytes) => bytes, | ||||
|                 Async::NotReady => panic!(), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     impl HelpUnwrap<ChunkedState> for Async<ChunkedState> { | ||||
|         fn unwrap(self) -> ChunkedState { | ||||
|             match self { | ||||
|                 Async::Ready(state) => state, | ||||
|                 Async::NotReady => panic!(), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_read_chunk_size() { | ||||
|         use std::io::ErrorKind::{UnexpectedEof, InvalidInput}; | ||||
|  | ||||
|         fn read(s: &str) -> u64 { | ||||
|             let mut state = ChunkedState::Size; | ||||
|             let rdr = &mut s.as_bytes(); | ||||
|             let mut size = 0; | ||||
|             loop { | ||||
|                 let result = state.step(rdr, &mut size, &mut None); | ||||
|                 let desc = format!("read_size failed for {:?}", s); | ||||
|                 state = result.expect(desc.as_str()).unwrap(); | ||||
|                 if state == ChunkedState::Body || state == ChunkedState::EndCr { | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|             size | ||||
|         } | ||||
|  | ||||
|         fn read_err(s: &str, expected_err: io::ErrorKind) { | ||||
|             let mut state = ChunkedState::Size; | ||||
|             let rdr = &mut s.as_bytes(); | ||||
|             let mut size = 0; | ||||
|             loop { | ||||
|                 let result = state.step(rdr, &mut size, &mut None); | ||||
|                 state = match result { | ||||
|                     Ok(s) => s.unwrap(), | ||||
|                     Err(e) => { | ||||
|                         assert!(expected_err == e.kind(), "Reading {:?}, expected {:?}, but got {:?}", | ||||
|                                                           s, expected_err, e.kind()); | ||||
|                         return; | ||||
|                     } | ||||
|                 }; | ||||
|                 if state == ChunkedState::Body || state == ChunkedState::End { | ||||
|                     panic!(format!("Was Ok. Expected Err for {:?}", s)); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         assert_eq!(1, read("1\r\n")); | ||||
|         assert_eq!(1, read("01\r\n")); | ||||
|         assert_eq!(0, read("0\r\n")); | ||||
|         assert_eq!(0, read("00\r\n")); | ||||
|         assert_eq!(10, read("A\r\n")); | ||||
|         assert_eq!(10, read("a\r\n")); | ||||
|         assert_eq!(255, read("Ff\r\n")); | ||||
|         assert_eq!(255, read("Ff   \r\n")); | ||||
|         // Missing LF or CRLF | ||||
|         read_err("F\rF", InvalidInput); | ||||
|         read_err("F", UnexpectedEof); | ||||
|         // Invalid hex digit | ||||
|         read_err("X\r\n", InvalidInput); | ||||
|         read_err("1X\r\n", InvalidInput); | ||||
|         read_err("-\r\n", InvalidInput); | ||||
|         read_err("-1\r\n", InvalidInput); | ||||
|         // Acceptable (if not fully valid) extensions do not influence the size | ||||
|         assert_eq!(1, read("1;extension\r\n")); | ||||
|         assert_eq!(10, read("a;ext name=value\r\n")); | ||||
|         assert_eq!(1, read("1;extension;extension2\r\n")); | ||||
|         assert_eq!(1, read("1;;;  ;\r\n")); | ||||
|         assert_eq!(2, read("2; extension...\r\n")); | ||||
|         assert_eq!(3, read("3   ; extension=123\r\n")); | ||||
|         assert_eq!(3, read("3   ;\r\n")); | ||||
|         assert_eq!(3, read("3   ;   \r\n")); | ||||
|         // Invalid extensions cause an error | ||||
|         read_err("1 invalid extension\r\n", InvalidInput); | ||||
|         read_err("1 A\r\n", InvalidInput); | ||||
|         read_err("1;no CRLF", UnexpectedEof); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_read_sized_early_eof() { | ||||
|         let mut bytes = &b"foo bar"[..]; | ||||
|         let mut decoder = Decoder::length(10); | ||||
|         assert_eq!(decoder.decode(&mut bytes).unwrap().unwrap().len(), 7); | ||||
|         let e = decoder.decode(&mut bytes).unwrap_err(); | ||||
|         assert_eq!(e.kind(), io::ErrorKind::Other); | ||||
|         assert_eq!(e.description(), "early eof"); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_read_chunked_early_eof() { | ||||
|         let mut bytes = &b"\ | ||||
|             9\r\n\ | ||||
|             foo bar\ | ||||
|         "[..]; | ||||
|         let mut decoder = Decoder::chunked(); | ||||
|         assert_eq!(decoder.decode(&mut bytes).unwrap().unwrap().len(), 7); | ||||
|         let e = decoder.decode(&mut bytes).unwrap_err(); | ||||
|         assert_eq!(e.kind(), io::ErrorKind::UnexpectedEof); | ||||
|         assert_eq!(e.description(), "early eof"); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_read_chunked_single_read() { | ||||
|         let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\n"[..]; | ||||
|         let buf = Decoder::chunked().decode(&mut mock_buf).expect("decode").unwrap(); | ||||
|         assert_eq!(16, buf.len()); | ||||
|         let result = String::from_utf8(buf.as_ref().to_vec()).expect("decode String"); | ||||
|         assert_eq!("1234567890abcdef", &result); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_read_chunked_after_eof() { | ||||
|         let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\n\r\n"[..]; | ||||
|         let mut decoder = Decoder::chunked(); | ||||
|  | ||||
|         // normal read | ||||
|         let buf = decoder.decode(&mut mock_buf).expect("decode").unwrap(); | ||||
|         assert_eq!(16, buf.len()); | ||||
|         let result = String::from_utf8(buf.as_ref().to_vec()).expect("decode String"); | ||||
|         assert_eq!("1234567890abcdef", &result); | ||||
|  | ||||
|         // eof read | ||||
|         let buf = decoder.decode(&mut mock_buf).expect("decode").unwrap(); | ||||
|         assert_eq!(0, buf.len()); | ||||
|  | ||||
|         // ensure read after eof also returns eof | ||||
|         let buf = decoder.decode(&mut mock_buf).expect("decode").unwrap(); | ||||
|         assert_eq!(0, buf.len()); | ||||
|     } | ||||
|  | ||||
|     // perform an async read using a custom buffer size and causing a blocking | ||||
|     // read at the specified byte | ||||
|     fn read_async(mut decoder: Decoder, | ||||
|                   content: &[u8], | ||||
|                   block_at: usize) | ||||
|                   -> String { | ||||
|         let content_len = content.len(); | ||||
|         let mut ins = AsyncIo::new(content, block_at); | ||||
|         let mut outs = Vec::new(); | ||||
|         loop { | ||||
|             match decoder.decode(&mut ins).expect("unexpected decode error: {}") { | ||||
|                 Async::Ready(buf) => { | ||||
|                     if buf.is_empty() { | ||||
|                         break; // eof | ||||
|                     } | ||||
|                     outs.write(buf.as_ref()).expect("write buffer"); | ||||
|                 }, | ||||
|                 Async::NotReady => { | ||||
|                     ins.block_in(content_len); // we only block once | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|         String::from_utf8(outs).expect("decode String") | ||||
|     } | ||||
|  | ||||
|     // iterate over the different ways that this async read could go. | ||||
|     // tests blocking a read at each byte along the content - The shotgun approach | ||||
|     fn all_async_cases(content: &str, expected: &str, decoder: Decoder) { | ||||
|         let content_len = content.len(); | ||||
|         for block_at in 0..content_len { | ||||
|             let actual = read_async(decoder.clone(), content.as_bytes(), block_at); | ||||
|             assert_eq!(expected, &actual) //, "Failed async. Blocking at {}", block_at); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_read_length_async() { | ||||
|         let content = "foobar"; | ||||
|         all_async_cases(content, content, Decoder::length(content.len() as u64)); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_read_chunked_async() { | ||||
|         let content = "3\r\nfoo\r\n3\r\nbar\r\n0\r\n\r\n"; | ||||
|         let expected = "foobar"; | ||||
|         all_async_cases(content, expected, Decoder::chunked()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_read_eof_async() { | ||||
|         let content = "foobar"; | ||||
|         all_async_cases(content, content, Decoder::eof()); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										309
									
								
								src/proto/h1/encode.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										309
									
								
								src/proto/h1/encode.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,309 @@ | ||||
| use std::cmp; | ||||
| use std::io::{self, Write}; | ||||
|  | ||||
| use proto::io::AtomicWrite; | ||||
|  | ||||
| /// Encoders to handle different Transfer-Encodings. | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct Encoder { | ||||
|     kind: Kind, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, PartialEq, Clone)] | ||||
| enum Kind { | ||||
|     /// An Encoder for when Transfer-Encoding includes `chunked`. | ||||
|     Chunked(Chunked), | ||||
|     /// An Encoder for when Content-Length is set. | ||||
|     /// | ||||
|     /// Enforces that the body is not longer than the Content-Length header. | ||||
|     Length(u64), | ||||
| } | ||||
|  | ||||
| impl Encoder { | ||||
|     pub fn chunked() -> Encoder { | ||||
|         Encoder { | ||||
|             kind: Kind::Chunked(Chunked::Init), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn length(len: u64) -> Encoder { | ||||
|         Encoder { | ||||
|             kind: Kind::Length(len), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn is_eof(&self) -> bool { | ||||
|         match self.kind { | ||||
|             Kind::Length(0) | | ||||
|             Kind::Chunked(Chunked::End) => true, | ||||
|             _ => false | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn eof(&self) -> Result<Option<&'static [u8]>, NotEof> { | ||||
|         match self.kind { | ||||
|             Kind::Length(0) => Ok(None), | ||||
|             Kind::Chunked(Chunked::Init) => Ok(Some(b"0\r\n\r\n")), | ||||
|             _ => Err(NotEof), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn encode<W: AtomicWrite>(&mut self, w: &mut W, msg: &[u8]) -> io::Result<usize> { | ||||
|         match self.kind { | ||||
|             Kind::Chunked(ref mut chunked) => { | ||||
|                 chunked.encode(w, msg) | ||||
|             }, | ||||
|             Kind::Length(ref mut remaining) => { | ||||
|                 if msg.is_empty() { | ||||
|                     return Ok(0); | ||||
|                 } | ||||
|                 let n = { | ||||
|                     let max = cmp::min(*remaining as usize, msg.len()); | ||||
|                     trace!("sized write = {}", max); | ||||
|                     let slice = &msg[..max]; | ||||
|  | ||||
|                     try!(w.write_atomic(&[slice])) | ||||
|                 }; | ||||
|  | ||||
|                 if n == 0 { | ||||
|                     return Err(io::Error::new(io::ErrorKind::WriteZero, "write zero")); | ||||
|                 } | ||||
|  | ||||
|                 *remaining -= n as u64; | ||||
|                 trace!("encoded {} bytes, remaining = {}", n, remaining); | ||||
|                 Ok(n) | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub struct NotEof; | ||||
|  | ||||
| #[derive(Debug, PartialEq, Clone)] | ||||
| enum Chunked { | ||||
|     Init, | ||||
|     Size(ChunkSize), | ||||
|     SizeCr, | ||||
|     SizeLf, | ||||
|     Body(usize), | ||||
|     BodyCr, | ||||
|     BodyLf, | ||||
|     End, | ||||
| } | ||||
|  | ||||
| impl Chunked { | ||||
|     fn encode<W: AtomicWrite>(&mut self, w: &mut W, msg: &[u8]) -> io::Result<usize> { | ||||
|         match *self { | ||||
|             Chunked::Init => { | ||||
|                 let mut size = ChunkSize { | ||||
|                     bytes: [0; CHUNK_SIZE_MAX_BYTES], | ||||
|                     pos: 0, | ||||
|                     len: 0, | ||||
|                 }; | ||||
|                 trace!("chunked write, size = {:?}", msg.len()); | ||||
|                 write!(&mut size, "{:X}", msg.len()) | ||||
|                     .expect("CHUNK_SIZE_MAX_BYTES should fit any usize"); | ||||
|                 *self = Chunked::Size(size); | ||||
|             } | ||||
|             Chunked::End => return Ok(0), | ||||
|             _ => {} | ||||
|         } | ||||
|         let mut n = { | ||||
|             let pieces = match *self { | ||||
|                 Chunked::Init => unreachable!("Chunked::Init should have become Chunked::Size"), | ||||
|                 Chunked::Size(ref size) => [ | ||||
|                     &size.bytes[size.pos.into() .. size.len.into()], | ||||
|                     &b"\r\n"[..], | ||||
|                     msg, | ||||
|                     &b"\r\n"[..], | ||||
|                 ], | ||||
|                 Chunked::SizeCr => [ | ||||
|                     &b""[..], | ||||
|                     &b"\r\n"[..], | ||||
|                     msg, | ||||
|                     &b"\r\n"[..], | ||||
|                 ], | ||||
|                 Chunked::SizeLf => [ | ||||
|                     &b""[..], | ||||
|                     &b"\n"[..], | ||||
|                     msg, | ||||
|                     &b"\r\n"[..], | ||||
|                 ], | ||||
|                 Chunked::Body(pos) => [ | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &msg[pos..], | ||||
|                     &b"\r\n"[..], | ||||
|                 ], | ||||
|                 Chunked::BodyCr => [ | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b"\r\n"[..], | ||||
|                 ], | ||||
|                 Chunked::BodyLf => [ | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b"\n"[..], | ||||
|                 ], | ||||
|                 Chunked::End => unreachable!("Chunked::End shouldn't write more") | ||||
|             }; | ||||
|             try!(w.write_atomic(&pieces)) | ||||
|         }; | ||||
|  | ||||
|         while n > 0 { | ||||
|             match *self { | ||||
|                 Chunked::Init => unreachable!("Chunked::Init should have become Chunked::Size"), | ||||
|                 Chunked::Size(mut size) => { | ||||
|                     n = size.update(n); | ||||
|                     if size.len == 0 { | ||||
|                         *self = Chunked::SizeCr; | ||||
|                     } else { | ||||
|                         *self = Chunked::Size(size); | ||||
|                     } | ||||
|                 }, | ||||
|                 Chunked::SizeCr => { | ||||
|                     *self = Chunked::SizeLf; | ||||
|                     n -= 1; | ||||
|                 } | ||||
|                 Chunked::SizeLf => { | ||||
|                     *self = Chunked::Body(0); | ||||
|                     n -= 1; | ||||
|                 } | ||||
|                 Chunked::Body(pos) => { | ||||
|                     let left = msg.len() - pos; | ||||
|                     if n >= left { | ||||
|                         *self = Chunked::BodyCr; | ||||
|                         n -= left; | ||||
|                     } else { | ||||
|                         *self = Chunked::Body(pos + n); | ||||
|                         n = 0; | ||||
|                     } | ||||
|                 } | ||||
|                 Chunked::BodyCr => { | ||||
|                     *self = Chunked::BodyLf; | ||||
|                     n -= 1; | ||||
|                 } | ||||
|                 Chunked::BodyLf => { | ||||
|                     assert!(n == 1); | ||||
|                     *self = if msg.len() == 0 { | ||||
|                         Chunked::End | ||||
|                     } else { | ||||
|                         Chunked::Init | ||||
|                     }; | ||||
|                     n = 0; | ||||
|                 }, | ||||
|                 Chunked::End => unreachable!("Chunked::End shouldn't have any to write") | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         match *self { | ||||
|             Chunked::Init | | ||||
|             Chunked::End => Ok(msg.len()), | ||||
|             _ => Err(io::ErrorKind::WouldBlock.into()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(target_pointer_width = "32")] | ||||
| const USIZE_BYTES: usize = 4; | ||||
|  | ||||
| #[cfg(target_pointer_width = "64")] | ||||
| const USIZE_BYTES: usize = 8; | ||||
|  | ||||
| // each byte will become 2 hex | ||||
| const CHUNK_SIZE_MAX_BYTES: usize = USIZE_BYTES * 2; | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| struct ChunkSize { | ||||
|     bytes: [u8; CHUNK_SIZE_MAX_BYTES], | ||||
|     pos: u8, | ||||
|     len: u8, | ||||
| } | ||||
|  | ||||
| impl ChunkSize { | ||||
|     fn update(&mut self, n: usize) -> usize { | ||||
|         let diff = (self.len - self.pos).into(); | ||||
|         if n >= diff { | ||||
|             self.pos = 0; | ||||
|             self.len = 0; | ||||
|             n - diff | ||||
|         } else { | ||||
|             self.pos += n as u8; // just verified it was a small usize | ||||
|             0 | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ::std::fmt::Debug for ChunkSize { | ||||
|     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { | ||||
|         f.debug_struct("ChunkSize") | ||||
|             .field("bytes", &&self.bytes[..self.len.into()]) | ||||
|             .field("pos", &self.pos) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ::std::cmp::PartialEq for ChunkSize { | ||||
|     fn eq(&self, other: &ChunkSize) -> bool { | ||||
|         self.len == other.len && | ||||
|             self.pos == other.pos && | ||||
|             (&self.bytes[..]) == (&other.bytes[..]) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl io::Write for ChunkSize { | ||||
|     fn write(&mut self, msg: &[u8]) -> io::Result<usize> { | ||||
|         let n = (&mut self.bytes[self.len.into() ..]).write(msg) | ||||
|             .expect("&mut [u8].write() cannot error"); | ||||
|         self.len += n as u8; // safe because bytes is never bigger than 256 | ||||
|         Ok(n) | ||||
|     } | ||||
|  | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::Encoder; | ||||
|     use mock::{AsyncIo, Buf}; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_chunked_encode_sync() { | ||||
|         let mut dst = Buf::new(); | ||||
|         let mut encoder = Encoder::chunked(); | ||||
|  | ||||
|         encoder.encode(&mut dst, b"foo bar").unwrap(); | ||||
|         encoder.encode(&mut dst, b"baz quux herp").unwrap(); | ||||
|         encoder.encode(&mut dst, b"").unwrap(); | ||||
|         assert_eq!(&dst[..], &b"7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n0\r\n\r\n"[..]); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_chunked_encode_async() { | ||||
|         let mut dst = AsyncIo::new(Buf::new(), 7); | ||||
|         let mut encoder = Encoder::chunked(); | ||||
|  | ||||
|         assert!(encoder.encode(&mut dst, b"foo bar").is_err()); | ||||
|         dst.block_in(6); | ||||
|         assert_eq!(7, encoder.encode(&mut dst, b"foo bar").unwrap()); | ||||
|         dst.block_in(30); | ||||
|         assert_eq!(13, encoder.encode(&mut dst, b"baz quux herp").unwrap()); | ||||
|         encoder.encode(&mut dst, b"").unwrap(); | ||||
|         assert_eq!(&dst[..], &b"7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n0\r\n\r\n"[..]); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_sized_encode() { | ||||
|         let mut dst = Buf::new(); | ||||
|         let mut encoder = Encoder::length(8); | ||||
|         encoder.encode(&mut dst, b"foo bar").unwrap(); | ||||
|         assert_eq!(encoder.encode(&mut dst, b"baz").unwrap(), 1); | ||||
|  | ||||
|         assert_eq!(dst, b"foo barb"); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										8
									
								
								src/proto/h1/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/proto/h1/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| pub use self::decode::Decoder; | ||||
| pub use self::encode::Encoder; | ||||
|  | ||||
| mod date; | ||||
| mod decode; | ||||
| mod encode; | ||||
| pub mod parse; | ||||
|  | ||||
							
								
								
									
										585
									
								
								src/proto/h1/parse.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										585
									
								
								src/proto/h1/parse.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,585 @@ | ||||
| use std::borrow::Cow; | ||||
| use std::fmt::{self, Write}; | ||||
|  | ||||
| use httparse; | ||||
| use bytes::{BytesMut, Bytes}; | ||||
|  | ||||
| use header::{self, Headers, ContentLength, TransferEncoding}; | ||||
| use proto::{MessageHead, RawStatus, Http1Transaction, ParseResult, | ||||
|            ServerTransaction, ClientTransaction, RequestLine, RequestHead}; | ||||
| use proto::h1::{Encoder, Decoder, date}; | ||||
| use method::Method; | ||||
| use status::StatusCode; | ||||
| use version::HttpVersion::{Http10, Http11}; | ||||
|  | ||||
| const MAX_HEADERS: usize = 100; | ||||
| const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific | ||||
|  | ||||
| impl Http1Transaction for ServerTransaction { | ||||
|     type Incoming = RequestLine; | ||||
|     type Outgoing = StatusCode; | ||||
|  | ||||
|     fn parse(buf: &mut BytesMut) -> ParseResult<RequestLine> { | ||||
|         if buf.len() == 0 { | ||||
|             return Ok(None); | ||||
|         } | ||||
|         let mut headers_indices = [HeaderIndices { | ||||
|             name: (0, 0), | ||||
|             value: (0, 0) | ||||
|         }; MAX_HEADERS]; | ||||
|         let (len, method, path, version, headers_len) = { | ||||
|             let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS]; | ||||
|             trace!("Request.parse([Header; {}], [u8; {}])", headers.len(), buf.len()); | ||||
|             let mut req = httparse::Request::new(&mut headers); | ||||
|             match try!(req.parse(&buf)) { | ||||
|                 httparse::Status::Complete(len) => { | ||||
|                     trace!("Request.parse Complete({})", len); | ||||
|                     let method = try!(req.method.unwrap().parse()); | ||||
|                     let path = req.path.unwrap(); | ||||
|                     let bytes_ptr = buf.as_ref().as_ptr() as usize; | ||||
|                     let path_start = path.as_ptr() as usize - bytes_ptr; | ||||
|                     let path_end = path_start + path.len(); | ||||
|                     let path = (path_start, path_end); | ||||
|                     let version = if req.version.unwrap() == 1 { Http11 } else { Http10 }; | ||||
|  | ||||
|                     record_header_indices(buf.as_ref(), &req.headers, &mut headers_indices); | ||||
|                     let headers_len = req.headers.len(); | ||||
|                     (len, method, path, version, headers_len) | ||||
|                 } | ||||
|                 httparse::Status::Partial => return Ok(None), | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         let mut headers = Headers::with_capacity(headers_len); | ||||
|         let slice = buf.split_to(len).freeze(); | ||||
|         let path = slice.slice(path.0, path.1); | ||||
|         // path was found to be utf8 by httparse | ||||
|         let path = try!(unsafe { ::uri::from_utf8_unchecked(path) }); | ||||
|         let subject = RequestLine( | ||||
|             method, | ||||
|             path, | ||||
|         ); | ||||
|  | ||||
|         headers.extend(HeadersAsBytesIter { | ||||
|             headers: headers_indices[..headers_len].iter(), | ||||
|             slice: slice, | ||||
|         }); | ||||
|  | ||||
|         Ok(Some((MessageHead { | ||||
|             version: version, | ||||
|             subject: subject, | ||||
|             headers: headers, | ||||
|         }, len))) | ||||
|     } | ||||
|  | ||||
|     fn decoder(head: &MessageHead<Self::Incoming>, method: &mut Option<Method>) -> ::Result<Decoder> { | ||||
|         use ::header; | ||||
|  | ||||
|         *method = Some(head.subject.0.clone()); | ||||
|  | ||||
|         // According to https://tools.ietf.org/html/rfc7230#section-3.3.3 | ||||
|         // 1. (irrelevant to Request) | ||||
|         // 2. (irrelevant to Request) | ||||
|         // 3. Transfer-Encoding: chunked has a chunked body. | ||||
|         // 4. If multiple differing Content-Length headers or invalid, close connection. | ||||
|         // 5. Content-Length header has a sized body. | ||||
|         // 6. Length 0. | ||||
|         // 7. (irrelevant to Request) | ||||
|  | ||||
|         if let Some(&header::TransferEncoding(ref encodings)) = head.headers.get() { | ||||
|             // https://tools.ietf.org/html/rfc7230#section-3.3.3 | ||||
|             // If Transfer-Encoding header is present, and 'chunked' is | ||||
|             // not the final encoding, and this is a Request, then it is | ||||
|             // mal-formed. A server should responsed with 400 Bad Request. | ||||
|             if encodings.last() == Some(&header::Encoding::Chunked) { | ||||
|                 Ok(Decoder::chunked()) | ||||
|             } else { | ||||
|                 debug!("request with transfer-encoding header, but not chunked, bad request"); | ||||
|                 Err(::Error::Header) | ||||
|             } | ||||
|         } else if let Some(&header::ContentLength(len)) = head.headers.get() { | ||||
|             Ok(Decoder::length(len)) | ||||
|         } else if head.headers.has::<header::ContentLength>() { | ||||
|             debug!("illegal Content-Length: {:?}", head.headers.get_raw("Content-Length")); | ||||
|             Err(::Error::Header) | ||||
|         } else { | ||||
|             Ok(Decoder::length(0)) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     fn encode(mut head: MessageHead<Self::Outgoing>, has_body: bool, method: &mut Option<Method>, dst: &mut Vec<u8>) -> Encoder { | ||||
|         trace!("ServerTransaction::encode has_body={}, method={:?}", has_body, method); | ||||
|  | ||||
|         let body = ServerTransaction::set_length(&mut head, has_body, method.as_ref()); | ||||
|  | ||||
|         let init_cap = 30 + head.headers.len() * AVERAGE_HEADER_SIZE; | ||||
|         dst.reserve(init_cap); | ||||
|         if head.version == ::HttpVersion::Http11 && head.subject == ::StatusCode::Ok { | ||||
|             extend(dst, b"HTTP/1.1 200 OK\r\n"); | ||||
|             let _ = write!(FastWrite(dst), "{}", head.headers); | ||||
|         } else { | ||||
|             let _ = write!(FastWrite(dst), "{} {}\r\n{}", head.version, head.subject, head.headers); | ||||
|         } | ||||
|         // using http::h1::date is quite a lot faster than generating a unique Date header each time | ||||
|         // like req/s goes up about 10% | ||||
|         if !head.headers.has::<header::Date>() { | ||||
|             dst.reserve(date::DATE_VALUE_LENGTH + 8); | ||||
|             extend(dst, b"Date: "); | ||||
|             date::extend(dst); | ||||
|             extend(dst, b"\r\n"); | ||||
|         } | ||||
|         extend(dst, b"\r\n"); | ||||
|         body | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ServerTransaction { | ||||
|     fn set_length(head: &mut MessageHead<StatusCode>, has_body: bool, method: Option<&Method>) -> Encoder { | ||||
|         // these are here thanks to borrowck | ||||
|         // `if method == Some(&Method::Get)` says the RHS doesnt live long enough | ||||
|         const HEAD: Option<&'static Method> = Some(&Method::Head); | ||||
|         const CONNECT: Option<&'static Method> = Some(&Method::Connect); | ||||
|  | ||||
|         let can_have_body = { | ||||
|             if method == HEAD { | ||||
|                 false | ||||
|             } else if method == CONNECT && head.subject.is_success() { | ||||
|                 false | ||||
|             } else { | ||||
|                 match head.subject { | ||||
|                     // TODO: support for 1xx codes needs improvement everywhere | ||||
|                     // would be 100...199 => false | ||||
|                     StatusCode::NoContent | | ||||
|                     StatusCode::NotModified => false, | ||||
|                     _ => true, | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         if has_body && can_have_body { | ||||
|             set_length(&mut head.headers) | ||||
|         } else { | ||||
|             head.headers.remove::<TransferEncoding>(); | ||||
|             if can_have_body { | ||||
|                 head.headers.set(ContentLength(0)); | ||||
|             } | ||||
|             Encoder::length(0) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Http1Transaction for ClientTransaction { | ||||
|     type Incoming = RawStatus; | ||||
|     type Outgoing = RequestLine; | ||||
|  | ||||
|     fn parse(buf: &mut BytesMut) -> ParseResult<RawStatus> { | ||||
|         if buf.len() == 0 { | ||||
|             return Ok(None); | ||||
|         } | ||||
|         let mut headers_indices = [HeaderIndices { | ||||
|             name: (0, 0), | ||||
|             value: (0, 0) | ||||
|         }; MAX_HEADERS]; | ||||
|         let (len, code, reason, version, headers_len) = { | ||||
|             let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS]; | ||||
|             trace!("Response.parse([Header; {}], [u8; {}])", headers.len(), buf.len()); | ||||
|             let mut res = httparse::Response::new(&mut headers); | ||||
|             let bytes = buf.as_ref(); | ||||
|             match try!(res.parse(bytes)) { | ||||
|                 httparse::Status::Complete(len) => { | ||||
|                     trace!("Response.parse Complete({})", len); | ||||
|                     let code = res.code.unwrap(); | ||||
|                     let status = try!(StatusCode::try_from(code).map_err(|_| ::Error::Status)); | ||||
|                     let reason = match status.canonical_reason() { | ||||
|                         Some(reason) if reason == res.reason.unwrap() => Cow::Borrowed(reason), | ||||
|                         _ => Cow::Owned(res.reason.unwrap().to_owned()) | ||||
|                     }; | ||||
|                     let version = if res.version.unwrap() == 1 { Http11 } else { Http10 }; | ||||
|                     record_header_indices(bytes, &res.headers, &mut headers_indices); | ||||
|                     let headers_len = res.headers.len(); | ||||
|                     (len, code, reason, version, headers_len) | ||||
|                 }, | ||||
|                 httparse::Status::Partial => return Ok(None), | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         let mut headers = Headers::with_capacity(headers_len); | ||||
|         let slice = buf.split_to(len).freeze(); | ||||
|         headers.extend(HeadersAsBytesIter { | ||||
|             headers: headers_indices[..headers_len].iter(), | ||||
|             slice: slice, | ||||
|         }); | ||||
|         Ok(Some((MessageHead { | ||||
|             version: version, | ||||
|             subject: RawStatus(code, reason), | ||||
|             headers: headers, | ||||
|         }, len))) | ||||
|     } | ||||
|  | ||||
|     fn decoder(inc: &MessageHead<Self::Incoming>, method: &mut Option<Method>) -> ::Result<Decoder> { | ||||
|         // According to https://tools.ietf.org/html/rfc7230#section-3.3.3 | ||||
|         // 1. HEAD responses, and Status 1xx, 204, and 304 cannot have a body. | ||||
|         // 2. Status 2xx to a CONNECT cannot have a body. | ||||
|         // 3. Transfer-Encoding: chunked has a chunked body. | ||||
|         // 4. If multiple differing Content-Length headers or invalid, close connection. | ||||
|         // 5. Content-Length header has a sized body. | ||||
|         // 6. (irrelevant to Response) | ||||
|         // 7. Read till EOF. | ||||
|  | ||||
|         match *method { | ||||
|             Some(Method::Head) => { | ||||
|                 return Ok(Decoder::length(0)); | ||||
|             } | ||||
|             Some(Method::Connect) => match inc.subject.0 { | ||||
|                 200...299 => { | ||||
|                     return Ok(Decoder::length(0)); | ||||
|                 }, | ||||
|                 _ => {}, | ||||
|             }, | ||||
|             Some(_) => {}, | ||||
|             None => { | ||||
|                 trace!("ClientTransaction::decoder is missing the Method"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         match inc.subject.0 { | ||||
|             100...199 | | ||||
|             204 | | ||||
|             304 => return Ok(Decoder::length(0)), | ||||
|             _ => (), | ||||
|         } | ||||
|  | ||||
|         if let Some(&header::TransferEncoding(ref codings)) = inc.headers.get() { | ||||
|             if codings.last() == Some(&header::Encoding::Chunked) { | ||||
|                 Ok(Decoder::chunked()) | ||||
|             } else { | ||||
|                 trace!("not chunked. read till eof"); | ||||
|                 Ok(Decoder::eof()) | ||||
|             } | ||||
|         } else if let Some(&header::ContentLength(len)) = inc.headers.get() { | ||||
|             Ok(Decoder::length(len)) | ||||
|         } else if inc.headers.has::<header::ContentLength>() { | ||||
|             debug!("illegal Content-Length: {:?}", inc.headers.get_raw("Content-Length")); | ||||
|             Err(::Error::Header) | ||||
|         } else { | ||||
|             trace!("neither Transfer-Encoding nor Content-Length"); | ||||
|             Ok(Decoder::eof()) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn encode(mut head: MessageHead<Self::Outgoing>, has_body: bool, method: &mut Option<Method>, dst: &mut Vec<u8>) -> Encoder { | ||||
|         trace!("ClientTransaction::encode has_body={}, method={:?}", has_body, method); | ||||
|  | ||||
|         *method = Some(head.subject.0.clone()); | ||||
|  | ||||
|         let body = ClientTransaction::set_length(&mut head, has_body); | ||||
|  | ||||
|         let init_cap = 30 + head.headers.len() * AVERAGE_HEADER_SIZE; | ||||
|         dst.reserve(init_cap); | ||||
|         let _ = write!(FastWrite(dst), "{} {}\r\n{}\r\n", head.subject, head.version, head.headers); | ||||
|  | ||||
|         body | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ClientTransaction { | ||||
|     fn set_length(head: &mut RequestHead, has_body: bool) -> Encoder { | ||||
|         if has_body { | ||||
|             set_length(&mut head.headers) | ||||
|         } else { | ||||
|             head.headers.remove::<ContentLength>(); | ||||
|             head.headers.remove::<TransferEncoding>(); | ||||
|             Encoder::length(0) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn set_length(headers: &mut Headers) -> Encoder { | ||||
|     let len = headers.get::<header::ContentLength>().map(|n| **n); | ||||
|  | ||||
|     if let Some(len) = len { | ||||
|         Encoder::length(len) | ||||
|     } else { | ||||
|         let encodings = match headers.get_mut::<header::TransferEncoding>() { | ||||
|             Some(&mut header::TransferEncoding(ref mut encodings)) => { | ||||
|                 if encodings.last() != Some(&header::Encoding::Chunked) { | ||||
|                     encodings.push(header::Encoding::Chunked); | ||||
|                 } | ||||
|                 false | ||||
|             }, | ||||
|             None => true | ||||
|         }; | ||||
|  | ||||
|         if encodings { | ||||
|             headers.set(header::TransferEncoding(vec![header::Encoding::Chunked])); | ||||
|         } | ||||
|         Encoder::chunked() | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| struct HeaderIndices { | ||||
|     name: (usize, usize), | ||||
|     value: (usize, usize), | ||||
| } | ||||
|  | ||||
| fn record_header_indices(bytes: &[u8], headers: &[httparse::Header], indices: &mut [HeaderIndices]) { | ||||
|     let bytes_ptr = bytes.as_ptr() as usize; | ||||
|     for (header, indices) in headers.iter().zip(indices.iter_mut()) { | ||||
|         let name_start = header.name.as_ptr() as usize - bytes_ptr; | ||||
|         let name_end = name_start + header.name.len(); | ||||
|         indices.name = (name_start, name_end); | ||||
|         let value_start = header.value.as_ptr() as usize - bytes_ptr; | ||||
|         let value_end = value_start + header.value.len(); | ||||
|         indices.value = (value_start, value_end); | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct HeadersAsBytesIter<'a> { | ||||
|     headers: ::std::slice::Iter<'a, HeaderIndices>, | ||||
|     slice: Bytes, | ||||
| } | ||||
|  | ||||
| impl<'a> Iterator for HeadersAsBytesIter<'a> { | ||||
|     type Item = (&'a str, Bytes); | ||||
|     fn next(&mut self) -> Option<Self::Item> { | ||||
|         self.headers.next().map(|header| { | ||||
|             let name = unsafe { | ||||
|                 let bytes = ::std::slice::from_raw_parts( | ||||
|                     self.slice.as_ref().as_ptr().offset(header.name.0 as isize), | ||||
|                     header.name.1 - header.name.0 | ||||
|                 ); | ||||
|                 ::std::str::from_utf8_unchecked(bytes) | ||||
|             }; | ||||
|             (name, self.slice.slice(header.value.0, header.value.1)) | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct FastWrite<'a>(&'a mut Vec<u8>); | ||||
|  | ||||
| impl<'a> fmt::Write for FastWrite<'a> { | ||||
|     #[inline] | ||||
|     fn write_str(&mut self, s: &str) -> fmt::Result { | ||||
|         extend(self.0, s.as_bytes()); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result { | ||||
|         fmt::write(self, args) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[inline] | ||||
| fn extend(dst: &mut Vec<u8>, data: &[u8]) { | ||||
|     dst.extend_from_slice(data); | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use bytes::BytesMut; | ||||
|  | ||||
|     use proto::{MessageHead, ServerTransaction, ClientTransaction, Http1Transaction}; | ||||
|     use header::{ContentLength, TransferEncoding}; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_parse_request() { | ||||
|         extern crate pretty_env_logger; | ||||
|         let _ = pretty_env_logger::init(); | ||||
|         let mut raw = BytesMut::from(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n".to_vec()); | ||||
|         let expected_len = raw.len(); | ||||
|         let (req, len) = ServerTransaction::parse(&mut raw).unwrap().unwrap(); | ||||
|         assert_eq!(len, expected_len); | ||||
|         assert_eq!(req.subject.0, ::Method::Get); | ||||
|         assert_eq!(req.subject.1, "/echo"); | ||||
|         assert_eq!(req.version, ::HttpVersion::Http11); | ||||
|         assert_eq!(req.headers.len(), 1); | ||||
|         assert_eq!(req.headers.get_raw("Host").map(|raw| &raw[0]), Some(b"hyper.rs".as_ref())); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     #[test] | ||||
|     fn test_parse_response() { | ||||
|         extern crate pretty_env_logger; | ||||
|         let _ = pretty_env_logger::init(); | ||||
|         let mut raw = BytesMut::from(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n".to_vec()); | ||||
|         let expected_len = raw.len(); | ||||
|         let (req, len) = ClientTransaction::parse(&mut raw).unwrap().unwrap(); | ||||
|         assert_eq!(len, expected_len); | ||||
|         assert_eq!(req.subject.0, 200); | ||||
|         assert_eq!(req.subject.1, "OK"); | ||||
|         assert_eq!(req.version, ::HttpVersion::Http11); | ||||
|         assert_eq!(req.headers.len(), 1); | ||||
|         assert_eq!(req.headers.get_raw("Content-Length").map(|raw| &raw[0]), Some(b"0".as_ref())); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_parse_request_errors() { | ||||
|         let mut raw = BytesMut::from(b"GET htt:p// HTTP/1.1\r\nHost: hyper.rs\r\n\r\n".to_vec()); | ||||
|         ServerTransaction::parse(&mut raw).unwrap_err(); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_parse_raw_status() { | ||||
|         let mut raw = BytesMut::from(b"HTTP/1.1 200 OK\r\n\r\n".to_vec()); | ||||
|         let (res, _) = ClientTransaction::parse(&mut raw).unwrap().unwrap(); | ||||
|         assert_eq!(res.subject.1, "OK"); | ||||
|  | ||||
|         let mut raw = BytesMut::from(b"HTTP/1.1 200 Howdy\r\n\r\n".to_vec()); | ||||
|         let (res, _) = ClientTransaction::parse(&mut raw).unwrap().unwrap(); | ||||
|         assert_eq!(res.subject.1, "Howdy"); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     #[test] | ||||
|     fn test_decoder_request() { | ||||
|         use super::Decoder; | ||||
|  | ||||
|         let method = &mut None; | ||||
|         let mut head = MessageHead::<::proto::RequestLine>::default(); | ||||
|  | ||||
|         head.subject.0 = ::Method::Get; | ||||
|         assert_eq!(Decoder::length(0), ServerTransaction::decoder(&head, method).unwrap()); | ||||
|         assert_eq!(*method, Some(::Method::Get)); | ||||
|  | ||||
|         head.subject.0 = ::Method::Post; | ||||
|         assert_eq!(Decoder::length(0), ServerTransaction::decoder(&head, method).unwrap()); | ||||
|         assert_eq!(*method, Some(::Method::Post)); | ||||
|  | ||||
|         head.headers.set(TransferEncoding::chunked()); | ||||
|         assert_eq!(Decoder::chunked(), ServerTransaction::decoder(&head, method).unwrap()); | ||||
|         // transfer-encoding and content-length = chunked | ||||
|         head.headers.set(ContentLength(10)); | ||||
|         assert_eq!(Decoder::chunked(), ServerTransaction::decoder(&head, method).unwrap()); | ||||
|  | ||||
|         head.headers.remove::<TransferEncoding>(); | ||||
|         assert_eq!(Decoder::length(10), ServerTransaction::decoder(&head, method).unwrap()); | ||||
|  | ||||
|         head.headers.set_raw("Content-Length", vec![b"5".to_vec(), b"5".to_vec()]); | ||||
|         assert_eq!(Decoder::length(5), ServerTransaction::decoder(&head, method).unwrap()); | ||||
|  | ||||
|         head.headers.set_raw("Content-Length", vec![b"10".to_vec(), b"11".to_vec()]); | ||||
|         ServerTransaction::decoder(&head, method).unwrap_err(); | ||||
|  | ||||
|         head.headers.remove::<ContentLength>(); | ||||
|  | ||||
|         head.headers.set_raw("Transfer-Encoding", "gzip"); | ||||
|         ServerTransaction::decoder(&head, method).unwrap_err(); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_decoder_response() { | ||||
|         use super::Decoder; | ||||
|  | ||||
|         let method = &mut Some(::Method::Get); | ||||
|         let mut head = MessageHead::<::proto::RawStatus>::default(); | ||||
|  | ||||
|         head.subject.0 = 204; | ||||
|         assert_eq!(Decoder::length(0), ClientTransaction::decoder(&head, method).unwrap()); | ||||
|         head.subject.0 = 304; | ||||
|         assert_eq!(Decoder::length(0), ClientTransaction::decoder(&head, method).unwrap()); | ||||
|  | ||||
|         head.subject.0 = 200; | ||||
|         assert_eq!(Decoder::eof(), ClientTransaction::decoder(&head, method).unwrap()); | ||||
|  | ||||
|         *method = Some(::Method::Head); | ||||
|         assert_eq!(Decoder::length(0), ClientTransaction::decoder(&head, method).unwrap()); | ||||
|  | ||||
|         *method = Some(::Method::Connect); | ||||
|         assert_eq!(Decoder::length(0), ClientTransaction::decoder(&head, method).unwrap()); | ||||
|  | ||||
|  | ||||
|         // CONNECT receiving non 200 can have a body | ||||
|         head.subject.0 = 404; | ||||
|         head.headers.set(ContentLength(10)); | ||||
|         assert_eq!(Decoder::length(10), ClientTransaction::decoder(&head, method).unwrap()); | ||||
|         head.headers.remove::<ContentLength>(); | ||||
|  | ||||
|  | ||||
|         *method = Some(::Method::Get); | ||||
|         head.headers.set(TransferEncoding::chunked()); | ||||
|         assert_eq!(Decoder::chunked(), ClientTransaction::decoder(&head, method).unwrap()); | ||||
|  | ||||
|         // transfer-encoding and content-length = chunked | ||||
|         head.headers.set(ContentLength(10)); | ||||
|         assert_eq!(Decoder::chunked(), ClientTransaction::decoder(&head, method).unwrap()); | ||||
|  | ||||
|         head.headers.remove::<TransferEncoding>(); | ||||
|         assert_eq!(Decoder::length(10), ClientTransaction::decoder(&head, method).unwrap()); | ||||
|  | ||||
|         head.headers.set_raw("Content-Length", vec![b"5".to_vec(), b"5".to_vec()]); | ||||
|         assert_eq!(Decoder::length(5), ClientTransaction::decoder(&head, method).unwrap()); | ||||
|  | ||||
|         head.headers.set_raw("Content-Length", vec![b"10".to_vec(), b"11".to_vec()]); | ||||
|         ClientTransaction::decoder(&head, method).unwrap_err(); | ||||
|     } | ||||
|  | ||||
|     #[cfg(feature = "nightly")] | ||||
|     use test::Bencher; | ||||
|  | ||||
|     #[cfg(feature = "nightly")] | ||||
|     #[bench] | ||||
|     fn bench_parse_incoming(b: &mut Bencher) { | ||||
|         let mut raw = BytesMut::from( | ||||
|             b"GET /super_long_uri/and_whatever?what_should_we_talk_about/\ | ||||
|             I_wonder/Hard_to_write_in_an_uri_after_all/you_have_to_make\ | ||||
|             _up_the_punctuation_yourself/how_fun_is_that?test=foo&test1=\ | ||||
|             foo1&test2=foo2&test3=foo3&test4=foo4 HTTP/1.1\r\nHost: \ | ||||
|             hyper.rs\r\nAccept: a lot of things\r\nAccept-Charset: \ | ||||
|             utf8\r\nAccept-Encoding: *\r\nAccess-Control-Allow-\ | ||||
|             Credentials: None\r\nAccess-Control-Allow-Origin: None\r\n\ | ||||
|             Access-Control-Allow-Methods: None\r\nAccess-Control-Allow-\ | ||||
|             Headers: None\r\nContent-Encoding: utf8\r\nContent-Security-\ | ||||
|             Policy: None\r\nContent-Type: text/html\r\nOrigin: hyper\ | ||||
|             \r\nSec-Websocket-Extensions: It looks super important!\r\n\ | ||||
|             Sec-Websocket-Origin: hyper\r\nSec-Websocket-Version: 4.3\r\ | ||||
|             \nStrict-Transport-Security: None\r\nUser-Agent: hyper\r\n\ | ||||
|             X-Content-Duration: None\r\nX-Content-Security-Policy: None\ | ||||
|             \r\nX-DNSPrefetch-Control: None\r\nX-Frame-Options: \ | ||||
|             Something important obviously\r\nX-Requested-With: Nothing\ | ||||
|             \r\n\r\n".to_vec() | ||||
|         ); | ||||
|         let len = raw.len(); | ||||
|  | ||||
|         b.bytes = len as u64; | ||||
|         b.iter(|| { | ||||
|             ServerTransaction::parse(&mut raw).unwrap(); | ||||
|             restart(&mut raw, len); | ||||
|         }); | ||||
|  | ||||
|  | ||||
|         fn restart(b: &mut BytesMut, len: usize) { | ||||
|             b.reserve(1); | ||||
|             unsafe { | ||||
|                 b.set_len(len); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[cfg(feature = "nightly")] | ||||
|     #[bench] | ||||
|     fn bench_server_transaction_encode(b: &mut Bencher) { | ||||
|         use header::{Headers, ContentLength, ContentType}; | ||||
|         use ::{StatusCode, HttpVersion}; | ||||
|  | ||||
|         let len = 108; | ||||
|         b.bytes = len as u64; | ||||
|  | ||||
|         let mut head = MessageHead { | ||||
|             subject: StatusCode::Ok, | ||||
|             headers: Headers::new(), | ||||
|             version: HttpVersion::Http11, | ||||
|         }; | ||||
|         head.headers.set(ContentLength(10)); | ||||
|         head.headers.set(ContentType::json()); | ||||
|  | ||||
|         b.iter(|| { | ||||
|             let mut vec = Vec::new(); | ||||
|             ServerTransaction::encode(head.clone(), true, &mut None, &mut vec); | ||||
|             assert_eq!(vec.len(), len); | ||||
|             ::test::black_box(vec); | ||||
|         }) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										1
									
								
								src/proto/h2/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/proto/h2/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
|  | ||||
							
								
								
									
										386
									
								
								src/proto/io.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										386
									
								
								src/proto/io.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,386 @@ | ||||
| use std::cmp; | ||||
| use std::fmt; | ||||
| use std::io::{self, Write}; | ||||
| use std::ptr; | ||||
|  | ||||
| use futures::{Async, Poll}; | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
|  | ||||
| use super::{Http1Transaction, MessageHead}; | ||||
| use bytes::{BytesMut, Bytes}; | ||||
|  | ||||
| const INIT_BUFFER_SIZE: usize = 8192; | ||||
| pub const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100; | ||||
|  | ||||
| pub struct Buffered<T> { | ||||
|     flush_pipeline: bool, | ||||
|     io: T, | ||||
|     read_blocked: bool, | ||||
|     read_buf: BytesMut, | ||||
|     write_buf: WriteBuf, | ||||
| } | ||||
|  | ||||
| impl<T> fmt::Debug for Buffered<T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Buffered") | ||||
|             .field("read_buf", &self.read_buf) | ||||
|             .field("write_buf", &self.write_buf) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: AsyncRead + AsyncWrite> Buffered<T> { | ||||
|     pub fn new(io: T) -> Buffered<T> { | ||||
|         Buffered { | ||||
|             flush_pipeline: false, | ||||
|             io: io, | ||||
|             read_buf: BytesMut::with_capacity(0), | ||||
|             write_buf: WriteBuf::new(), | ||||
|             read_blocked: false, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn set_flush_pipeline(&mut self, enabled: bool) { | ||||
|         self.flush_pipeline = enabled; | ||||
|     } | ||||
|  | ||||
|     pub fn read_buf(&self) -> &[u8] { | ||||
|         self.read_buf.as_ref() | ||||
|     } | ||||
|  | ||||
|     pub fn write_buf_mut(&mut self) -> &mut Vec<u8> { | ||||
|         self.write_buf.maybe_reset(); | ||||
|         self.write_buf.maybe_reserve(0); | ||||
|         &mut self.write_buf.0.bytes | ||||
|     } | ||||
|  | ||||
|     pub fn consume_leading_lines(&mut self) { | ||||
|         if !self.read_buf.is_empty() { | ||||
|             let mut i = 0; | ||||
|             while i < self.read_buf.len() { | ||||
|                 match self.read_buf[i] { | ||||
|                     b'\r' | b'\n' => i += 1, | ||||
|                     _ => break, | ||||
|                 } | ||||
|             } | ||||
|             self.read_buf.split_to(i); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn parse<S: Http1Transaction>(&mut self) -> Poll<MessageHead<S::Incoming>, ::Error> { | ||||
|         loop { | ||||
|             match try!(S::parse(&mut self.read_buf)) { | ||||
|                 Some(head) => { | ||||
|                     //trace!("parsed {} bytes out of {}", len, self.read_buf.len()); | ||||
|                     return Ok(Async::Ready(head.0)) | ||||
|                 }, | ||||
|                 None => { | ||||
|                     if self.read_buf.capacity() >= MAX_BUFFER_SIZE { | ||||
|                         debug!("MAX_BUFFER_SIZE reached, closing"); | ||||
|                         return Err(::Error::TooLarge); | ||||
|                     } | ||||
|                 }, | ||||
|             } | ||||
|             match try_ready!(self.read_from_io()) { | ||||
|                 0 => { | ||||
|                     trace!("parse eof"); | ||||
|                     //TODO: With Rust 1.14, this can be Error::from(ErrorKind) | ||||
|                     return Err(io::Error::new(io::ErrorKind::UnexpectedEof, ParseEof).into()); | ||||
|                 } | ||||
|                 _ => {}, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn read_from_io(&mut self) -> Poll<usize, io::Error> { | ||||
|         use bytes::BufMut; | ||||
|         self.read_blocked = false; | ||||
|         //TODO: use io.read_buf(), so we don't have to zero memory | ||||
|         //Reason this doesn't use it yet is because benchmarks show the | ||||
|         //slightest **decrease** in performance. Switching should be done | ||||
|         //when it doesn't cost anything. | ||||
|         if self.read_buf.remaining_mut() < INIT_BUFFER_SIZE { | ||||
|             self.read_buf.reserve(INIT_BUFFER_SIZE); | ||||
|             unsafe { // Zero out unused memory | ||||
|                 let buf = self.read_buf.bytes_mut(); | ||||
|                 let len = buf.len(); | ||||
|                 ptr::write_bytes(buf.as_mut_ptr(), 0, len); | ||||
|             } | ||||
|         } | ||||
|         unsafe { | ||||
|             let n = match self.io.read(self.read_buf.bytes_mut()) { | ||||
|                 Ok(n) => n, | ||||
|                 Err(e) => { | ||||
|                     if e.kind() == io::ErrorKind::WouldBlock { | ||||
|                         self.read_blocked = true; | ||||
|                         return Ok(Async::NotReady); | ||||
|                     } | ||||
|                     return Err(e) | ||||
|                 } | ||||
|             }; | ||||
|             self.read_buf.advance_mut(n); | ||||
|             Ok(Async::Ready(n)) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn buffer<B: AsRef<[u8]>>(&mut self, buf: B) -> usize { | ||||
|         self.write_buf.buffer(buf.as_ref()) | ||||
|     } | ||||
|  | ||||
|     pub fn io_mut(&mut self) -> &mut T { | ||||
|         &mut self.io | ||||
|     } | ||||
|  | ||||
|     pub fn is_read_blocked(&self) -> bool { | ||||
|         self.read_blocked | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Write> Write for Buffered<T> { | ||||
|     fn write(&mut self, data: &[u8]) -> io::Result<usize> { | ||||
|         let n = self.write_buf.buffer(data); | ||||
|         if n == 0 { | ||||
|             Err(io::ErrorKind::WouldBlock.into()) | ||||
|         } else { | ||||
|             Ok(n) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         if self.flush_pipeline && self.read_buf.is_empty() { | ||||
|             Ok(()) | ||||
|         } else if self.write_buf.remaining() == 0 { | ||||
|             self.io.flush() | ||||
|         } else { | ||||
|             loop { | ||||
|                 let n = try!(self.write_buf.write_into(&mut self.io)); | ||||
|                 trace!("flushed {} bytes", n); | ||||
|                 if self.write_buf.remaining() == 0 { | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|             self.io.flush() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub trait MemRead { | ||||
|     fn read_mem(&mut self, len: usize) -> Poll<Bytes, io::Error>; | ||||
| } | ||||
|  | ||||
| impl<T: AsyncRead + AsyncWrite> MemRead for Buffered<T> { | ||||
|     fn read_mem(&mut self, len: usize) -> Poll<Bytes, io::Error> { | ||||
|         trace!("Buffered.read_mem read_buf={}, wanted={}", self.read_buf.len(), len); | ||||
|         if !self.read_buf.is_empty() { | ||||
|             let n = ::std::cmp::min(len, self.read_buf.len()); | ||||
|             trace!("Buffered.read_mem read_buf is not empty, slicing {}", n); | ||||
|             Ok(Async::Ready(self.read_buf.split_to(n).freeze())) | ||||
|         } else { | ||||
|             let n = try_ready!(self.read_from_io()); | ||||
|             Ok(Async::Ready(self.read_buf.split_to(::std::cmp::min(len, n)).freeze())) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone)] | ||||
| pub struct Cursor<T> { | ||||
|     bytes: T, | ||||
|     pos: usize, | ||||
| } | ||||
|  | ||||
| impl<T: AsRef<[u8]>> Cursor<T> { | ||||
|     pub fn new(bytes: T) -> Cursor<T> { | ||||
|         Cursor { | ||||
|             bytes: bytes, | ||||
|             pos: 0, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn has_started(&self) -> bool { | ||||
|         self.pos != 0 | ||||
|     } | ||||
|  | ||||
|     pub fn is_written(&self) -> bool { | ||||
|         trace!("Cursor::is_written pos = {}, len = {}", self.pos, self.bytes.as_ref().len()); | ||||
|         self.pos >= self.bytes.as_ref().len() | ||||
|     } | ||||
|  | ||||
|     pub fn write_to<W: Write>(&mut self, dst: &mut W) -> io::Result<usize> { | ||||
|         if self.remaining() == 0 { | ||||
|             Ok(0) | ||||
|         } else { | ||||
|             dst.write(&self.bytes.as_ref()[self.pos..]).map(|n| { | ||||
|                 self.pos += n; | ||||
|                 n | ||||
|             }) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn remaining(&self) -> usize { | ||||
|         self.bytes.as_ref().len() - self.pos | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn buf(&self) -> &[u8] { | ||||
|         &self.bytes.as_ref()[self.pos..] | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn consume(&mut self, num: usize) { | ||||
|         trace!("Cursor::consume({})", num); | ||||
|         self.pos = ::std::cmp::min(self.bytes.as_ref().len(), self.pos + num); | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: AsRef<[u8]>> fmt::Debug for Cursor<T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Cursor") | ||||
|             .field("pos", &self.pos) | ||||
|             .field("len", &self.bytes.as_ref().len()) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub trait AtomicWrite { | ||||
|     fn write_atomic(&mut self, data: &[&[u8]]) -> io::Result<usize>; | ||||
| } | ||||
|  | ||||
| /* | ||||
| #[cfg(not(windows))] | ||||
| impl<T: Write + ::vecio::Writev> AtomicWrite for T { | ||||
|  | ||||
|     fn write_atomic(&mut self, bufs: &[&[u8]]) -> io::Result<usize> { | ||||
|         self.writev(bufs) | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| #[cfg(windows)] | ||||
| */ | ||||
| impl<T: Write> AtomicWrite for T { | ||||
|     fn write_atomic(&mut self, bufs: &[&[u8]]) -> io::Result<usize> { | ||||
|         if bufs.len() == 1 { | ||||
|             self.write(bufs[0]) | ||||
|         } else { | ||||
|             let vec = bufs.concat(); | ||||
|             self.write(&vec) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| //} | ||||
|  | ||||
| // an internal buffer to collect writes before flushes | ||||
| #[derive(Debug)] | ||||
| struct WriteBuf(Cursor<Vec<u8>>); | ||||
|  | ||||
| impl WriteBuf { | ||||
|     fn new() -> WriteBuf { | ||||
|         WriteBuf(Cursor::new(Vec::new())) | ||||
|     } | ||||
|  | ||||
|     fn write_into<W: Write>(&mut self, w: &mut W) -> io::Result<usize> { | ||||
|         self.0.write_to(w) | ||||
|     } | ||||
|  | ||||
|     fn buffer(&mut self, data: &[u8]) -> usize { | ||||
|         trace!("WriteBuf::buffer() len = {:?}", data.len()); | ||||
|         self.maybe_reset(); | ||||
|         self.maybe_reserve(data.len()); | ||||
|         let vec = &mut self.0.bytes; | ||||
|         let len = cmp::min(vec.capacity() - vec.len(), data.len()); | ||||
|         assert!(vec.capacity() - vec.len() >= len); | ||||
|         unsafe { | ||||
|             // in rust 1.9, we could use slice::copy_from_slice | ||||
|             ptr::copy( | ||||
|                 data.as_ptr(), | ||||
|                 vec.as_mut_ptr().offset(vec.len() as isize), | ||||
|                 len | ||||
|             ); | ||||
|             let new_len = vec.len() + len; | ||||
|             vec.set_len(new_len); | ||||
|         } | ||||
|         len | ||||
|     } | ||||
|  | ||||
|     fn remaining(&self) -> usize { | ||||
|         self.0.remaining() | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn maybe_reserve(&mut self, needed: usize) { | ||||
|         let vec = &mut self.0.bytes; | ||||
|         let cap = vec.capacity(); | ||||
|         if cap == 0 { | ||||
|             let init = cmp::min(MAX_BUFFER_SIZE, cmp::max(INIT_BUFFER_SIZE, needed)); | ||||
|             trace!("WriteBuf reserving initial {}", init); | ||||
|             vec.reserve(init); | ||||
|         } else if cap < MAX_BUFFER_SIZE { | ||||
|             vec.reserve(cmp::min(needed, MAX_BUFFER_SIZE - cap)); | ||||
|             trace!("WriteBuf reserved {}", vec.capacity() - cap); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn maybe_reset(&mut self) { | ||||
|         if self.0.pos != 0 && self.0.remaining() == 0 { | ||||
|             self.0.pos = 0; | ||||
|             unsafe { | ||||
|                 self.0.bytes.set_len(0); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| struct ParseEof; | ||||
|  | ||||
| impl fmt::Display for ParseEof { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.write_str("parse eof") | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ::std::error::Error for ParseEof { | ||||
|     fn description(&self) -> &str { | ||||
|         "parse eof" | ||||
|     } | ||||
| } | ||||
|  | ||||
| // TODO: Move tests to their own mod | ||||
| #[cfg(test)] | ||||
| use std::io::Read; | ||||
|  | ||||
| #[cfg(test)] | ||||
| impl<T: Read> MemRead for ::mock::AsyncIo<T> { | ||||
|     fn read_mem(&mut self, len: usize) -> Poll<Bytes, io::Error> { | ||||
|         let mut v = vec![0; len]; | ||||
|         let n = try_nb!(self.read(v.as_mut_slice())); | ||||
|         Ok(Async::Ready(BytesMut::from(&v[..n]).freeze())) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn test_iobuf_write_empty_slice() { | ||||
|     use mock::{AsyncIo, Buf as MockBuf}; | ||||
|  | ||||
|     let mut mock = AsyncIo::new(MockBuf::new(), 256); | ||||
|     mock.error(io::Error::new(io::ErrorKind::Other, "logic error")); | ||||
|  | ||||
|     let mut io_buf = Buffered::new(mock); | ||||
|  | ||||
|     // underlying io will return the logic error upon write, | ||||
|     // so we are testing that the io_buf does not trigger a write | ||||
|     // when there is nothing to flush | ||||
|     io_buf.flush().expect("should short-circuit flush"); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn test_parse_reads_until_blocked() { | ||||
|     use mock::{AsyncIo, Buf as MockBuf}; | ||||
|     // missing last line ending | ||||
|     let raw = "HTTP/1.1 200 OK\r\n"; | ||||
|  | ||||
|     let mock = AsyncIo::new(MockBuf::wrap(raw.into()), raw.len()); | ||||
|     let mut buffered = Buffered::new(mock); | ||||
|     assert_eq!(buffered.parse::<super::ClientTransaction>().unwrap(), Async::NotReady); | ||||
|     assert!(buffered.io.blocked()); | ||||
| } | ||||
							
								
								
									
										179
									
								
								src/proto/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								src/proto/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,179 @@ | ||||
| //! Pieces pertaining to the HTTP message protocol. | ||||
| use std::borrow::Cow; | ||||
| use std::fmt; | ||||
|  | ||||
| use bytes::BytesMut; | ||||
|  | ||||
| use header::{Connection, ConnectionOption, Expect}; | ||||
| use header::Headers; | ||||
| use method::Method; | ||||
| use status::StatusCode; | ||||
| use uri::Uri; | ||||
| use version::HttpVersion; | ||||
| use version::HttpVersion::{Http10, Http11}; | ||||
|  | ||||
| pub use self::conn::{Conn, KeepAlive, KA}; | ||||
| pub use self::body::{Body, TokioBody}; | ||||
| pub use self::chunk::Chunk; | ||||
|  | ||||
| mod body; | ||||
| mod chunk; | ||||
| mod conn; | ||||
| mod io; | ||||
| mod h1; | ||||
| //mod h2; | ||||
| pub mod request; | ||||
| pub mod response; | ||||
|  | ||||
|  | ||||
| /// An Incoming Message head. Includes request/status line, and headers. | ||||
| #[derive(Clone, Debug, Default, PartialEq)] | ||||
| pub struct MessageHead<S> { | ||||
|     /// HTTP version of the message. | ||||
|     pub version: HttpVersion, | ||||
|     /// Subject (request line or status line) of Incoming message. | ||||
|     pub subject: S, | ||||
|     /// Headers of the Incoming message. | ||||
|     pub headers: Headers | ||||
| } | ||||
|  | ||||
| /// An incoming request message. | ||||
| pub type RequestHead = MessageHead<RequestLine>; | ||||
|  | ||||
| #[derive(Debug, Default, PartialEq)] | ||||
| pub struct RequestLine(pub Method, pub Uri); | ||||
|  | ||||
| impl fmt::Display for RequestLine { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         write!(f, "{} {}", self.0, self.1) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// An incoming response message. | ||||
| pub type ResponseHead = MessageHead<RawStatus>; | ||||
|  | ||||
| impl<S> MessageHead<S> { | ||||
|     pub fn should_keep_alive(&self) -> bool { | ||||
|         should_keep_alive(self.version, &self.headers) | ||||
|     } | ||||
|  | ||||
|     pub fn expecting_continue(&self) -> bool { | ||||
|         expecting_continue(self.version, &self.headers) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ResponseHead { | ||||
|     /// Converts this head's RawStatus into a StatusCode. | ||||
|     #[inline] | ||||
|     pub fn status(&self) -> StatusCode { | ||||
|         self.subject.status() | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// The raw status code and reason-phrase. | ||||
| #[derive(Clone, PartialEq, Debug)] | ||||
| pub struct RawStatus(pub u16, pub Cow<'static, str>); | ||||
|  | ||||
| impl RawStatus { | ||||
|     /// Converts this into a StatusCode. | ||||
|     #[inline] | ||||
|     pub fn status(&self) -> StatusCode { | ||||
|         StatusCode::try_from(self.0).unwrap() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Display for RawStatus { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         write!(f, "{} {}", self.0, self.1) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<StatusCode> for RawStatus { | ||||
|     fn from(status: StatusCode) -> RawStatus { | ||||
|         RawStatus(status.into(), Cow::Borrowed(status.canonical_reason().unwrap_or(""))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for RawStatus { | ||||
|     fn default() -> RawStatus { | ||||
|         RawStatus(200, Cow::Borrowed("OK")) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<MessageHead<::StatusCode>> for MessageHead<RawStatus> { | ||||
|     fn from(head: MessageHead<::StatusCode>) -> MessageHead<RawStatus> { | ||||
|         MessageHead { | ||||
|             subject: head.subject.into(), | ||||
|             version: head.version, | ||||
|             headers: head.headers, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Checks if a connection should be kept alive. | ||||
| #[inline] | ||||
| pub fn should_keep_alive(version: HttpVersion, headers: &Headers) -> bool { | ||||
|     let ret = match (version, headers.get::<Connection>()) { | ||||
|         (Http10, None) => false, | ||||
|         (Http10, Some(conn)) if !conn.contains(&ConnectionOption::KeepAlive) => false, | ||||
|         (Http11, Some(conn)) if conn.contains(&ConnectionOption::Close)  => false, | ||||
|         _ => true | ||||
|     }; | ||||
|     trace!("should_keep_alive(version={:?}, header={:?}) = {:?}", version, headers.get::<Connection>(), ret); | ||||
|     ret | ||||
| } | ||||
|  | ||||
| /// Checks if a connection is expecting a `100 Continue` before sending its body. | ||||
| #[inline] | ||||
| pub fn expecting_continue(version: HttpVersion, headers: &Headers) -> bool { | ||||
|     let ret = match (version, headers.get::<Expect>()) { | ||||
|         (Http11, Some(&Expect::Continue)) => true, | ||||
|         _ => false | ||||
|     }; | ||||
|     trace!("expecting_continue(version={:?}, header={:?}) = {:?}", version, headers.get::<Expect>(), ret); | ||||
|     ret | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub enum ServerTransaction {} | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub enum ClientTransaction {} | ||||
|  | ||||
| pub trait Http1Transaction { | ||||
|     type Incoming; | ||||
|     type Outgoing: Default; | ||||
|     fn parse(bytes: &mut BytesMut) -> ParseResult<Self::Incoming>; | ||||
|     fn decoder(head: &MessageHead<Self::Incoming>, method: &mut Option<::Method>) -> ::Result<h1::Decoder>; | ||||
|     fn encode(head: MessageHead<Self::Outgoing>, has_body: bool, method: &mut Option<Method>, dst: &mut Vec<u8>) -> h1::Encoder; | ||||
| } | ||||
|  | ||||
| pub type ParseResult<T> = ::Result<Option<(MessageHead<T>, usize)>>; | ||||
|  | ||||
| #[test] | ||||
| fn test_should_keep_alive() { | ||||
|     let mut headers = Headers::new(); | ||||
|  | ||||
|     assert!(!should_keep_alive(Http10, &headers)); | ||||
|     assert!(should_keep_alive(Http11, &headers)); | ||||
|  | ||||
|     headers.set(Connection::close()); | ||||
|     assert!(!should_keep_alive(Http10, &headers)); | ||||
|     assert!(!should_keep_alive(Http11, &headers)); | ||||
|  | ||||
|     headers.set(Connection::keep_alive()); | ||||
|     assert!(should_keep_alive(Http10, &headers)); | ||||
|     assert!(should_keep_alive(Http11, &headers)); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn test_expecting_continue() { | ||||
|     let mut headers = Headers::new(); | ||||
|  | ||||
|     assert!(!expecting_continue(Http10, &headers)); | ||||
|     assert!(!expecting_continue(Http11, &headers)); | ||||
|  | ||||
|     headers.set(Expect::Continue); | ||||
|     assert!(!expecting_continue(Http10, &headers)); | ||||
|     assert!(expecting_continue(Http11, &headers)); | ||||
| } | ||||
							
								
								
									
										309
									
								
								src/proto/request.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										309
									
								
								src/proto/request.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,309 @@ | ||||
| use std::fmt; | ||||
| #[cfg(feature = "compat")] | ||||
| use std::mem::replace; | ||||
| use std::net::SocketAddr; | ||||
|  | ||||
| #[cfg(feature = "compat")] | ||||
| use http_types; | ||||
|  | ||||
| use header::Headers; | ||||
| use proto::{Body, MessageHead, RequestHead, RequestLine}; | ||||
| use method::Method; | ||||
| use uri::{self, Uri}; | ||||
| use version::HttpVersion; | ||||
|  | ||||
| /// An HTTP Request | ||||
| pub struct Request<B = Body> { | ||||
|     method: Method, | ||||
|     uri: Uri, | ||||
|     version: HttpVersion, | ||||
|     headers: Headers, | ||||
|     body: Option<B>, | ||||
|     is_proxy: bool, | ||||
|     remote_addr: Option<SocketAddr>, | ||||
| } | ||||
|  | ||||
| impl<B> Request<B> { | ||||
|     /// Construct a new Request. | ||||
|     #[inline] | ||||
|     pub fn new(method: Method, uri: Uri) -> Request<B> { | ||||
|         Request { | ||||
|             method: method, | ||||
|             uri: uri, | ||||
|             version: HttpVersion::default(), | ||||
|             headers: Headers::new(), | ||||
|             body: None, | ||||
|             is_proxy: false, | ||||
|             remote_addr: None, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Read the Request Uri. | ||||
|     #[inline] | ||||
|     pub fn uri(&self) -> &Uri { &self.uri } | ||||
|  | ||||
|     /// Read the Request Version. | ||||
|     #[inline] | ||||
|     pub fn version(&self) -> HttpVersion { self.version } | ||||
|  | ||||
|     /// Read the Request headers. | ||||
|     #[inline] | ||||
|     pub fn headers(&self) -> &Headers { &self.headers } | ||||
|  | ||||
|     /// Read the Request method. | ||||
|     #[inline] | ||||
|     pub fn method(&self) -> &Method { &self.method } | ||||
|  | ||||
|     /// Read the Request body. | ||||
|     #[inline] | ||||
|     pub fn body_ref(&self) -> Option<&B> { self.body.as_ref() } | ||||
|  | ||||
|     /// The remote socket address of this request | ||||
|     /// | ||||
|     /// This is an `Option`, because some underlying transports may not have | ||||
|     /// a socket address, such as Unix Sockets. | ||||
|     /// | ||||
|     /// This field is not used for outgoing requests. | ||||
|     #[inline] | ||||
|     pub fn remote_addr(&self) -> Option<SocketAddr> { self.remote_addr } | ||||
|  | ||||
|     /// The target path of this Request. | ||||
|     #[inline] | ||||
|     pub fn path(&self) -> &str { | ||||
|         self.uri.path() | ||||
|     } | ||||
|  | ||||
|     /// The query string of this Request. | ||||
|     #[inline] | ||||
|     pub fn query(&self) -> Option<&str> { | ||||
|         self.uri.query() | ||||
|     } | ||||
|  | ||||
|     /// Set the Method of this request. | ||||
|     #[inline] | ||||
|     pub fn set_method(&mut self, method: Method) { self.method = method; } | ||||
|  | ||||
|     /// Get a mutable reference to the Request headers. | ||||
|     #[inline] | ||||
|     pub fn headers_mut(&mut self) -> &mut Headers { &mut self.headers } | ||||
|  | ||||
|     /// Set the `Uri` of this request. | ||||
|     #[inline] | ||||
|     pub fn set_uri(&mut self, uri: Uri) { self.uri = uri; } | ||||
|  | ||||
|     /// Set the `HttpVersion` of this request. | ||||
|     #[inline] | ||||
|     pub fn set_version(&mut self, version: HttpVersion) { self.version = version; } | ||||
|  | ||||
|     /// Set the body of the request. | ||||
|     /// | ||||
|     /// By default, the body will be sent using `Transfer-Encoding: chunked`. To | ||||
|     /// override this behavior, manually set a [`ContentLength`] header with the | ||||
|     /// length of `body`. | ||||
|     #[inline] | ||||
|     pub fn set_body<T: Into<B>>(&mut self, body: T) { self.body = Some(body.into()); } | ||||
|  | ||||
|     /// Set that the URI should use the absolute form. | ||||
|     /// | ||||
|     /// This is only needed when talking to HTTP/1 proxies to URLs not | ||||
|     /// protected by TLS. | ||||
|     #[inline] | ||||
|     pub fn set_proxy(&mut self, is_proxy: bool) { self.is_proxy = is_proxy; } | ||||
| } | ||||
|  | ||||
| impl Request<Body> { | ||||
|     /// Deconstruct this Request into its pieces. | ||||
|     /// | ||||
|     /// Modifying these pieces will have no effect on how hyper behaves. | ||||
|     #[inline] | ||||
|     pub fn deconstruct(self) -> (Method, Uri, HttpVersion, Headers, Body) { | ||||
|         (self.method, self.uri, self.version, self.headers, self.body.unwrap_or_default()) | ||||
|     } | ||||
|  | ||||
|     /// Take the Request body. | ||||
|     #[inline] | ||||
|     pub fn body(self) -> Body { self.body.unwrap_or_default() } | ||||
| } | ||||
|  | ||||
| impl<B> fmt::Debug for Request<B> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Request") | ||||
|             .field("method", &self.method) | ||||
|             .field("uri", &self.uri) | ||||
|             .field("version", &self.version) | ||||
|             .field("remote_addr", &self.remote_addr) | ||||
|             .field("headers", &self.headers) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "compat")] | ||||
| impl From<Request> for http_types::Request<Body> { | ||||
|     fn from(from_req: Request) -> http_types::Request<Body> { | ||||
|         let (m, u, v, h, b) = from_req.deconstruct(); | ||||
|  | ||||
|         let to_req = http_types::Request::new(()); | ||||
|         let (mut to_parts, _) = to_req.into_parts(); | ||||
|  | ||||
|         to_parts.method = m.into(); | ||||
|         to_parts.uri = u.into(); | ||||
|         to_parts.version = v.into(); | ||||
|         to_parts.headers = h.into(); | ||||
|  | ||||
|         http_types::Request::from_parts(to_parts, b) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "compat")] | ||||
| impl<B> From<http_types::Request<B>> for Request<B> { | ||||
|     fn from(from_req: http_types::Request<B>) -> Request<B> { | ||||
|         let (from_parts, body) = from_req.into_parts(); | ||||
|  | ||||
|         let mut to_req = Request::new(from_parts.method.into(), from_parts.uri.into()); | ||||
|         to_req.set_version(from_parts.version.into()); | ||||
|         replace(to_req.headers_mut(), from_parts.headers.into()); | ||||
|         to_req.set_body(body); | ||||
|         to_req | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Constructs a request using a received ResponseHead and optional body | ||||
| pub fn from_wire<B>(addr: Option<SocketAddr>, incoming: RequestHead, body: B) -> Request<B> { | ||||
|     let MessageHead { version, subject: RequestLine(method, uri), headers } = incoming; | ||||
|  | ||||
|     Request::<B> { | ||||
|         method: method, | ||||
|         uri: uri, | ||||
|         headers: headers, | ||||
|         version: version, | ||||
|         remote_addr: addr, | ||||
|         body: Some(body), | ||||
|         is_proxy: false, | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn split<B>(req: Request<B>) -> (RequestHead, Option<B>) { | ||||
|     let uri = if req.is_proxy { | ||||
|         req.uri | ||||
|     } else { | ||||
|         uri::origin_form(&req.uri) | ||||
|     }; | ||||
|     let head = RequestHead { | ||||
|         subject: ::proto::RequestLine(req.method, uri), | ||||
|         headers: req.headers, | ||||
|         version: req.version, | ||||
|     }; | ||||
|     (head, req.body) | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     /* | ||||
|     use std::io::Write; | ||||
|     use std::str::from_utf8; | ||||
|     use Url; | ||||
|     use method::Method::{Get, Head, Post}; | ||||
|     use mock::{MockStream, MockConnector}; | ||||
|     use net::Fresh; | ||||
|     use header::{ContentLength,TransferEncoding,Encoding}; | ||||
|     use url::form_urlencoded; | ||||
|     use super::Request; | ||||
|     use http::h1::Http11Message; | ||||
|  | ||||
|     fn run_request(req: Request<Fresh>) -> Vec<u8> { | ||||
|         let req = req.start().unwrap(); | ||||
|         let message = req.message; | ||||
|         let mut message = message.downcast::<Http11Message>().ok().unwrap(); | ||||
|         message.flush_outgoing().unwrap(); | ||||
|         let stream = *message | ||||
|             .into_inner().downcast::<MockStream>().ok().unwrap(); | ||||
|         stream.write | ||||
|     } | ||||
|  | ||||
|     fn assert_no_body(s: &str) { | ||||
|         assert!(!s.contains("Content-Length:")); | ||||
|         assert!(!s.contains("Transfer-Encoding:")); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_get_empty_body() { | ||||
|         let req = Request::with_connector( | ||||
|             Get, Url::parse("http://example.dom").unwrap(), &mut MockConnector | ||||
|         ).unwrap(); | ||||
|         let bytes = run_request(req); | ||||
|         let s = from_utf8(&bytes[..]).unwrap(); | ||||
|         assert_no_body(s); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_head_empty_body() { | ||||
|         let req = Request::with_connector( | ||||
|             Head, Url::parse("http://example.dom").unwrap(), &mut MockConnector | ||||
|         ).unwrap(); | ||||
|         let bytes = run_request(req); | ||||
|         let s = from_utf8(&bytes[..]).unwrap(); | ||||
|         assert_no_body(s); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_url_query() { | ||||
|         let url = Url::parse("http://example.dom?q=value").unwrap(); | ||||
|         let req = Request::with_connector( | ||||
|             Get, url, &mut MockConnector | ||||
|         ).unwrap(); | ||||
|         let bytes = run_request(req); | ||||
|         let s = from_utf8(&bytes[..]).unwrap(); | ||||
|         assert!(s.contains("?q=value")); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_post_content_length() { | ||||
|         let url = Url::parse("http://example.dom").unwrap(); | ||||
|         let mut req = Request::with_connector( | ||||
|             Post, url, &mut MockConnector | ||||
|         ).unwrap(); | ||||
|         let mut body = String::new(); | ||||
|         form_urlencoded::Serializer::new(&mut body).append_pair("q", "value"); | ||||
|         req.headers_mut().set(ContentLength(body.len() as u64)); | ||||
|         let bytes = run_request(req); | ||||
|         let s = from_utf8(&bytes[..]).unwrap(); | ||||
|         assert!(s.contains("Content-Length:")); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_post_chunked() { | ||||
|         let url = Url::parse("http://example.dom").unwrap(); | ||||
|         let req = Request::with_connector( | ||||
|             Post, url, &mut MockConnector | ||||
|         ).unwrap(); | ||||
|         let bytes = run_request(req); | ||||
|         let s = from_utf8(&bytes[..]).unwrap(); | ||||
|         assert!(!s.contains("Content-Length:")); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_host_header() { | ||||
|         let url = Url::parse("http://example.dom").unwrap(); | ||||
|         let req = Request::with_connector( | ||||
|             Get, url, &mut MockConnector | ||||
|         ).unwrap(); | ||||
|         let bytes = run_request(req); | ||||
|         let s = from_utf8(&bytes[..]).unwrap(); | ||||
|         assert!(s.contains("Host: example.dom")); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_proxy() { | ||||
|         let url = Url::parse("http://example.dom").unwrap(); | ||||
|         let mut req = Request::with_connector( | ||||
|             Get, url, &mut MockConnector | ||||
|         ).unwrap(); | ||||
|         req.message.set_proxied(true); | ||||
|         let bytes = run_request(req); | ||||
|         let s = from_utf8(&bytes[..]).unwrap(); | ||||
|         let request_line = "GET http://example.dom/ HTTP/1.1"; | ||||
|         assert_eq!(&s[..request_line.len()], request_line); | ||||
|         assert!(s.contains("Host: example.dom")); | ||||
|     } | ||||
|     */ | ||||
| } | ||||
							
								
								
									
										212
									
								
								src/proto/response.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								src/proto/response.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,212 @@ | ||||
| use std::fmt; | ||||
| #[cfg(feature = "compat")] | ||||
| use std::mem::replace; | ||||
|  | ||||
| #[cfg(feature = "compat")] | ||||
| use http_types; | ||||
|  | ||||
| use header::{Header, Headers}; | ||||
| use proto::{MessageHead, ResponseHead, Body}; | ||||
| use status::StatusCode; | ||||
| use version::HttpVersion; | ||||
|  | ||||
| /// An HTTP Response | ||||
| pub struct Response<B = Body> { | ||||
|     version: HttpVersion, | ||||
|     headers: Headers, | ||||
|     status: StatusCode, | ||||
|     #[cfg(feature = "raw_status")] | ||||
|     raw_status: ::proto::RawStatus, | ||||
|     body: Option<B>, | ||||
| } | ||||
|  | ||||
| impl<B> Response<B> { | ||||
|     /// Constructs a default response | ||||
|     #[inline] | ||||
|     pub fn new() -> Response<B> { | ||||
|         Response::default() | ||||
|     } | ||||
|  | ||||
|     /// Get the HTTP version of this response. | ||||
|     #[inline] | ||||
|     pub fn version(&self) -> HttpVersion { self.version } | ||||
|  | ||||
|     /// Get the headers from the response. | ||||
|     #[inline] | ||||
|     pub fn headers(&self) -> &Headers { &self.headers } | ||||
|  | ||||
|     /// Get a mutable reference to the headers. | ||||
|     #[inline] | ||||
|     pub fn headers_mut(&mut self) -> &mut Headers { &mut self.headers } | ||||
|  | ||||
|     /// Get the status from the server. | ||||
|     #[inline] | ||||
|     pub fn status(&self) -> StatusCode { self.status } | ||||
|  | ||||
|     /// Get the raw status code and reason. | ||||
|     /// | ||||
|     /// This method is only useful when inspecting the raw subject line from | ||||
|     /// a received response. | ||||
|     #[inline] | ||||
|     #[cfg(feature = "raw_status")] | ||||
|     pub fn status_raw(&self) -> &::proto::RawStatus { &self.raw_status } | ||||
|  | ||||
|     /// Set the `StatusCode` for this response. | ||||
|     #[inline] | ||||
|     pub fn set_status(&mut self, status: StatusCode) { | ||||
|         self.status = status; | ||||
|     } | ||||
|  | ||||
|     /// Set the status and move the Response. | ||||
|     /// | ||||
|     /// Useful for the "builder-style" pattern. | ||||
|     #[inline] | ||||
|     pub fn with_status(mut self, status: StatusCode) -> Self { | ||||
|         self.set_status(status); | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Set a header and move the Response. | ||||
|     /// | ||||
|     /// Useful for the "builder-style" pattern. | ||||
|     #[inline] | ||||
|     pub fn with_header<H: Header>(mut self, header: H) -> Self { | ||||
|         self.headers.set(header); | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Set the headers and move the Response. | ||||
|     /// | ||||
|     /// Useful for the "builder-style" pattern. | ||||
|     #[inline] | ||||
|     pub fn with_headers(mut self, headers: Headers) -> Self { | ||||
|         self.headers = headers; | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Set the body. | ||||
|     #[inline] | ||||
|     pub fn set_body<T: Into<B>>(&mut self, body: T) { | ||||
|         self.body = Some(body.into()); | ||||
|     } | ||||
|  | ||||
|     /// Set the body and move the Response. | ||||
|     /// | ||||
|     /// Useful for the "builder-style" pattern. | ||||
|     #[inline] | ||||
|     pub fn with_body<T: Into<B>>(mut self, body: T) -> Self { | ||||
|         self.set_body(body); | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Read the body. | ||||
|     #[inline] | ||||
|     pub fn body_ref(&self) -> Option<&B> { self.body.as_ref() } | ||||
| } | ||||
|  | ||||
| impl Response<Body> { | ||||
|     /// Take the `Body` of this response. | ||||
|     #[inline] | ||||
|     pub fn body(self) -> Body { | ||||
|         self.body.unwrap_or_default() | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "raw_status"))] | ||||
| impl<B> Default for Response<B> { | ||||
|     fn default() -> Response<B> { | ||||
|         Response::<B> { | ||||
|             version: Default::default(), | ||||
|             headers: Default::default(), | ||||
|             status: Default::default(), | ||||
|             body: None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "raw_status")] | ||||
| impl<B> Default for Response<B> { | ||||
|     fn default() -> Response<B> { | ||||
|         Response::<B> { | ||||
|             version: Default::default(), | ||||
|             headers: Default::default(), | ||||
|             status: Default::default(), | ||||
|             raw_status: Default::default(), | ||||
|             body: None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for Response { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Response") | ||||
|             .field("status", &self.status) | ||||
|             .field("version", &self.version) | ||||
|             .field("headers", &self.headers) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "compat")] | ||||
| impl<B> From<http_types::Response<B>> for Response<B> { | ||||
|     fn from(from_res: http_types::Response<B>) -> Response<B> { | ||||
|         let (from_parts, body) = from_res.into_parts(); | ||||
|         let mut to_res = Response::new(); | ||||
|         to_res.version = from_parts.version.into(); | ||||
|         to_res.set_status(from_parts.status.into()); | ||||
|         replace(to_res.headers_mut(), from_parts.headers.into()); | ||||
|         to_res.with_body(body) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "compat")] | ||||
| impl From<Response> for http_types::Response<Body> { | ||||
|     fn from(mut from_res: Response) -> http_types::Response<Body> { | ||||
|         let (mut to_parts, ()) = http_types::Response::new(()).into_parts(); | ||||
|         to_parts.version = from_res.version().into(); | ||||
|         to_parts.status = from_res.status().into(); | ||||
|         let from_headers = replace(from_res.headers_mut(), Headers::new()); | ||||
|         to_parts.headers = from_headers.into(); | ||||
|         http_types::Response::from_parts(to_parts, from_res.body()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Constructs a response using a received ResponseHead and optional body | ||||
| #[inline] | ||||
| #[cfg(not(feature = "raw_status"))] | ||||
| pub fn from_wire<B>(incoming: ResponseHead, body: Option<B>) -> Response<B> { | ||||
|     let status = incoming.status(); | ||||
|  | ||||
|     Response::<B> { | ||||
|         status: status, | ||||
|         version: incoming.version, | ||||
|         headers: incoming.headers, | ||||
|         body: body, | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Constructs a response using a received ResponseHead and optional body | ||||
| #[inline] | ||||
| #[cfg(feature = "raw_status")] | ||||
| pub fn from_wire<B>(incoming: ResponseHead, body: Option<B>) -> Response<B> { | ||||
|     let status = incoming.status(); | ||||
|  | ||||
|     Response::<B> { | ||||
|         status: status, | ||||
|         version: incoming.version, | ||||
|         headers: incoming.headers, | ||||
|         raw_status: incoming.subject, | ||||
|         body: body, | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Splits this response into a MessageHead<StatusCode> and its body | ||||
| #[inline] | ||||
| pub fn split<B>(res: Response<B>) -> (MessageHead<StatusCode>, Option<B>) { | ||||
|     let head = MessageHead::<StatusCode> { | ||||
|         version: res.version, | ||||
|         headers: res.headers, | ||||
|         subject: res.status | ||||
|     }; | ||||
|     (head, res.body) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user