use std::io;
use std::fmt;
use std::convert::From;
use std::error::Error;
use hpack::decoder::DecoderError;
pub mod frame;
pub mod transport;
pub mod connection;
pub mod session;
pub mod priority;
pub mod client;
pub mod server;
pub type StreamId = u32;
pub type Header = (Vec<u8>, Vec<u8>);
pub const ALPN_PROTOCOLS: &'static [&'static [u8]] = &[
b"h2",
b"h2-16",
b"h2-15",
b"h2-14",
];
#[derive(Debug)]
pub enum HttpError {
IoError(io::Error),
InvalidFrame,
CompressionError(DecoderError),
UnknownStreamId,
UnableToConnect,
MalformedResponse,
Other(Box<Error + Send + Sync>),
}
impl From<io::Error> for HttpError {
fn from(err: io::Error) -> HttpError {
HttpError::IoError(err)
}
}
impl fmt::Display for HttpError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "HTTP/2 Error: {}", self.description())
}
}
impl Error for HttpError {
fn description(&self) -> &str {
match *self {
HttpError::IoError(_) => "Encountered an IO error",
HttpError::InvalidFrame => "Encountered an invalid HTTP/2 frame",
HttpError::CompressionError(_) => "Encountered an error with HPACK compression",
HttpError::UnknownStreamId => "Attempted an operation with an unknown HTTP/2 stream ID",
HttpError::UnableToConnect => "An error attempting to establish an HTTP/2 connection",
HttpError::MalformedResponse => "The received response was malformed",
HttpError::Other(_) => "An unknown error",
}
}
fn cause(&self) -> Option<&Error> {
match *self {
HttpError::Other(ref e) => Some(&**e),
HttpError::IoError(ref e) => Some(e),
_ => None,
}
}
}
#[cfg(test)]
impl PartialEq for HttpError {
fn eq(&self, other: &HttpError) -> bool {
match (self, other) {
(&HttpError::IoError(ref e1), &HttpError::IoError(ref e2)) => {
e1.kind() == e2.kind() && e1.description() == e2.description()
},
(&HttpError::InvalidFrame, &HttpError::InvalidFrame) => true,
(&HttpError::CompressionError(ref e1), &HttpError::CompressionError(ref e2)) => {
e1 == e2
},
(&HttpError::UnknownStreamId, &HttpError::UnknownStreamId) => true,
(&HttpError::UnableToConnect, &HttpError::UnableToConnect) => true,
(&HttpError::MalformedResponse, &HttpError::MalformedResponse) => true,
(&HttpError::Other(ref e1), &HttpError::Other(ref e2)) => {
e1.description() == e2.description()
},
_ => false,
}
}
}
pub type HttpResult<T> = Result<T, HttpError>;
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum HttpScheme {
Http,
Https,
}
impl HttpScheme {
#[inline]
pub fn as_bytes(&self) -> &'static [u8] {
match *self {
HttpScheme::Http => b"http",
HttpScheme::Https => b"https",
}
}
}
#[derive(Clone)]
pub struct Response {
pub stream_id: StreamId,
pub headers: Vec<Header>,
pub body: Vec<u8>,
}
impl Response {
pub fn new(stream_id: StreamId, headers: Vec<Header>, body: Vec<u8>)
-> Response {
Response {
stream_id: stream_id,
headers: headers,
body: body,
}
}
pub fn status_code(&self) -> HttpResult<u16> {
if self.headers.len() < 1 {
return Err(HttpError::MalformedResponse)
}
if &self.headers[0].0 != &b":status" {
Err(HttpError::MalformedResponse)
} else {
Ok(try!(Response::parse_status_code(&self.headers[0].1)))
}
}
fn parse_status_code(buf: &[u8]) -> HttpResult<u16> {
if buf.len() != 3 {
return Err(HttpError::MalformedResponse);
}
if buf[0] < b'1' || buf[0] > b'5' {
return Err(HttpError::MalformedResponse);
}
if buf[1] < b'0' || buf[1] > b'9' || buf[2] < b'0' || buf[2] > b'9' {
return Err(HttpError::MalformedResponse);
}
Ok(100 * ((buf[0] - b'0') as u16) +
10 * ((buf[1] - b'0') as u16) +
1 * ((buf[2] - b'0') as u16))
}
}
#[derive(Clone)]
pub struct Request {
pub stream_id: u32,
pub headers: Vec<Header>,
pub body: Vec<u8>,
}
#[cfg(test)]
pub mod tests;