Files
hyper/src/proto/h1/role.rs

1466 lines
53 KiB
Rust

use std::fmt::{self, Write};
use std::mem;
use bytes::{BytesMut, Bytes};
use http::header::{self, Entry, HeaderName, HeaderValue};
use http::{HeaderMap, Method, StatusCode, Version};
use httparse;
use error::Parse;
use headers;
use proto::{BodyLength, MessageHead, RequestLine, RequestHead};
use proto::h1::{Decode, Decoder, Encode, Encoder, Http1Transaction, ParseResult, ParseContext, ParsedMessage, date};
const MAX_HEADERS: usize = 100;
const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific
// There are 2 main roles, Client and Server.
//
// There is 1 modifier, OnUpgrade, which can wrap Client and Server,
// to signal that HTTP upgrades are not supported.
pub(crate) struct Client<T>(T);
pub(crate) struct Server<T>(T);
impl<T> Http1Transaction for Server<T>
where
T: OnUpgrade,
{
type Incoming = RequestLine;
type Outgoing = StatusCode;
fn parse(buf: &mut BytesMut, ctx: ParseContext) -> ParseResult<RequestLine> {
if buf.len() == 0 {
return Ok(None);
}
// Unsafe: both headers_indices and headers are using unitialized memory,
// but we *never* read any of it until after httparse has assigned
// values into it. By not zeroing out the stack memory, this saves
// a good ~5% on pipeline benchmarks.
let mut headers_indices: [HeaderIndices; MAX_HEADERS] = unsafe { mem::uninitialized() };
let (len, subject, version, headers_len) = {
let mut headers: [httparse::Header; MAX_HEADERS] = unsafe { mem::uninitialized() };
trace!("Request.parse([Header; {}], [u8; {}])", headers.len(), buf.len());
let mut req = httparse::Request::new(&mut headers);
match req.parse(&buf)? {
httparse::Status::Complete(len) => {
trace!("Request.parse Complete({})", len);
let method = Method::from_bytes(req.method.unwrap().as_bytes())?;
let path = req.path.unwrap().parse()?;
let subject = RequestLine(method, path);
let version = if req.version.unwrap() == 1 {
Version::HTTP_11
} else {
Version::HTTP_10
};
record_header_indices(buf.as_ref(), &req.headers, &mut headers_indices);
let headers_len = req.headers.len();
(len, subject, version, headers_len)
}
httparse::Status::Partial => return Ok(None),
}
};
let slice = buf.split_to(len).freeze();
// 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)
let mut decoder = None;
let mut expect_continue = false;
let mut keep_alive = version == Version::HTTP_11;
let mut con_len = None;
let mut is_te = false;
let mut is_te_chunked = false;
let mut headers = ctx.cached_headers
.take()
.unwrap_or_else(HeaderMap::new);
headers.reserve(headers_len);
for header in &headers_indices[..headers_len] {
let name = HeaderName::from_bytes(&slice[header.name.0..header.name.1])
.expect("header name already validated");
let val = slice.slice(header.value.0, header.value.1);
// Unsafe: httparse already validated header value
let value = unsafe {
HeaderValue::from_shared_unchecked(val)
};
match name {
header::TRANSFER_ENCODING => {
// 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 respond with 400 Bad Request.
if version == Version::HTTP_10 {
debug!("HTTP/1.0 cannot have Transfer-Encoding header");
return Err(Parse::Header);
}
is_te = true;
if headers::is_chunked_(&value) {
is_te_chunked = true;
decoder = Some(Decoder::chunked());
//debug!("request with transfer-encoding header, but not chunked, bad request");
//return Err(Parse::Header);
}
},
header::CONTENT_LENGTH => {
if is_te {
continue;
}
let len = value.to_str()
.map_err(|_| Parse::Header)
.and_then(|s| s.parse().map_err(|_| Parse::Header))?;
if let Some(prev) = con_len {
if prev != len {
debug!(
"multiple Content-Length headers with different values: [{}, {}]",
prev,
len,
);
return Err(Parse::Header);
}
// we don't need to append this secondary length
continue;
}
con_len = Some(len);
decoder = Some(Decoder::length(len));
},
header::CONNECTION => {
// keep_alive was previously set to default for Version
if keep_alive {
// HTTP/1.1
keep_alive = !headers::connection_close(&value);
} else {
// HTTP/1.0
keep_alive = headers::connection_keep_alive(&value);
}
},
header::EXPECT => {
expect_continue = value.as_bytes() == b"100-continue";
},
_ => (),
}
headers.append(name, value);
}
let decoder = if let Some(decoder) = decoder {
decoder
} else {
if is_te && !is_te_chunked {
debug!("request with transfer-encoding header, but not chunked, bad request");
return Err(Parse::Header);
}
Decoder::length(0)
};
*ctx.req_method = Some(subject.0.clone());
Ok(Some(ParsedMessage {
head: MessageHead {
version,
subject,
headers,
},
decode: Decode::Normal(decoder),
expect_continue,
keep_alive,
}))
}
fn encode(mut msg: Encode<Self::Outgoing>, dst: &mut Vec<u8>) -> ::Result<Encoder> {
trace!("Server::encode body={:?}, method={:?}", msg.body, msg.req_method);
debug_assert!(!msg.title_case_headers, "no server config for title case headers");
// hyper currently doesn't support returning 1xx status codes as a Response
// This is because Service only allows returning a single Response, and
// so if you try to reply with a e.g. 100 Continue, you have no way of
// replying with the latter status code response.
let (ret, mut is_last) = if StatusCode::SWITCHING_PROTOCOLS == msg.head.subject {
(T::on_encode_upgrade(&mut msg), true)
} else if msg.head.subject.is_informational() {
error!("response with 1xx status code not supported");
*msg.head = MessageHead::default();
msg.head.subject = StatusCode::INTERNAL_SERVER_ERROR;
msg.body = None;
//TODO: change this to a more descriptive error than just a parse error
(Err(::Error::new_status()), true)
} else {
(Ok(()), !msg.keep_alive)
};
// In some error cases, we don't know about the invalid message until already
// pushing some bytes onto the `dst`. In those cases, we don't want to send
// the half-pushed message, so rewind to before.
let orig_len = dst.len();
let rewind = |dst: &mut Vec<u8>| {
dst.truncate(orig_len);
};
let init_cap = 30 + msg.head.headers.len() * AVERAGE_HEADER_SIZE;
dst.reserve(init_cap);
if msg.head.version == Version::HTTP_11 && msg.head.subject == StatusCode::OK {
extend(dst, b"HTTP/1.1 200 OK\r\n");
} else {
match msg.head.version {
Version::HTTP_10 => extend(dst, b"HTTP/1.0 "),
Version::HTTP_11 => extend(dst, b"HTTP/1.1 "),
_ => unreachable!(),
}
extend(dst, msg.head.subject.as_str().as_bytes());
extend(dst, b" ");
// a reason MUST be written, as many parsers will expect it.
extend(dst, msg.head.subject.canonical_reason().unwrap_or("<none>").as_bytes());
extend(dst, b"\r\n");
}
let mut encoder = Encoder::length(0);
let mut wrote_len = false;
let mut wrote_date = false;
'headers: for (name, mut values) in msg.head.headers.drain() {
match name {
header::CONTENT_LENGTH => {
if wrote_len {
warn!("transfer-encoding and content-length both found, canceling");
rewind(dst);
return Err(::Error::new_header());
}
match msg.body {
Some(BodyLength::Known(known_len)) => {
// The Payload claims to know a length, and
// the headers are already set. For performance
// reasons, we are just going to trust that
// the values match.
//
// In debug builds, we'll assert they are the
// same to help developers find bugs.
encoder = Encoder::length(known_len);
#[cfg(debug_assertions)]
{
let mut folded = None::<(u64, HeaderValue)>;
for value in values {
if let Some(len) = headers::content_length_parse(&value) {
if let Some(fold) = folded {
if fold.0 != len {
panic!("multiple Content-Length values found: [{}, {}]", fold.0, len);
}
folded = Some(fold);
} else {
folded = Some((len, value));
}
} else {
panic!("illegal Content-Length value: {:?}", value);
}
}
if let Some((len, value)) = folded {
assert!(
len == known_len,
"payload claims content-length of {}, custom content-length header claims {}",
known_len,
len,
);
extend(dst, b"content-length: ");
extend(dst, value.as_bytes());
extend(dst, b"\r\n");
wrote_len = true;
continue 'headers;
} else {
// No values in content-length... ignore?
continue 'headers;
}
}
},
Some(BodyLength::Unknown) => {
// The Payload impl didn't know how long the
// body is, but a length header was included.
// We have to parse the value to return our
// Encoder...
let mut folded = None::<(u64, HeaderValue)>;
for value in values {
if let Some(len) = headers::content_length_parse(&value) {
if let Some(fold) = folded {
if fold.0 != len {
warn!("multiple Content-Length values found: [{}, {}]", fold.0, len);
rewind(dst);
return Err(::Error::new_header());
}
folded = Some(fold);
} else {
folded = Some((len, value));
}
} else {
warn!("illegal Content-Length value: {:?}", value);
rewind(dst);
return Err(::Error::new_header());
}
}
if let Some((len, value)) = folded {
encoder = Encoder::length(len);
extend(dst, b"content-length: ");
extend(dst, value.as_bytes());
extend(dst, b"\r\n");
wrote_len = true;
continue 'headers;
} else {
// No values in content-length... ignore?
continue 'headers;
}
},
None => {
// We have no body to actually send,
// but the headers claim a content-length.
// There's only 2 ways this makes sense:
//
// - The header says the length is `0`.
// - This is a response to a `HEAD` request.
if msg.req_method == &Some(Method::HEAD) {
debug_assert_eq!(encoder, Encoder::length(0));
} else {
for value in values {
if value.as_bytes() != b"0" {
warn!("content-length value found, but empty body provided: {:?}", value);
}
}
continue 'headers;
}
}
}
wrote_len = true;
},
header::TRANSFER_ENCODING => {
if wrote_len {
warn!("transfer-encoding and content-length both found, canceling");
rewind(dst);
return Err(::Error::new_header());
}
// check that we actually can send a chunked body...
if msg.head.version == Version::HTTP_10 || !Server::can_chunked(msg.req_method, msg.head.subject) {
continue;
}
wrote_len = true;
encoder = Encoder::chunked();
extend(dst, b"transfer-encoding: ");
let mut saw_chunked;
if let Some(te) = values.next() {
extend(dst, te.as_bytes());
saw_chunked = headers::is_chunked_(&te);
for value in values {
extend(dst, b", ");
extend(dst, value.as_bytes());
saw_chunked = headers::is_chunked_(&value);
}
if !saw_chunked {
extend(dst, b", chunked\r\n");
} else {
extend(dst, b"\r\n");
}
} else {
// zero lines? add a chunked line then
extend(dst, b"chunked\r\n");
}
continue 'headers;
},
header::CONNECTION => {
if !is_last {
for value in values {
extend(dst, name.as_str().as_bytes());
extend(dst, b": ");
extend(dst, value.as_bytes());
extend(dst, b"\r\n");
if headers::connection_close(&value) {
is_last = true;
}
}
continue 'headers;
}
},
header::DATE => {
wrote_date = true;
},
_ => (),
}
//TODO: this should perhaps instead combine them into
//single lines, as RFC7230 suggests is preferable.
for value in values {
extend(dst, name.as_str().as_bytes());
extend(dst, b": ");
extend(dst, value.as_bytes());
extend(dst, b"\r\n");
}
}
if !wrote_len {
encoder = match msg.body {
Some(BodyLength::Unknown) => {
if msg.head.version == Version::HTTP_10 || !Server::can_chunked(msg.req_method, msg.head.subject) {
Encoder::close_delimited()
} else {
extend(dst, b"transfer-encoding: chunked\r\n");
Encoder::chunked()
}
},
None |
Some(BodyLength::Known(0)) => {
extend(dst, b"content-length: 0\r\n");
Encoder::length(0)
},
Some(BodyLength::Known(len)) => {
let _ = write!(FastWrite(dst), "content-length: {}\r\n", len);
Encoder::length(len)
},
};
}
// cached date is much faster than formatting every request
if !wrote_date {
dst.reserve(date::DATE_VALUE_LENGTH + 8);
extend(dst, b"date: ");
date::extend(dst);
extend(dst, b"\r\n\r\n");
} else {
extend(dst, b"\r\n");
}
ret.map(|()| encoder.set_last(is_last))
}
fn on_error(err: &::Error) -> Option<MessageHead<Self::Outgoing>> {
use ::error::{Kind, Parse};
let status = match *err.kind() {
Kind::Parse(Parse::Method) |
Kind::Parse(Parse::Header) |
Kind::Parse(Parse::Uri) |
Kind::Parse(Parse::Version) => {
StatusCode::BAD_REQUEST
},
Kind::Parse(Parse::TooLarge) => {
StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE
},
_ => return None,
};
debug!("sending automatic response ({}) for parse error", status);
let mut msg = MessageHead::default();
msg.subject = status;
Some(msg)
}
fn should_error_on_parse_eof() -> bool {
false
}
fn should_read_first() -> bool {
true
}
fn update_date() {
date::update();
}
}
impl Server<()> {
/*
fn set_length(head: &mut MessageHead<StatusCode>, body: Option<BodyLength>, method: Option<&Method>) -> Encoder {
// these are here thanks to borrowck
// `if method == Some(&Method::Get)` says the RHS doesn't 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::SWITCHING_PROTOCOLS |
StatusCode::NO_CONTENT |
StatusCode::NOT_MODIFIED => false,
_ => true,
}
}
};
if let (Some(body), true) = (body, can_have_body) {
set_length(&mut head.headers, body, head.version == Version::HTTP_11)
} else {
head.headers.remove(header::TRANSFER_ENCODING);
if can_have_body {
headers::content_length_zero(&mut head.headers);
}
Encoder::length(0)
}
}
*/
fn can_chunked(method: &Option<Method>, status: StatusCode) -> bool {
if method == &Some(Method::HEAD) {
false
} else if method == &Some(Method::CONNECT) && status.is_success() {
false
} else {
match status {
// TODO: support for 1xx codes needs improvement everywhere
// would be 100...199 => false
StatusCode::SWITCHING_PROTOCOLS |
StatusCode::NO_CONTENT |
StatusCode::NOT_MODIFIED => false,
_ => true,
}
}
}
}
impl<T> Http1Transaction for Client<T>
where
T: OnUpgrade,
{
type Incoming = StatusCode;
type Outgoing = RequestLine;
fn parse(buf: &mut BytesMut, ctx: ParseContext) -> ParseResult<StatusCode> {
if buf.len() == 0 {
return Ok(None);
}
// Unsafe: see comment in Server Http1Transaction, above.
let mut headers_indices: [HeaderIndices; MAX_HEADERS] = unsafe { mem::uninitialized() };
let (len, status, version, headers_len) = {
let mut headers: [httparse::Header; MAX_HEADERS] = unsafe { mem::uninitialized() };
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 status = StatusCode::from_u16(res.code.unwrap())?;
let version = if res.version.unwrap() == 1 {
Version::HTTP_11
} else {
Version::HTTP_10
};
record_header_indices(bytes, &res.headers, &mut headers_indices);
let headers_len = res.headers.len();
(len, status, version, headers_len)
},
httparse::Status::Partial => return Ok(None),
}
};
let slice = buf.split_to(len).freeze();
let mut headers = ctx.cached_headers
.take()
.unwrap_or_else(HeaderMap::new);
headers.reserve(headers_len);
fill_headers(&mut headers, slice, &headers_indices[..headers_len]);
let keep_alive = version == Version::HTTP_11;
let head = MessageHead {
version,
subject: status,
headers,
};
let decode = Client::<T>::decoder(&head, ctx.req_method)?;
Ok(Some(ParsedMessage {
head,
decode,
expect_continue: false,
keep_alive,
}))
}
fn encode(msg: Encode<Self::Outgoing>, dst: &mut Vec<u8>) -> ::Result<Encoder> {
trace!("Client::encode body={:?}, method={:?}", msg.body, msg.req_method);
*msg.req_method = Some(msg.head.subject.0.clone());
let body = Client::set_length(msg.head, msg.body);
let init_cap = 30 + msg.head.headers.len() * AVERAGE_HEADER_SIZE;
dst.reserve(init_cap);
extend(dst, msg.head.subject.0.as_str().as_bytes());
extend(dst, b" ");
//TODO: add API to http::Uri to encode without std::fmt
let _ = write!(FastWrite(dst), "{} ", msg.head.subject.1);
match msg.head.version {
Version::HTTP_10 => extend(dst, b"HTTP/1.0"),
Version::HTTP_11 => extend(dst, b"HTTP/1.1"),
_ => unreachable!(),
}
extend(dst, b"\r\n");
if msg.title_case_headers {
write_headers_title_case(&msg.head.headers, dst);
} else {
write_headers(&msg.head.headers, dst);
}
extend(dst, b"\r\n");
msg.head.headers.clear(); //TODO: remove when switching to drain()
Ok(body)
}
fn on_error(_err: &::Error) -> Option<MessageHead<Self::Outgoing>> {
// we can't tell the server about any errors it creates
None
}
fn should_error_on_parse_eof() -> bool {
true
}
fn should_read_first() -> bool {
false
}
}
impl<T: OnUpgrade> Client<T> {
fn decoder(inc: &MessageHead<StatusCode>, method: &mut Option<Method>) -> Result<Decode, Parse> {
// 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 inc.subject.as_u16() {
101 => {
return T::on_decode_upgrade().map(Decode::Final);
},
100...199 => {
trace!("ignoring informational response: {}", inc.subject.as_u16());
return Ok(Decode::Ignore);
},
204 |
304 => return Ok(Decode::Normal(Decoder::length(0))),
_ => (),
}
match *method {
Some(Method::HEAD) => {
return Ok(Decode::Normal(Decoder::length(0)));
}
Some(Method::CONNECT) => match inc.subject.as_u16() {
200...299 => {
return Ok(Decode::Final(Decoder::length(0)));
},
_ => {},
},
Some(_) => {},
None => {
trace!("Client::decoder is missing the Method");
}
}
if inc.headers.contains_key(header::TRANSFER_ENCODING) {
// 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 respond with 400 Bad Request.
if inc.version == Version::HTTP_10 {
debug!("HTTP/1.0 cannot have Transfer-Encoding header");
Err(Parse::Header)
} else if headers::transfer_encoding_is_chunked(&inc.headers) {
Ok(Decode::Normal(Decoder::chunked()))
} else {
trace!("not chunked, read till eof");
Ok(Decode::Normal(Decoder::eof()))
}
} else if let Some(len) = headers::content_length_parse_all(&inc.headers) {
Ok(Decode::Normal(Decoder::length(len)))
} else if inc.headers.contains_key(header::CONTENT_LENGTH) {
debug!("illegal Content-Length header");
Err(Parse::Header)
} else {
trace!("neither Transfer-Encoding nor Content-Length");
Ok(Decode::Normal(Decoder::eof()))
}
}
}
impl Client<()> {
fn set_length(head: &mut RequestHead, body: Option<BodyLength>) -> Encoder {
if let Some(body) = body {
let can_chunked = head.version == Version::HTTP_11
&& (head.subject.0 != Method::HEAD)
&& (head.subject.0 != Method::GET)
&& (head.subject.0 != Method::CONNECT);
set_length(&mut head.headers, body, can_chunked)
} else {
head.headers.remove(header::TRANSFER_ENCODING);
Encoder::length(0)
}
}
}
fn set_length(headers: &mut HeaderMap, body: BodyLength, can_chunked: bool) -> Encoder {
// If the user already set specific headers, we should respect them, regardless
// of what the Payload knows about itself. They set them for a reason.
// Because of the borrow checker, we can't check the for an existing
// Content-Length header while holding an `Entry` for the Transfer-Encoding
// header, so unfortunately, we must do the check here, first.
let existing_con_len = headers::content_length_parse_all(headers);
let mut should_remove_con_len = false;
if can_chunked {
// If the user set a transfer-encoding, respect that. Let's just
// make sure `chunked` is the final encoding.
let encoder = match headers.entry(header::TRANSFER_ENCODING)
.expect("TRANSFER_ENCODING is valid HeaderName") {
Entry::Occupied(te) => {
should_remove_con_len = true;
if headers::is_chunked(te.iter()) {
Some(Encoder::chunked())
} else {
warn!("user provided transfer-encoding does not end in 'chunked'");
// There's a Transfer-Encoding, but it doesn't end in 'chunked'!
// An example that could trigger this:
//
// Transfer-Encoding: gzip
//
// This can be bad, depending on if this is a request or a
// response.
//
// - A request is illegal if there is a `Transfer-Encoding`
// but it doesn't end in `chunked`.
// - A response that has `Transfer-Encoding` but doesn't
// end in `chunked` isn't illegal, it just forces this
// to be close-delimited.
//
// We can try to repair this, by adding `chunked` ourselves.
headers::add_chunked(te);
Some(Encoder::chunked())
}
},
Entry::Vacant(te) => {
if let Some(len) = existing_con_len {
Some(Encoder::length(len))
} else if let BodyLength::Unknown = body {
should_remove_con_len = true;
te.insert(HeaderValue::from_static("chunked"));
Some(Encoder::chunked())
} else {
None
}
},
};
// This is because we need a second mutable borrow to remove
// content-length header.
if let Some(encoder) = encoder {
if should_remove_con_len && existing_con_len.is_some() {
headers.remove(header::CONTENT_LENGTH);
}
return encoder;
}
// User didn't set transfer-encoding, AND we know body length,
// so we can just set the Content-Length automatically.
let len = if let BodyLength::Known(len) = body {
len
} else {
unreachable!("BodyLength::Unknown would set chunked");
};
set_content_length(headers, len)
} else {
// Chunked isn't legal, so if it is set, we need to remove it.
// Also, if it *is* set, then we shouldn't replace with a length,
// since the user tried to imply there isn't a length.
let encoder = if headers.remove(header::TRANSFER_ENCODING).is_some() {
trace!("removing illegal transfer-encoding header");
should_remove_con_len = true;
Encoder::close_delimited()
} else if let Some(len) = existing_con_len {
Encoder::length(len)
} else if let BodyLength::Known(len) = body {
set_content_length(headers, len)
} else {
Encoder::close_delimited()
};
if should_remove_con_len && existing_con_len.is_some() {
headers.remove(header::CONTENT_LENGTH);
}
encoder
}
}
fn set_content_length(headers: &mut HeaderMap, len: u64) -> Encoder {
// At this point, there should not be a valid Content-Length
// header. However, since we'll be indexing in anyways, we can
// warn the user if there was an existing illegal header.
//
// Or at least, we can in theory. It's actually a little bit slower,
// so perhaps only do that while the user is developing/testing.
if cfg!(debug_assertions) {
match headers.entry(header::CONTENT_LENGTH)
.expect("CONTENT_LENGTH is valid HeaderName") {
Entry::Occupied(mut cl) => {
// Internal sanity check, we should have already determined
// that the header was illegal before calling this function.
debug_assert!(headers::content_length_parse_all_values(cl.iter()).is_none());
// Uh oh, the user set `Content-Length` headers, but set bad ones.
// This would be an illegal message anyways, so let's try to repair
// with our known good length.
error!("user provided content-length header was invalid");
cl.insert(headers::content_length_value(len));
Encoder::length(len)
},
Entry::Vacant(cl) => {
cl.insert(headers::content_length_value(len));
Encoder::length(len)
}
}
} else {
headers.insert(header::CONTENT_LENGTH, headers::content_length_value(len));
Encoder::length(len)
}
}
pub(crate) trait OnUpgrade {
fn on_encode_upgrade(msg: &mut Encode<StatusCode>) -> ::Result<()>;
fn on_decode_upgrade() -> Result<Decoder, Parse>;
}
pub(crate) enum YesUpgrades {}
pub(crate) enum NoUpgrades {}
impl OnUpgrade for YesUpgrades {
fn on_encode_upgrade(_: &mut Encode<StatusCode>) -> ::Result<()> {
Ok(())
}
fn on_decode_upgrade() -> Result<Decoder, Parse> {
debug!("101 response received, upgrading");
// 101 upgrades always have no body
Ok(Decoder::length(0))
}
}
impl OnUpgrade for NoUpgrades {
fn on_encode_upgrade(msg: &mut Encode<StatusCode>) -> ::Result<()> {
error!("response with 101 status code not supported");
*msg.head = MessageHead::default();
msg.head.subject = ::StatusCode::INTERNAL_SERVER_ERROR;
msg.body = None;
//TODO: replace with more descriptive error
Err(::Error::new_status())
}
fn on_decode_upgrade() -> Result<Decoder, Parse> {
debug!("received 101 upgrade response, not supported");
Err(Parse::UpgradeNotSupported)
}
}
#[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);
}
}
fn fill_headers(headers: &mut HeaderMap, slice: Bytes, indices: &[HeaderIndices]) {
for header in indices {
let name = HeaderName::from_bytes(&slice[header.name.0..header.name.1])
.expect("header name already validated");
let value = unsafe {
HeaderValue::from_shared_unchecked(
slice.slice(header.value.0, header.value.1)
)
};
headers.append(name, value);
}
}
// Write header names as title case. The header name is assumed to be ASCII,
// therefore it is trivial to convert an ASCII character from lowercase to
// uppercase. It is as simple as XORing the lowercase character byte with
// space.
fn title_case(dst: &mut Vec<u8>, name: &[u8]) {
dst.reserve(name.len());
let mut iter = name.iter();
// Uppercase the first character
if let Some(c) = iter.next() {
if *c >= b'a' && *c <= b'z' {
dst.push(*c ^ b' ');
}
}
while let Some(c) = iter.next() {
dst.push(*c);
if *c == b'-' {
if let Some(c) = iter.next() {
if *c >= b'a' && *c <= b'z' {
dst.push(*c ^ b' ');
}
}
}
}
}
fn write_headers_title_case(headers: &HeaderMap, dst: &mut Vec<u8>) {
for (name, value) in headers {
title_case(dst, name.as_str().as_bytes());
extend(dst, b": ");
extend(dst, value.as_bytes());
extend(dst, b"\r\n");
}
}
fn write_headers(headers: &HeaderMap, dst: &mut Vec<u8>) {
for (name, value) in headers {
extend(dst, name.as_str().as_bytes());
extend(dst, b": ");
extend(dst, value.as_bytes());
extend(dst, b"\r\n");
}
}
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 super::*;
use super::{Server as S, Client as C};
type Server = S<NoUpgrades>;
type Client = C<NoUpgrades>;
#[test]
fn test_parse_request() {
extern crate pretty_env_logger;
let _ = pretty_env_logger::try_init();
let mut raw = BytesMut::from(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n".to_vec());
let mut method = None;
let msg = Server::parse(&mut raw, ParseContext {
cached_headers: &mut None,
req_method: &mut method,
}).unwrap().unwrap();
assert_eq!(raw.len(), 0);
assert_eq!(msg.head.subject.0, ::Method::GET);
assert_eq!(msg.head.subject.1, "/echo");
assert_eq!(msg.head.version, ::Version::HTTP_11);
assert_eq!(msg.head.headers.len(), 1);
assert_eq!(msg.head.headers["Host"], "hyper.rs");
assert_eq!(method, Some(::Method::GET));
}
#[test]
fn test_parse_response() {
extern crate pretty_env_logger;
let _ = pretty_env_logger::try_init();
let mut raw = BytesMut::from(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n".to_vec());
let ctx = ParseContext {
cached_headers: &mut None,
req_method: &mut Some(::Method::GET),
};
let msg = Client::parse(&mut raw, ctx).unwrap().unwrap();
assert_eq!(raw.len(), 0);
assert_eq!(msg.head.subject, ::StatusCode::OK);
assert_eq!(msg.head.version, ::Version::HTTP_11);
assert_eq!(msg.head.headers.len(), 1);
assert_eq!(msg.head.headers["Content-Length"], "0");
}
#[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());
let ctx = ParseContext {
cached_headers: &mut None,
req_method: &mut None,
};
Server::parse(&mut raw, ctx).unwrap_err();
}
#[test]
fn test_decoder_request() {
use super::Decoder;
fn parse(s: &str) -> ParsedMessage<RequestLine> {
let mut bytes = BytesMut::from(s);
Server::parse(&mut bytes, ParseContext {
cached_headers: &mut None,
req_method: &mut None,
})
.expect("parse ok")
.expect("parse complete")
}
fn parse_err(s: &str, comment: &str) -> ::error::Parse {
let mut bytes = BytesMut::from(s);
Server::parse(&mut bytes, ParseContext {
cached_headers: &mut None,
req_method: &mut None,
})
.expect_err(comment)
}
// no length or transfer-encoding means 0-length body
assert_eq!(parse("\
GET / HTTP/1.1\r\n\
\r\n\
").decode, Decode::Normal(Decoder::length(0)));
assert_eq!(parse("\
POST / HTTP/1.1\r\n\
\r\n\
").decode, Decode::Normal(Decoder::length(0)));
// transfer-encoding: chunked
assert_eq!(parse("\
POST / HTTP/1.1\r\n\
transfer-encoding: chunked\r\n\
\r\n\
").decode, Decode::Normal(Decoder::chunked()));
assert_eq!(parse("\
POST / HTTP/1.1\r\n\
transfer-encoding: gzip, chunked\r\n\
\r\n\
").decode, Decode::Normal(Decoder::chunked()));
assert_eq!(parse("\
POST / HTTP/1.1\r\n\
transfer-encoding: gzip\r\n\
transfer-encoding: chunked\r\n\
\r\n\
").decode, Decode::Normal(Decoder::chunked()));
// content-length
assert_eq!(parse("\
POST / HTTP/1.1\r\n\
content-length: 10\r\n\
\r\n\
").decode, Decode::Normal(Decoder::length(10)));
// transfer-encoding and content-length = chunked
assert_eq!(parse("\
POST / HTTP/1.1\r\n\
content-length: 10\r\n\
transfer-encoding: chunked\r\n\
\r\n\
").decode, Decode::Normal(Decoder::chunked()));
assert_eq!(parse("\
POST / HTTP/1.1\r\n\
transfer-encoding: chunked\r\n\
content-length: 10\r\n\
\r\n\
").decode, Decode::Normal(Decoder::chunked()));
assert_eq!(parse("\
POST / HTTP/1.1\r\n\
transfer-encoding: gzip\r\n\
content-length: 10\r\n\
transfer-encoding: chunked\r\n\
\r\n\
").decode, Decode::Normal(Decoder::chunked()));
// multiple content-lengths of same value are fine
assert_eq!(parse("\
POST / HTTP/1.1\r\n\
content-length: 10\r\n\
content-length: 10\r\n\
\r\n\
").decode, Decode::Normal(Decoder::length(10)));
// multiple content-lengths with different values is an error
parse_err("\
POST / HTTP/1.1\r\n\
content-length: 10\r\n\
content-length: 11\r\n\
\r\n\
", "multiple content-lengths");
// transfer-encoding that isn't chunked is an error
parse_err("\
POST / HTTP/1.1\r\n\
transfer-encoding: gzip\r\n\
\r\n\
", "transfer-encoding but not chunked");
parse_err("\
POST / HTTP/1.1\r\n\
transfer-encoding: chunked, gzip\r\n\
\r\n\
", "transfer-encoding doesn't end in chunked");
// http/1.0
assert_eq!(parse("\
POST / HTTP/1.0\r\n\
content-length: 10\r\n\
\r\n\
").decode, Decode::Normal(Decoder::length(10)));
// 1.0 doesn't understand chunked, so its an error
parse_err("\
POST / HTTP/1.0\r\n\
transfer-encoding: chunked\r\n\
\r\n\
", "1.0 chunked");
}
#[test]
fn test_decoder_response() {
fn parse(s: &str) -> ParsedMessage<StatusCode> {
parse_with_method(s, Method::GET)
}
fn parse_with_method(s: &str, m: Method) -> ParsedMessage<StatusCode> {
let mut bytes = BytesMut::from(s);
Client::parse(&mut bytes, ParseContext {
cached_headers: &mut None,
req_method: &mut Some(m),
})
.expect("parse ok")
.expect("parse complete")
}
fn parse_err(s: &str) -> ::error::Parse {
let mut bytes = BytesMut::from(s);
Client::parse(&mut bytes, ParseContext {
cached_headers: &mut None,
req_method: &mut Some(Method::GET),
})
.expect_err("parse should err")
}
// no content-length or transfer-encoding means close-delimited
assert_eq!(parse("\
HTTP/1.1 200 OK\r\n\
\r\n\
").decode, Decode::Normal(Decoder::eof()));
// 204 and 304 never have a body
assert_eq!(parse("\
HTTP/1.1 204 No Content\r\n\
\r\n\
").decode, Decode::Normal(Decoder::length(0)));
assert_eq!(parse("\
HTTP/1.1 304 Not Modified\r\n\
\r\n\
").decode, Decode::Normal(Decoder::length(0)));
// content-length
assert_eq!(parse("\
HTTP/1.1 200 OK\r\n\
content-length: 8\r\n\
\r\n\
").decode, Decode::Normal(Decoder::length(8)));
assert_eq!(parse("\
HTTP/1.1 200 OK\r\n\
content-length: 8\r\n\
content-length: 8\r\n\
\r\n\
").decode, Decode::Normal(Decoder::length(8)));
parse_err("\
HTTP/1.1 200 OK\r\n\
content-length: 8\r\n\
content-length: 9\r\n\
\r\n\
");
// transfer-encoding
assert_eq!(parse("\
HTTP/1.1 200 OK\r\n\
transfer-encoding: chunked\r\n\
\r\n\
").decode, Decode::Normal(Decoder::chunked()));
// transfer-encoding and content-length = chunked
assert_eq!(parse("\
HTTP/1.1 200 OK\r\n\
content-length: 10\r\n\
transfer-encoding: chunked\r\n\
\r\n\
").decode, Decode::Normal(Decoder::chunked()));
// HEAD can have content-length, but not body
assert_eq!(parse_with_method("\
HTTP/1.1 200 OK\r\n\
content-length: 8\r\n\
\r\n\
", Method::HEAD).decode, Decode::Normal(Decoder::length(0)));
// CONNECT with 200 never has body
assert_eq!(parse_with_method("\
HTTP/1.1 200 OK\r\n\
\r\n\
", Method::CONNECT).decode, Decode::Final(Decoder::length(0)));
// CONNECT receiving non 200 can have a body
assert_eq!(parse_with_method("\
HTTP/1.1 400 Bad Request\r\n\
\r\n\
", Method::CONNECT).decode, Decode::Normal(Decoder::eof()));
// 1xx status codes
assert_eq!(parse("\
HTTP/1.1 100 Continue\r\n\
\r\n\
").decode, Decode::Ignore);
assert_eq!(parse("\
HTTP/1.1 103 Early Hints\r\n\
\r\n\
").decode, Decode::Ignore);
// 101 upgrade not supported yet
parse_err("\
HTTP/1.1 101 Switching Protocols\r\n\
\r\n\
");
// http/1.0
assert_eq!(parse("\
HTTP/1.0 200 OK\r\n\
\r\n\
").decode, Decode::Normal(Decoder::eof()));
// 1.0 doesn't understand chunked
parse_err("\
HTTP/1.0 200 OK\r\n\
transfer-encoding: chunked\r\n\
\r\n\
");
}
#[test]
fn test_client_request_encode_title_case() {
use http::header::HeaderValue;
use proto::BodyLength;
let mut head = MessageHead::default();
head.headers.insert("content-length", HeaderValue::from_static("10"));
head.headers.insert("content-type", HeaderValue::from_static("application/json"));
let mut vec = Vec::new();
Client::encode(Encode {
head: &mut head,
body: Some(BodyLength::Known(10)),
keep_alive: true,
req_method: &mut None,
title_case_headers: true,
}, &mut vec).unwrap();
assert_eq!(vec, b"GET / HTTP/1.1\r\nContent-Length: 10\r\nContent-Type: application/json\r\n\r\n".to_vec());
}
#[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();
let mut headers = Some(HeaderMap::new());
b.bytes = len as u64;
b.iter(|| {
let msg = Server::parse(&mut raw, ParseContext {
cached_headers: &mut headers,
req_method: &mut None,
}).unwrap().unwrap();
headers = Some(msg.head.headers);
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_parse_short(b: &mut Bencher) {
let mut raw = BytesMut::from(
b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n".to_vec()
);
let len = raw.len();
let mut headers = Some(HeaderMap::new());
b.bytes = len as u64;
b.iter(|| {
let msg = Server::parse(&mut raw, ParseContext {
cached_headers: &mut headers,
req_method: &mut None,
}).unwrap().unwrap();
headers = Some(msg.head.headers);
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_encode_headers_preset(b: &mut Bencher) {
use http::header::HeaderValue;
use proto::BodyLength;
let len = 108;
b.bytes = len as u64;
let mut head = MessageHead::default();
let mut headers = HeaderMap::new();
headers.insert("content-length", HeaderValue::from_static("10"));
headers.insert("content-type", HeaderValue::from_static("application/json"));
b.iter(|| {
let mut vec = Vec::new();
head.headers = headers.clone();
Server::encode(Encode {
head: &mut head,
body: Some(BodyLength::Known(10)),
keep_alive: true,
req_method: &mut Some(Method::GET),
title_case_headers: false,
}, &mut vec).unwrap();
assert_eq!(vec.len(), len);
::test::black_box(vec);
})
}
#[cfg(feature = "nightly")]
#[bench]
fn bench_server_encode_no_headers(b: &mut Bencher) {
use proto::BodyLength;
let len = 76;
b.bytes = len as u64;
let mut head = MessageHead::default();
b.iter(|| {
let mut vec = Vec::new();
Server::encode(Encode {
head: &mut head,
body: Some(BodyLength::Known(10)),
keep_alive: true,
req_method: &mut Some(Method::GET),
title_case_headers: false,
}, &mut vec).unwrap();
assert_eq!(vec.len(), len);
::test::black_box(vec);
})
}
}