use std::fmt; use bytes::{Bytes, IntoBuf}; use http::{self, HeaderMap, HttpTryFrom}; use super::SendFrame; use h2::frame::{self, Frame, StreamId}; pub const SETTINGS: &'static [u8] = &[0, 0, 0, 4, 0, 0, 0, 0, 0]; pub const SETTINGS_ACK: &'static [u8] = &[0, 0, 0, 4, 1, 0, 0, 0, 0]; // ==== helper functions to easily construct h2 Frames ==== pub fn headers(id: T) -> Mock where T: Into, { Mock(frame::Headers::new( id.into(), frame::Pseudo::default(), HeaderMap::default(), )) } pub fn data(id: T, buf: B) -> Mock where T: Into, B: Into, { Mock(frame::Data::new(id.into(), buf.into())) } pub fn push_promise(id: T1, promised: T2) -> Mock where T1: Into, T2: Into, { Mock(frame::PushPromise::new( id.into(), promised.into(), frame::Pseudo::default(), HeaderMap::default(), )) } pub fn window_update(id: T, sz: u32) -> frame::WindowUpdate where T: Into, { frame::WindowUpdate::new(id.into(), sz) } pub fn go_away(id: T) -> Mock where T: Into, { Mock(frame::GoAway::new(id.into(), frame::Reason::NO_ERROR)) } pub fn reset(id: T) -> Mock where T: Into, { Mock(frame::Reset::new(id.into(), frame::Reason::NO_ERROR)) } pub fn settings() -> Mock { Mock(frame::Settings::default()) } pub fn settings_ack() -> Mock { Mock(frame::Settings::ack()) } pub fn ping(payload: [u8; 8]) -> Mock { Mock(frame::Ping::new(payload)) } // === Generic helpers of all frame types pub struct Mock(T); impl fmt::Debug for Mock { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, f) } } impl From> for Frame where T: Into, { fn from(src: Mock) -> Self { src.0.into() } } // Headers helpers impl Mock { pub fn request(self, method: M, uri: U) -> Self where M: HttpTryInto, U: HttpTryInto, { let method = method.try_into().unwrap(); let uri = uri.try_into().unwrap(); let (id, _, fields) = self.into_parts(); let frame = frame::Headers::new(id, frame::Pseudo::request(method, uri), fields); Mock(frame) } pub fn response(self, status: S) -> Self where S: HttpTryInto, { let status = status.try_into().unwrap(); let (id, _, fields) = self.into_parts(); let frame = frame::Headers::new(id, frame::Pseudo::response(status), fields); Mock(frame) } pub fn fields(self, fields: HeaderMap) -> Self { let (id, pseudo, _) = self.into_parts(); let frame = frame::Headers::new(id, pseudo, fields); Mock(frame) } pub fn field(self, key: K, value: V) -> Self where K: HttpTryInto, V: HttpTryInto, { let (id, pseudo, mut fields) = self.into_parts(); fields.insert(key.try_into().unwrap(), value.try_into().unwrap()); let frame = frame::Headers::new(id, pseudo, fields); Mock(frame) } pub fn scheme(self, value: &str) -> Self { let (id, mut pseudo, fields) = self.into_parts(); let value = value.parse().unwrap(); pseudo.set_scheme(value); Mock(frame::Headers::new(id, pseudo, fields)) } pub fn eos(mut self) -> Self { self.0.set_end_stream(); self } fn into_parts(self) -> (StreamId, frame::Pseudo, HeaderMap) { assert!(!self.0.is_end_stream(), "eos flag will be lost"); assert!(self.0.is_end_headers(), "unset eoh will be lost"); let id = self.0.stream_id(); let parts = self.0.into_parts(); (id, parts.0, parts.1) } } impl From> for frame::Headers { fn from(src: Mock) -> Self { src.0 } } impl From> for SendFrame { fn from(src: Mock) -> Self { Frame::Headers(src.0) } } // Data helpers impl Mock { pub fn eos(mut self) -> Self { self.0.set_end_stream(true); self } } impl From> for SendFrame { fn from(src: Mock) -> Self { let id = src.0.stream_id(); let eos = src.0.is_end_stream(); let payload = src.0.into_payload(); let mut frame = frame::Data::new(id, payload.into_buf()); frame.set_end_stream(eos); Frame::Data(frame) } } // PushPromise helpers impl Mock { pub fn request(self, method: M, uri: U) -> Self where M: HttpTryInto, U: HttpTryInto, { let method = method.try_into().unwrap(); let uri = uri.try_into().unwrap(); let (id, promised, _, fields) = self.into_parts(); let frame = frame::PushPromise::new(id, promised, frame::Pseudo::request(method, uri), fields); Mock(frame) } pub fn fields(self, fields: HeaderMap) -> Self { let (id, promised, pseudo, _) = self.into_parts(); let frame = frame::PushPromise::new(id, promised, pseudo, fields); Mock(frame) } fn into_parts(self) -> (StreamId, StreamId, frame::Pseudo, HeaderMap) { assert!(self.0.is_end_headers(), "unset eoh will be lost"); let id = self.0.stream_id(); let promised = self.0.promised_id(); let parts = self.0.into_parts(); (id, promised, parts.0, parts.1) } } impl From> for SendFrame { fn from(src: Mock) -> Self { Frame::PushPromise(src.0) } } // GoAway helpers impl Mock { pub fn protocol_error(self) -> Self { Mock(frame::GoAway::new( self.0.last_stream_id(), frame::Reason::PROTOCOL_ERROR, )) } pub fn flow_control(self) -> Self { Mock(frame::GoAway::new( self.0.last_stream_id(), frame::Reason::FLOW_CONTROL_ERROR, )) } pub fn frame_size(self) -> Self { Mock(frame::GoAway::new( self.0.last_stream_id(), frame::Reason::FRAME_SIZE_ERROR, )) } } impl From> for SendFrame { fn from(src: Mock) -> Self { Frame::GoAway(src.0) } } // ==== Reset helpers impl Mock { pub fn protocol_error(self) -> Self { let id = self.0.stream_id(); Mock(frame::Reset::new(id, frame::Reason::PROTOCOL_ERROR)) } pub fn flow_control(self) -> Self { let id = self.0.stream_id(); Mock(frame::Reset::new(id, frame::Reason::FLOW_CONTROL_ERROR)) } pub fn refused(self) -> Self { let id = self.0.stream_id(); Mock(frame::Reset::new(id, frame::Reason::REFUSED_STREAM)) } pub fn cancel(self) -> Self { let id = self.0.stream_id(); Mock(frame::Reset::new(id, frame::Reason::CANCEL)) } } impl From> for SendFrame { fn from(src: Mock) -> Self { Frame::Reset(src.0) } } // ==== Settings helpers impl Mock { pub fn max_concurrent_streams(mut self, max: u32) -> Self { self.0.set_max_concurrent_streams(Some(max)); self } pub fn initial_window_size(mut self, val: u32) -> Self { self.0.set_initial_window_size(Some(val)); self } } impl From> for frame::Settings { fn from(src: Mock) -> Self { src.0 } } impl From> for SendFrame { fn from(src: Mock) -> Self { Frame::Settings(src.0) } } // ==== Ping helpers impl Mock { pub fn pong(self) -> Self { let payload = self.0.into_payload(); Mock(frame::Ping::pong(payload)) } } impl From> for SendFrame { fn from(src: Mock) -> Self { Frame::Ping(src.0) } } // ==== "trait alias" for types that are HttpTryFrom and have Debug Errors ==== pub trait HttpTryInto { type Error: fmt::Debug; fn try_into(self) -> Result; } impl HttpTryInto for U where T: HttpTryFrom, T::Error: fmt::Debug, { type Error = T::Error; fn try_into(self) -> Result { T::try_from(self) } }