Validate & convert messages before buffering
Malformed requests and responses should immediately result in a RST_STREAM. To support this, received header frames are validated and converted to Request / Response values immediately on receipt and before buffering.
This commit is contained in:
		| @@ -26,7 +26,7 @@ use bytes::{Buf, IntoBuf}; | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
| use tokio_io::codec::length_delimited; | ||||
|  | ||||
| use std::io; | ||||
| use std::{fmt, io}; | ||||
|  | ||||
| /// Either a Client or a Server | ||||
| pub trait Peer { | ||||
| @@ -34,7 +34,7 @@ pub trait Peer { | ||||
|     type Send; | ||||
|  | ||||
|     /// Message type polled from the transport | ||||
|     type Poll; | ||||
|     type Poll: fmt::Debug; | ||||
|  | ||||
|     fn is_server() -> bool; | ||||
|  | ||||
| @@ -43,7 +43,7 @@ pub trait Peer { | ||||
|         headers: Self::Send, | ||||
|         end_of_stream: bool) -> frame::Headers; | ||||
|  | ||||
|     fn convert_poll_message(headers: frame::Headers) -> Result<Self::Poll, ConnectionError>; | ||||
|     fn convert_poll_message(headers: frame::Headers) -> Result<Self::Poll, ProtoError>; | ||||
| } | ||||
|  | ||||
| pub type PingPayload = [u8; 8]; | ||||
|   | ||||
| @@ -36,7 +36,7 @@ pub(super) struct Recv<B, P> | ||||
|     pending_accept: store::Queue<B, stream::NextAccept, P>, | ||||
|  | ||||
|     /// Holds frames that are waiting to be read | ||||
|     buffer: Buffer<Frame<Bytes>>, | ||||
|     buffer: Buffer<Event<P::Poll>>, | ||||
|  | ||||
|     /// Refused StreamId, this represents a frame that must be sent out. | ||||
|     refused: Option<StreamId>, | ||||
| @@ -44,6 +44,13 @@ pub(super) struct Recv<B, P> | ||||
|     _p: PhantomData<(B)>, | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub(super) enum Event<T> { | ||||
|     Headers(T), | ||||
|     Data(Bytes), | ||||
|     Trailers(::HeaderMap), | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, Copy)] | ||||
| struct Indices { | ||||
|     head: store::Key, | ||||
| @@ -110,44 +117,13 @@ impl<B, P> Recv<B, P> | ||||
|         Ok(Some(id)) | ||||
|     } | ||||
|  | ||||
|     pub fn take_request(&mut self, stream: &mut store::Ptr<B, P>) | ||||
|         -> Result<Request<()>, ConnectionError> | ||||
|     { | ||||
|         match stream.pending_recv.pop_front(&mut self.buffer) { | ||||
|             Some(Frame::Headers(frame)) => { | ||||
|                 // TODO: This error should probably be caught on receipt of the | ||||
|                 // frame vs. now. | ||||
|                 Ok(server::Peer::convert_poll_message(frame)?) | ||||
|             } | ||||
|             _ => panic!(), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn poll_response(&mut self, stream: &mut store::Ptr<B, P>) | ||||
|         -> Poll<Response<()>, ConnectionError> { | ||||
|         // If the buffer is not empty, then the first frame must be a HEADERS | ||||
|         // frame or the user violated the contract. | ||||
|         match stream.pending_recv.pop_front(&mut self.buffer) { | ||||
|             Some(Frame::Headers(v)) => { | ||||
|                 // TODO: This error should probably be caught on receipt of the | ||||
|                 // frame vs. now. | ||||
|                 Ok(client::Peer::convert_poll_message(v)?.into()) | ||||
|             } | ||||
|             Some(_) => unimplemented!(), | ||||
|             None => { | ||||
|                 stream.state.ensure_recv_open()?; | ||||
|  | ||||
|                 stream.recv_task = Some(task::current()); | ||||
|                 Ok(Async::NotReady) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Transition the stream state based on receiving headers | ||||
|     /// | ||||
|     /// The caller ensures that the frame represents headers and not trailers. | ||||
|     pub fn recv_headers(&mut self, | ||||
|                         frame: frame::Headers, | ||||
|                         stream: &mut store::Ptr<B, P>) | ||||
|         -> Result<(), ConnectionError> | ||||
|         -> Result<(), ProtoError> | ||||
|     { | ||||
|         trace!("opening stream; init_window={}", self.init_window_sz); | ||||
|         let is_initial = stream.state.recv_open(frame.is_end_stream())?; | ||||
| @@ -161,7 +137,7 @@ impl<B, P> Recv<B, P> | ||||
|                 self.next_stream_id = frame.stream_id(); | ||||
|                 self.next_stream_id.increment(); | ||||
|             } else { | ||||
|                 return Err(ProtocolError.into()); | ||||
|                 return Err(ProtoError::Connection(ProtocolError)); | ||||
|             } | ||||
|  | ||||
|             // TODO: be smarter about this logic | ||||
| @@ -173,8 +149,10 @@ impl<B, P> Recv<B, P> | ||||
|             self.inc_num_streams(); | ||||
|         } | ||||
|  | ||||
|         let message = P::convert_poll_message(frame)?; | ||||
|  | ||||
|         // Push the frame onto the stream's recv buffer | ||||
|         stream.pending_recv.push_back(&mut self.buffer, frame.into()); | ||||
|         stream.pending_recv.push_back(&mut self.buffer, Event::Headers(message)); | ||||
|         stream.notify_recv(); | ||||
|  | ||||
|         // Only servers can receive a headers frame that initiates the stream. | ||||
| @@ -190,13 +168,15 @@ impl<B, P> Recv<B, P> | ||||
|     pub fn recv_trailers(&mut self, | ||||
|                          frame: frame::Headers, | ||||
|                          stream: &mut store::Ptr<B, P>) | ||||
|         -> Result<(), ConnectionError> | ||||
|         -> Result<(), ProtoError> | ||||
|     { | ||||
|         // Transition the state | ||||
|         stream.state.recv_close()?; | ||||
|  | ||||
|         let trailers = frame.into_fields(); | ||||
|  | ||||
|         // Push the frame onto the stream's recv buffer | ||||
|         stream.pending_recv.push_back(&mut self.buffer, frame.into()); | ||||
|         stream.pending_recv.push_back(&mut self.buffer, Event::Trailers(trailers)); | ||||
|         stream.notify_recv(); | ||||
|  | ||||
|         Ok(()) | ||||
| @@ -236,7 +216,7 @@ impl<B, P> Recv<B, P> | ||||
|         } | ||||
|  | ||||
|         stream.pending_recv.peek_front(&self.buffer) | ||||
|             .map(|frame| !frame.is_data()) | ||||
|             .map(|event| !event.is_data()) | ||||
|             .unwrap_or(true) | ||||
|     } | ||||
|  | ||||
| @@ -278,11 +258,15 @@ impl<B, P> Recv<B, P> | ||||
|         stream.in_flight_recv_data += sz; | ||||
|  | ||||
|         if frame.is_end_stream() { | ||||
|             try!(stream.state.recv_close()); | ||||
|             if stream.state.recv_close().is_err() { | ||||
|                 return Err(ProtocolError.into()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         let event = Event::Data(frame.into_payload()); | ||||
|  | ||||
|         // Push the frame onto the recv buffer | ||||
|         stream.pending_recv.push_back(&mut self.buffer, frame.into()); | ||||
|         stream.pending_recv.push_back(&mut self.buffer, event); | ||||
|         stream.notify_recv(); | ||||
|  | ||||
|         Ok(()) | ||||
| @@ -530,12 +514,12 @@ impl<B, P> Recv<B, P> | ||||
|         -> Poll<Option<Bytes>, ConnectionError> | ||||
|     { | ||||
|         match stream.pending_recv.pop_front(&mut self.buffer) { | ||||
|             Some(Frame::Data(frame)) => { | ||||
|                 Ok(Some(frame.into_payload()).into()) | ||||
|             Some(Event::Data(payload)) => { | ||||
|                 Ok(Some(payload).into()) | ||||
|             } | ||||
|             Some(frame) => { | ||||
|             Some(event) => { | ||||
|                 // Frame is trailer | ||||
|                 stream.pending_recv.push_front(&mut self.buffer, frame); | ||||
|                 stream.pending_recv.push_front(&mut self.buffer, event); | ||||
|  | ||||
|                 // No more data frames | ||||
|                 Ok(None.into()) | ||||
| @@ -557,8 +541,8 @@ impl<B, P> Recv<B, P> | ||||
|         -> Poll<Option<HeaderMap>, ConnectionError> | ||||
|     { | ||||
|         match stream.pending_recv.pop_front(&mut self.buffer) { | ||||
|             Some(Frame::Headers(frame)) => { | ||||
|                 Ok(Some(frame.into_fields()).into()) | ||||
|             Some(Event::Trailers(trailers)) => { | ||||
|                 Ok(Some(trailers).into()) | ||||
|             } | ||||
|             Some(_) => { | ||||
|                 // TODO: This is a user error. `poll_trailers` was called before | ||||
| @@ -583,3 +567,55 @@ impl<B, P> Recv<B, P> | ||||
|         unimplemented!(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<B> Recv<B, server::Peer> | ||||
|     where B: Buf, | ||||
| { | ||||
|     /// TODO: Should this fn return `Result`? | ||||
|     pub fn take_request(&mut self, stream: &mut store::Ptr<B, server::Peer>) | ||||
|         -> Result<Request<()>, ConnectionError> | ||||
|     { | ||||
|         match stream.pending_recv.pop_front(&mut self.buffer) { | ||||
|             Some(Event::Headers(request)) => Ok(request), | ||||
|                 /* | ||||
|                 // TODO: This error should probably be caught on receipt of the | ||||
|                 // frame vs. now. | ||||
|                 Ok(server::Peer::convert_poll_message(frame)?) | ||||
|                 */ | ||||
|             _ => panic!(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<B> Recv<B, client::Peer> | ||||
|     where B: Buf, | ||||
| { | ||||
|     pub fn poll_response(&mut self, stream: &mut store::Ptr<B, client::Peer>) | ||||
|         -> Poll<Response<()>, ConnectionError> { | ||||
|         // If the buffer is not empty, then the first frame must be a HEADERS | ||||
|         // frame or the user violated the contract. | ||||
|         match stream.pending_recv.pop_front(&mut self.buffer) { | ||||
|             Some(Event::Headers(response)) => { | ||||
|                 Ok(response.into()) | ||||
|             } | ||||
|             Some(_) => unimplemented!(), | ||||
|             None => { | ||||
|                 stream.state.ensure_recv_open()?; | ||||
|  | ||||
|                 stream.recv_task = Some(task::current()); | ||||
|                 Ok(Async::NotReady) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| // ===== impl Event ===== | ||||
|  | ||||
| impl<T> Event<T> { | ||||
|     fn is_data(&self) -> bool { | ||||
|         match *self { | ||||
|             Event::Data(..) => true, | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| use ConnectionError; | ||||
| use proto::ProtoError; | ||||
| use error::Reason; | ||||
| use error::Reason::*; | ||||
| use error::User::*; | ||||
| @@ -125,7 +126,7 @@ impl State { | ||||
|     /// frame is received. | ||||
|     /// | ||||
|     /// Returns true if this transitions the state to Open | ||||
|     pub fn recv_open(&mut self, eos: bool) -> Result<bool, ConnectionError> { | ||||
|     pub fn recv_open(&mut self, eos: bool) -> Result<bool, ProtoError> { | ||||
|         let remote = Peer::Streaming; | ||||
|         let mut initial = false; | ||||
|  | ||||
| @@ -173,7 +174,7 @@ impl State { | ||||
|             } | ||||
|             _ => { | ||||
|                 // All other transitions result in a protocol error | ||||
|                 return Err(ProtocolError.into()); | ||||
|                 return Err(ProtoError::Connection(ProtocolError)); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
| @@ -192,7 +193,7 @@ impl State { | ||||
|     } | ||||
|  | ||||
|     /// Indicates that the remote side will not send more data to the local. | ||||
|     pub fn recv_close(&mut self) -> Result<(), ConnectionError> { | ||||
|     pub fn recv_close(&mut self) -> Result<(), ProtoError> { | ||||
|         match self.inner { | ||||
|             Open { local, .. } => { | ||||
|                 // The remote side will continue to receive data. | ||||
| @@ -205,7 +206,7 @@ impl State { | ||||
|                 self.inner = Closed(None); | ||||
|                 Ok(()) | ||||
|             } | ||||
|             _ => Err(ProtocolError.into()), | ||||
|             _ => Err(ProtoError::Connection(ProtocolError)), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -64,7 +64,7 @@ pub(super) struct Stream<B, P> | ||||
|     pub is_pending_window_update: bool, | ||||
|  | ||||
|     /// Frames pending for this stream to read | ||||
|     pub pending_recv: buffer::Deque<Frame<Bytes>>, | ||||
|     pub pending_recv: buffer::Deque<recv::Event<P::Poll>>, | ||||
|  | ||||
|     /// Task tracking receiving frames | ||||
|     pub recv_task: Option<task::Task>, | ||||
|   | ||||
| @@ -92,7 +92,7 @@ impl<B, P> Streams<B, P> | ||||
|         let stream = me.store.resolve(key); | ||||
|  | ||||
|         me.actions.transition(stream, |actions, stream| { | ||||
|             if stream.state.is_recv_headers() { | ||||
|             let res = if stream.state.is_recv_headers() { | ||||
|                 actions.recv.recv_headers(frame, stream) | ||||
|             } else { | ||||
|                 if !frame.is_end_stream() { | ||||
| @@ -101,6 +101,17 @@ impl<B, P> Streams<B, P> | ||||
|                 } | ||||
|  | ||||
|                 actions.recv.recv_trailers(frame, stream) | ||||
|             }; | ||||
|  | ||||
|             match res { | ||||
|                 Ok(()) => Ok(()), | ||||
|                 Err(ProtoError::Connection(reason)) => Err(reason.into()), | ||||
|                 Err(ProtoError::Stream { reason, .. }) => { | ||||
|                     // Reset the stream. | ||||
|                     actions.send.send_reset(reason, stream, &mut actions.task); | ||||
|                     Ok(()) | ||||
|                 } | ||||
|                 Err(ProtoError::Io(_)) => unreachable!(), | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| @@ -381,21 +392,6 @@ impl<B, P> StreamRef<B, P> | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     /// Called by the server after the stream is accepted. Given that clients | ||||
|     /// initialize streams by sending HEADERS, the request will always be | ||||
|     /// available. | ||||
|     /// | ||||
|     /// # Panics | ||||
|     /// | ||||
|     /// This function panics if the request isn't present. | ||||
|     pub fn take_request(&self) -> Result<Request<()>, ConnectionError> { | ||||
|         let mut me = self.inner.lock().unwrap(); | ||||
|         let me = &mut *me; | ||||
|  | ||||
|         let mut stream = me.store.resolve(self.key); | ||||
|         me.actions.recv.take_request(&mut stream) | ||||
|     } | ||||
|  | ||||
|     pub fn send_reset(&mut self, reason: Reason) { | ||||
|         let mut me = self.inner.lock().unwrap(); | ||||
|         let me = &mut *me; | ||||
| @@ -431,15 +427,6 @@ impl<B, P> StreamRef<B, P> | ||||
|         me.actions.recv.body_is_empty(&stream) | ||||
|     } | ||||
|  | ||||
|     pub fn poll_response(&mut self) -> Poll<Response<()>, ConnectionError> { | ||||
|         let mut me = self.inner.lock().unwrap(); | ||||
|         let me = &mut *me; | ||||
|  | ||||
|         let mut stream = me.store.resolve(self.key); | ||||
|  | ||||
|         me.actions.recv.poll_response(&mut stream) | ||||
|     } | ||||
|  | ||||
|     pub fn poll_data(&mut self) -> Poll<Option<Bytes>, ConnectionError> { | ||||
|         let mut me = self.inner.lock().unwrap(); | ||||
|         let me = &mut *me; | ||||
| @@ -503,6 +490,38 @@ impl<B, P> StreamRef<B, P> | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<B> StreamRef<B, server::Peer> | ||||
|     where B: Buf, | ||||
| { | ||||
|     /// Called by the server after the stream is accepted. Given that clients | ||||
|     /// initialize streams by sending HEADERS, the request will always be | ||||
|     /// available. | ||||
|     /// | ||||
|     /// # Panics | ||||
|     /// | ||||
|     /// This function panics if the request isn't present. | ||||
|     pub fn take_request(&self) -> Result<Request<()>, ConnectionError> { | ||||
|         let mut me = self.inner.lock().unwrap(); | ||||
|         let me = &mut *me; | ||||
|  | ||||
|         let mut stream = me.store.resolve(self.key); | ||||
|         me.actions.recv.take_request(&mut stream) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<B> StreamRef<B, client::Peer> | ||||
|     where B: Buf, | ||||
| { | ||||
|     pub fn poll_response(&mut self) -> Poll<Response<()>, ConnectionError> { | ||||
|         let mut me = self.inner.lock().unwrap(); | ||||
|         let me = &mut *me; | ||||
|  | ||||
|         let mut stream = me.store.resolve(self.key); | ||||
|  | ||||
|         me.actions.recv.poll_response(&mut stream) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<B, P> Clone for StreamRef<B, P> | ||||
|     where P: Peer, | ||||
| { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user