blob: 36fcafa0f00374a82763a3843d68ce88657ac11f [file] [log] [blame]
use hpack;
use bytes::Bytes;
use std::fmt;
/// A helper macro that unpacks a sequence of 4 bytes found in the buffer with
/// the given identifier, starting at the given offset, into the given integer
/// type. Obviously, the integer type should be able to support at least 4
/// bytes.
///
/// # Examples
///
/// ```rust
/// let buf: [u8; 4] = [0, 0, 0, 1];
/// assert_eq!(1u32, unpack_octets_4!(buf, 0, u32));
/// ```
#[macro_escape]
macro_rules! unpack_octets_4 {
// TODO: Get rid of this macro
($buf:expr, $offset:expr, $tip:ty) => (
(($buf[$offset + 0] as $tip) << 24) |
(($buf[$offset + 1] as $tip) << 16) |
(($buf[$offset + 2] as $tip) << 8) |
(($buf[$offset + 3] as $tip) << 0)
);
}
mod data;
mod go_away;
mod head;
mod headers;
mod ping;
mod priority;
mod reason;
mod reset;
mod settings;
mod stream_id;
mod util;
mod window_update;
pub use self::data::Data;
pub use self::go_away::GoAway;
pub use self::head::{Head, Kind};
pub use self::headers::{Continuation, Headers, Pseudo, PushPromise};
pub use self::ping::Ping;
pub use self::priority::{Priority, StreamDependency};
pub use self::reason::Reason;
pub use self::reset::Reset;
pub use self::settings::Settings;
pub use self::stream_id::{StreamId, StreamIdOverflow};
pub use self::window_update::WindowUpdate;
// Re-export some constants
pub use self::settings::{
DEFAULT_INITIAL_WINDOW_SIZE,
DEFAULT_MAX_FRAME_SIZE,
DEFAULT_SETTINGS_HEADER_TABLE_SIZE,
MAX_INITIAL_WINDOW_SIZE,
MAX_MAX_FRAME_SIZE,
};
pub type FrameSize = u32;
pub const HEADER_LEN: usize = 9;
#[derive(Eq, PartialEq)]
pub enum Frame<T = Bytes> {
Data(Data<T>),
Headers(Headers),
Priority(Priority),
PushPromise(PushPromise),
Settings(Settings),
Ping(Ping),
GoAway(GoAway),
WindowUpdate(WindowUpdate),
Reset(Reset),
}
impl<T> Frame<T> {
pub fn map<F, U>(self, f: F) -> Frame<U>
where
F: FnOnce(T) -> U,
{
use self::Frame::*;
match self {
Data(frame) => frame.map(f).into(),
Headers(frame) => frame.into(),
Priority(frame) => frame.into(),
PushPromise(frame) => frame.into(),
Settings(frame) => frame.into(),
Ping(frame) => frame.into(),
GoAway(frame) => frame.into(),
WindowUpdate(frame) => frame.into(),
Reset(frame) => frame.into(),
}
}
}
impl<T> fmt::Debug for Frame<T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
use self::Frame::*;
match *self {
Data(ref frame) => write!(fmt, "Frame::Data({:?})", frame),
Headers(ref frame) => write!(fmt, "Frame::Headers({:?})", frame),
Priority(ref frame) => write!(fmt, "Frame::Priority({:?})", frame),
PushPromise(ref frame) => write!(fmt, "Frame::PushPromise({:?})", frame),
Settings(ref frame) => write!(fmt, "Frame::Settings({:?})", frame),
Ping(ref frame) => write!(fmt, "Frame::Ping({:?})", frame),
GoAway(ref frame) => write!(fmt, "Frame::GoAway({:?})", frame),
WindowUpdate(ref frame) => write!(fmt, "Frame::WindowUpdate({:?})", frame),
Reset(ref frame) => write!(fmt, "Frame::Reset({:?})", frame),
}
}
}
/// Errors that can occur during parsing an HTTP/2 frame.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error {
/// A length value other than 8 was set on a PING message.
BadFrameSize,
/// The padding length was larger than the frame-header-specified
/// length of the payload.
TooMuchPadding,
/// An invalid setting value was provided
InvalidSettingValue,
/// An invalid window update value
InvalidWindowUpdateValue,
/// The payload length specified by the frame header was not the
/// value necessary for the specific frame type.
InvalidPayloadLength,
/// Received a payload with an ACK settings frame
InvalidPayloadAckSettings,
/// An invalid stream identifier was provided.
///
/// This is returned if a SETTINGS or PING frame is received with a stream
/// identifier other than zero.
InvalidStreamId,
/// A request or response is malformed.
MalformedMessage,
/// An invalid stream dependency ID was provided
///
/// This is returned if a HEADERS or PRIORITY frame is received with an
/// invalid stream identifier.
InvalidDependencyId,
/// Failed to perform HPACK decoding
Hpack(hpack::DecoderError),
}