mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-30 18:34:36 +01:00
use custom headers map; more optimizations
This commit is contained in:
parent
4ef46e26f9
commit
748289f0ff
@ -344,7 +344,7 @@ impl Responder for NamedFile {
|
||||
// check last modified
|
||||
let not_modified = if !none_match(etag.as_ref(), req) {
|
||||
true
|
||||
} else if req.headers().contains_key(header::IF_NONE_MATCH) {
|
||||
} else if req.headers().contains_key(&header::IF_NONE_MATCH) {
|
||||
false
|
||||
} else if let (Some(ref m), Some(header::IfModifiedSince(ref since))) =
|
||||
(last_modified, req.get_header())
|
||||
@ -378,7 +378,7 @@ impl Responder for NamedFile {
|
||||
let mut offset = 0;
|
||||
|
||||
// check for range header
|
||||
if let Some(ranges) = req.headers().get(header::RANGE) {
|
||||
if let Some(ranges) = req.headers().get(&header::RANGE) {
|
||||
if let Ok(rangesheader) = ranges.to_str() {
|
||||
if let Ok(rangesvec) = HttpRange::parse(rangesheader, length) {
|
||||
length = rangesvec[0].length;
|
||||
|
@ -60,6 +60,7 @@ bitflags = "1.0"
|
||||
bytes = "0.4"
|
||||
byteorder = "1.2"
|
||||
derive_more = "0.14"
|
||||
either = "1.5.2"
|
||||
encoding = "0.2"
|
||||
futures = "0.1"
|
||||
hashbrown = "0.1.8"
|
||||
|
@ -107,7 +107,7 @@ where
|
||||
let mut head = ResponseHead::default();
|
||||
head.version = parts.version;
|
||||
head.status = parts.status;
|
||||
head.headers = parts.headers;
|
||||
head.headers = parts.headers.into();
|
||||
|
||||
Ok((head, payload))
|
||||
})
|
||||
|
@ -55,7 +55,7 @@ where
|
||||
#[inline]
|
||||
pub fn from_headers(stream: S, headers: &HeaderMap) -> Decoder<S> {
|
||||
// check content-encoding
|
||||
let encoding = if let Some(enc) = headers.get(CONTENT_ENCODING) {
|
||||
let encoding = if let Some(enc) = headers.get(&CONTENT_ENCODING) {
|
||||
if let Ok(enc) = enc.to_str() {
|
||||
ContentEncoding::from(enc)
|
||||
} else {
|
||||
|
@ -31,7 +31,7 @@ impl<B: MessageBody> Encoder<B> {
|
||||
head: &mut ResponseHead,
|
||||
body: ResponseBody<B>,
|
||||
) -> ResponseBody<Encoder<B>> {
|
||||
let can_encode = !(head.headers().contains_key(CONTENT_ENCODING)
|
||||
let can_encode = !(head.headers().contains_key(&CONTENT_ENCODING)
|
||||
|| head.status == StatusCode::SWITCHING_PROTOCOLS
|
||||
|| encoding == ContentEncoding::Identity
|
||||
|| encoding == ContentEncoding::Auto);
|
||||
|
@ -22,8 +22,8 @@ use crate::response::Response;
|
||||
bitflags! {
|
||||
struct Flags: u8 {
|
||||
const HEAD = 0b0000_0001;
|
||||
const KEEPALIVE_ENABLED = 0b0000_1000;
|
||||
const STREAM = 0b0001_0000;
|
||||
const KEEPALIVE_ENABLED = 0b0000_0010;
|
||||
const STREAM = 0b0000_0100;
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,8 +39,8 @@ pub struct Codec {
|
||||
|
||||
// encoder part
|
||||
flags: Flags,
|
||||
headers_size: u32,
|
||||
encoder: encoder::MessageEncoder<Response<()>>,
|
||||
// headers_size: u32,
|
||||
}
|
||||
|
||||
impl Default for Codec {
|
||||
@ -73,7 +73,7 @@ impl Codec {
|
||||
ctype: ConnectionType::Close,
|
||||
|
||||
flags,
|
||||
headers_size: 0,
|
||||
// headers_size: 0,
|
||||
encoder: encoder::MessageEncoder::default(),
|
||||
}
|
||||
}
|
||||
@ -159,9 +159,6 @@ impl Encoder for Codec {
|
||||
) -> Result<(), Self::Error> {
|
||||
match item {
|
||||
Message::Item((mut res, length)) => {
|
||||
if res.head().status == StatusCode::CONTINUE {
|
||||
dst.extend_from_slice(b"HTTP/1.1 100 Continue\r\n\r\n");
|
||||
} else {
|
||||
// set response version
|
||||
res.head_mut().version = self.version;
|
||||
|
||||
@ -188,8 +185,7 @@ impl Encoder for Codec {
|
||||
self.ctype,
|
||||
&self.config,
|
||||
)?;
|
||||
self.headers_size = (dst.len() - len) as u32;
|
||||
}
|
||||
// self.headers_size = (dst.len() - len) as u32;
|
||||
}
|
||||
Message::Chunk(Some(bytes)) => {
|
||||
self.encoder.encode_chunk(bytes.as_ref(), dst)?;
|
||||
|
@ -5,11 +5,12 @@ use actix_codec::Decoder;
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use futures::{Async, Poll};
|
||||
use http::header::{HeaderName, HeaderValue};
|
||||
use http::{header, HeaderMap, HttpTryFrom, Method, StatusCode, Uri, Version};
|
||||
use http::{header, HttpTryFrom, Method, StatusCode, Uri, Version};
|
||||
use httparse;
|
||||
use log::{debug, error, trace};
|
||||
|
||||
use crate::error::ParseError;
|
||||
use crate::header::HeaderMap;
|
||||
use crate::message::{ConnectionType, ResponseHead};
|
||||
use crate::request::Request;
|
||||
|
||||
@ -630,6 +631,7 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::error::ParseError;
|
||||
use crate::http::header::{HeaderName, SET_COOKIE};
|
||||
use crate::httpmessage::HttpMessage;
|
||||
|
||||
impl PayloadType {
|
||||
@ -790,7 +792,13 @@ mod tests {
|
||||
assert_eq!(req.version(), Version::HTTP_11);
|
||||
assert_eq!(*req.method(), Method::GET);
|
||||
assert_eq!(req.path(), "/test");
|
||||
assert_eq!(req.headers().get("test").unwrap().as_bytes(), b"value");
|
||||
assert_eq!(
|
||||
req.headers()
|
||||
.get(HeaderName::try_from("test").unwrap())
|
||||
.unwrap()
|
||||
.as_bytes(),
|
||||
b"value"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -805,12 +813,11 @@ mod tests {
|
||||
|
||||
let val: Vec<_> = req
|
||||
.headers()
|
||||
.get_all("Set-Cookie")
|
||||
.iter()
|
||||
.get_all(SET_COOKIE)
|
||||
.map(|v| v.to_str().unwrap().to_owned())
|
||||
.collect();
|
||||
assert_eq!(val[0], "c1=cookie1");
|
||||
assert_eq!(val[1], "c2=cookie2");
|
||||
assert_eq!(val[1], "c1=cookie1");
|
||||
assert_eq!(val[0], "c2=cookie2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -6,15 +6,16 @@ use std::str::FromStr;
|
||||
use std::{cmp, fmt, io, mem};
|
||||
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
use http::header::{
|
||||
HeaderValue, ACCEPT_ENCODING, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING,
|
||||
};
|
||||
use http::{HeaderMap, Method, StatusCode, Version};
|
||||
|
||||
use crate::body::BodySize;
|
||||
use crate::config::ServiceConfig;
|
||||
use crate::header::map;
|
||||
use crate::header::ContentEncoding;
|
||||
use crate::helpers;
|
||||
use crate::http::header::{
|
||||
HeaderValue, ACCEPT_ENCODING, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING,
|
||||
};
|
||||
use crate::http::{HeaderMap, Method, StatusCode, Version};
|
||||
use crate::message::{ConnectionType, Head, RequestHead, ResponseHead};
|
||||
use crate::request::Request;
|
||||
use crate::response::Response;
|
||||
@ -109,7 +110,7 @@ pub(crate) trait MessageType: Sized {
|
||||
let mut has_date = false;
|
||||
let mut remaining = dst.remaining_mut();
|
||||
let mut buf = unsafe { &mut *(dst.bytes_mut() as *mut [u8]) };
|
||||
for (key, value) in self.headers() {
|
||||
for (key, value) in self.headers().inner.iter() {
|
||||
match *key {
|
||||
CONNECTION => continue,
|
||||
TRANSFER_ENCODING | CONTENT_LENGTH if skip_len => continue,
|
||||
@ -118,9 +119,10 @@ pub(crate) trait MessageType: Sized {
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let v = value.as_ref();
|
||||
let k = key.as_str().as_bytes();
|
||||
match value {
|
||||
map::Value::One(ref val) => {
|
||||
let v = val.as_ref();
|
||||
let len = k.len() + v.len() + 4;
|
||||
if len > remaining {
|
||||
unsafe {
|
||||
@ -133,7 +135,6 @@ pub(crate) trait MessageType: Sized {
|
||||
buf = &mut *(dst.bytes_mut() as *mut _);
|
||||
}
|
||||
}
|
||||
|
||||
buf[pos..pos + k.len()].copy_from_slice(k);
|
||||
pos += k.len();
|
||||
buf[pos..pos + 2].copy_from_slice(b": ");
|
||||
@ -144,6 +145,34 @@ pub(crate) trait MessageType: Sized {
|
||||
pos += 2;
|
||||
remaining -= len;
|
||||
}
|
||||
map::Value::Multi(ref vec) => {
|
||||
for val in vec {
|
||||
let v = val.as_ref();
|
||||
let len = k.len() + v.len() + 4;
|
||||
if len > remaining {
|
||||
unsafe {
|
||||
dst.advance_mut(pos);
|
||||
}
|
||||
pos = 0;
|
||||
dst.reserve(len);
|
||||
remaining = dst.remaining_mut();
|
||||
unsafe {
|
||||
buf = &mut *(dst.bytes_mut() as *mut _);
|
||||
}
|
||||
}
|
||||
buf[pos..pos + k.len()].copy_from_slice(k);
|
||||
pos += k.len();
|
||||
buf[pos..pos + 2].copy_from_slice(b": ");
|
||||
pos += 2;
|
||||
buf[pos..pos + v.len()].copy_from_slice(v);
|
||||
pos += v.len();
|
||||
buf[pos..pos + 2].copy_from_slice(b"\r\n");
|
||||
pos += 2;
|
||||
remaining -= len;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
dst.advance_mut(pos);
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ where
|
||||
head.uri = parts.uri;
|
||||
head.method = parts.method;
|
||||
head.version = parts.version;
|
||||
head.headers = parts.headers;
|
||||
head.headers = parts.headers.into();
|
||||
tokio_current_thread::spawn(ServiceResponse::<S::Future, B> {
|
||||
state: ServiceResponseState::ServiceCall(
|
||||
self.service.call(req),
|
||||
|
@ -64,7 +64,7 @@ impl Header for CacheControl {
|
||||
where
|
||||
T: crate::HttpMessage,
|
||||
{
|
||||
let directives = from_comma_delimited(msg.headers().get_all(Self::name()))?;
|
||||
let directives = from_comma_delimited(msg.headers().get_all(&Self::name()))?;
|
||||
if !directives.is_empty() {
|
||||
Ok(CacheControl(directives))
|
||||
} else {
|
||||
|
@ -444,7 +444,7 @@ impl Header for ContentDisposition {
|
||||
}
|
||||
|
||||
fn parse<T: crate::HttpMessage>(msg: &T) -> Result<Self, crate::error::ParseError> {
|
||||
if let Some(h) = msg.headers().get(Self::name()) {
|
||||
if let Some(h) = msg.headers().get(&Self::name()) {
|
||||
Self::from_raw(&h)
|
||||
} else {
|
||||
Err(crate::error::ParseError::Header)
|
||||
|
@ -73,12 +73,12 @@ impl Header for IfRange {
|
||||
T: HttpMessage,
|
||||
{
|
||||
let etag: Result<EntityTag, _> =
|
||||
from_one_raw_str(msg.headers().get(header::IF_RANGE));
|
||||
from_one_raw_str(msg.headers().get(&header::IF_RANGE));
|
||||
if let Ok(etag) = etag {
|
||||
return Ok(IfRange::EntityTag(etag));
|
||||
}
|
||||
let date: Result<HttpDate, _> =
|
||||
from_one_raw_str(msg.headers().get(header::IF_RANGE));
|
||||
from_one_raw_str(msg.headers().get(&header::IF_RANGE));
|
||||
if let Ok(date) = date {
|
||||
return Ok(IfRange::Date(date));
|
||||
}
|
||||
|
384
actix-http/src/header/map.rs
Normal file
384
actix-http/src/header/map.rs
Normal file
@ -0,0 +1,384 @@
|
||||
use either::Either;
|
||||
use hashbrown::hash_map::{self, Entry};
|
||||
use hashbrown::HashMap;
|
||||
use http::header::{HeaderName, HeaderValue};
|
||||
use http::HttpTryFrom;
|
||||
|
||||
/// A set of HTTP headers
|
||||
///
|
||||
/// `HeaderMap` is an multimap of [`HeaderName`] to values.
|
||||
///
|
||||
/// [`HeaderName`]: struct.HeaderName.html
|
||||
#[derive(Debug)]
|
||||
pub struct HeaderMap {
|
||||
pub(crate) inner: HashMap<HeaderName, Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Value {
|
||||
One(HeaderValue),
|
||||
Multi(Vec<HeaderValue>),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
fn get(&self) -> &HeaderValue {
|
||||
match self {
|
||||
Value::One(ref val) => val,
|
||||
Value::Multi(ref val) => &val[0],
|
||||
}
|
||||
}
|
||||
|
||||
fn get_mut(&mut self) -> &mut HeaderValue {
|
||||
match self {
|
||||
Value::One(ref mut val) => val,
|
||||
Value::Multi(ref mut val) => &mut val[0],
|
||||
}
|
||||
}
|
||||
|
||||
fn append(&mut self, val: HeaderValue) {
|
||||
match self {
|
||||
Value::One(_) => {
|
||||
let data = std::mem::replace(self, Value::Multi(vec![val]));
|
||||
match data {
|
||||
Value::One(val) => self.append(val),
|
||||
Value::Multi(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
Value::Multi(ref mut vec) => vec.push(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HeaderMap {
|
||||
/// Create an empty `HeaderMap`.
|
||||
///
|
||||
/// The map will be created without any capacity. This function will not
|
||||
/// allocate.
|
||||
pub fn new() -> Self {
|
||||
HeaderMap {
|
||||
inner: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an empty `HeaderMap` with the specified capacity.
|
||||
///
|
||||
/// The returned map will allocate internal storage in order to hold about
|
||||
/// `capacity` elements without reallocating. However, this is a "best
|
||||
/// effort" as there are usage patterns that could cause additional
|
||||
/// allocations before `capacity` headers are stored in the map.
|
||||
///
|
||||
/// More capacity than requested may be allocated.
|
||||
pub fn with_capacity(capacity: usize) -> HeaderMap {
|
||||
HeaderMap {
|
||||
inner: HashMap::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of keys stored in the map.
|
||||
///
|
||||
/// This number could be be less than or equal to actual headers stored in
|
||||
/// the map.
|
||||
pub fn len(&self) -> usize {
|
||||
self.inner.len()
|
||||
}
|
||||
|
||||
/// Returns true if the map contains no elements.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.inner.len() == 0
|
||||
}
|
||||
|
||||
/// Clears the map, removing all key-value pairs. Keeps the allocated memory
|
||||
/// for reuse.
|
||||
pub fn clear(&mut self) {
|
||||
self.inner.clear();
|
||||
}
|
||||
|
||||
/// Returns the number of headers the map can hold without reallocating.
|
||||
///
|
||||
/// This number is an approximation as certain usage patterns could cause
|
||||
/// additional allocations before the returned capacity is filled.
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.inner.capacity()
|
||||
}
|
||||
|
||||
/// Reserves capacity for at least `additional` more headers to be inserted
|
||||
/// into the `HeaderMap`.
|
||||
///
|
||||
/// The header map may reserve more space to avoid frequent reallocations.
|
||||
/// Like with `with_capacity`, this will be a "best effort" to avoid
|
||||
/// allocations until `additional` more headers are inserted. Certain usage
|
||||
/// patterns could cause additional allocations before the number is
|
||||
/// reached.
|
||||
pub fn reserve(&mut self, additional: usize) {
|
||||
self.inner.reserve(additional)
|
||||
}
|
||||
|
||||
/// Returns a reference to the value associated with the key.
|
||||
///
|
||||
/// If there are multiple values associated with the key, then the first one
|
||||
/// is returned. Use `get_all` to get all values associated with a given
|
||||
/// key. Returns `None` if there are no values associated with the key.
|
||||
pub fn get<N: AsName>(&self, name: N) -> Option<&HeaderValue> {
|
||||
self.get2(name).map(|v| v.get())
|
||||
}
|
||||
|
||||
fn get2<N: AsName>(&self, name: N) -> Option<&Value> {
|
||||
match name.as_name() {
|
||||
Either::Left(name) => self.inner.get(name),
|
||||
Either::Right(s) => {
|
||||
if let Ok(name) = HeaderName::try_from(s) {
|
||||
self.inner.get(&name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a view of all values associated with a key.
|
||||
///
|
||||
/// The returned view does not incur any allocations and allows iterating
|
||||
/// the values associated with the key. See [`GetAll`] for more details.
|
||||
/// Returns `None` if there are no values associated with the key.
|
||||
///
|
||||
/// [`GetAll`]: struct.GetAll.html
|
||||
pub fn get_all<N: AsName>(&self, name: N) -> GetAll {
|
||||
GetAll {
|
||||
idx: 0,
|
||||
item: self.get2(name),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the value associated with the key.
|
||||
///
|
||||
/// If there are multiple values associated with the key, then the first one
|
||||
/// is returned. Use `entry` to get all values associated with a given
|
||||
/// key. Returns `None` if there are no values associated with the key.
|
||||
pub fn get_mut<N: AsName>(&mut self, name: N) -> Option<&mut HeaderValue> {
|
||||
match name.as_name() {
|
||||
Either::Left(name) => self.inner.get_mut(name).map(|v| v.get_mut()),
|
||||
Either::Right(s) => {
|
||||
if let Ok(name) = HeaderName::try_from(s) {
|
||||
self.inner.get_mut(&name).map(|v| v.get_mut())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the map contains a value for the specified key.
|
||||
pub fn contains_key<N: AsName>(&self, key: N) -> bool {
|
||||
match key.as_name() {
|
||||
Either::Left(name) => self.inner.contains_key(name),
|
||||
Either::Right(s) => {
|
||||
if let Ok(name) = HeaderName::try_from(s) {
|
||||
self.inner.contains_key(&name)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator visiting all key-value pairs.
|
||||
///
|
||||
/// The iteration order is arbitrary, but consistent across platforms for
|
||||
/// the same crate version. Each key will be yielded once per associated
|
||||
/// value. So, if a key has 3 associated values, it will be yielded 3 times.
|
||||
pub fn iter(&self) -> Iter {
|
||||
Iter::new(self.inner.iter())
|
||||
}
|
||||
|
||||
/// An iterator visiting all keys.
|
||||
///
|
||||
/// The iteration order is arbitrary, but consistent across platforms for
|
||||
/// the same crate version. Each key will be yielded only once even if it
|
||||
/// has multiple associated values.
|
||||
pub fn keys(&self) -> Keys {
|
||||
Keys(self.inner.keys())
|
||||
}
|
||||
|
||||
/// Inserts a key-value pair into the map.
|
||||
///
|
||||
/// If the map did not previously have this key present, then `None` is
|
||||
/// returned.
|
||||
///
|
||||
/// If the map did have this key present, the new value is associated with
|
||||
/// the key and all previous values are removed. **Note** that only a single
|
||||
/// one of the previous values is returned. If there are multiple values
|
||||
/// that have been previously associated with the key, then the first one is
|
||||
/// returned. See `insert_mult` on `OccupiedEntry` for an API that returns
|
||||
/// all values.
|
||||
///
|
||||
/// The key is not updated, though; this matters for types that can be `==`
|
||||
/// without being identical.
|
||||
pub fn insert(&mut self, key: HeaderName, val: HeaderValue) {
|
||||
let _ = self.inner.insert(key, Value::One(val));
|
||||
}
|
||||
|
||||
/// Inserts a key-value pair into the map.
|
||||
///
|
||||
/// If the map did not previously have this key present, then `false` is
|
||||
/// returned.
|
||||
///
|
||||
/// If the map did have this key present, the new value is pushed to the end
|
||||
/// of the list of values currently associated with the key. The key is not
|
||||
/// updated, though; this matters for types that can be `==` without being
|
||||
/// identical.
|
||||
pub fn append(&mut self, key: HeaderName, value: HeaderValue) {
|
||||
match self.inner.entry(key) {
|
||||
Entry::Occupied(mut entry) => entry.get_mut().append(value),
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(Value::One(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes all headers for a particular header name from the map.
|
||||
pub fn remove<N: AsName>(&mut self, key: N) {
|
||||
match key.as_name() {
|
||||
Either::Left(name) => {
|
||||
let _ = self.inner.remove(name);
|
||||
}
|
||||
Either::Right(s) => {
|
||||
if let Ok(name) = HeaderName::try_from(s) {
|
||||
let _ = self.inner.remove(&name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait AsName {
|
||||
fn as_name(&self) -> Either<&HeaderName, &str>;
|
||||
}
|
||||
|
||||
impl AsName for HeaderName {
|
||||
fn as_name(&self) -> Either<&HeaderName, &str> {
|
||||
Either::Left(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsName for &'a HeaderName {
|
||||
fn as_name(&self) -> Either<&HeaderName, &str> {
|
||||
Either::Left(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsName for &'a str {
|
||||
fn as_name(&self) -> Either<&HeaderName, &str> {
|
||||
Either::Right(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsName for String {
|
||||
fn as_name(&self) -> Either<&HeaderName, &str> {
|
||||
Either::Right(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsName for &'a String {
|
||||
fn as_name(&self) -> Either<&HeaderName, &str> {
|
||||
Either::Right(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GetAll<'a> {
|
||||
idx: usize,
|
||||
item: Option<&'a Value>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for GetAll<'a> {
|
||||
type Item = &'a HeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<&'a HeaderValue> {
|
||||
if let Some(ref val) = self.item {
|
||||
match val {
|
||||
Value::One(ref val) => {
|
||||
self.item.take();
|
||||
Some(val)
|
||||
}
|
||||
Value::Multi(ref vec) => {
|
||||
if self.idx < vec.len() {
|
||||
let item = Some(&vec[self.idx]);
|
||||
self.idx += 1;
|
||||
item
|
||||
} else {
|
||||
self.item.take();
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Keys<'a>(hash_map::Keys<'a, HeaderName, Value>);
|
||||
|
||||
impl<'a> Iterator for Keys<'a> {
|
||||
type Item = &'a HeaderName;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<&'a HeaderName> {
|
||||
self.0.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a HeaderMap {
|
||||
type Item = (&'a HeaderName, &'a HeaderValue);
|
||||
type IntoIter = Iter<'a>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Iter<'a> {
|
||||
idx: usize,
|
||||
current: Option<(&'a HeaderName, &'a Vec<HeaderValue>)>,
|
||||
iter: hash_map::Iter<'a, HeaderName, Value>,
|
||||
}
|
||||
|
||||
impl<'a> Iter<'a> {
|
||||
fn new(iter: hash_map::Iter<'a, HeaderName, Value>) -> Self {
|
||||
Self {
|
||||
iter,
|
||||
idx: 0,
|
||||
current: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Iter<'a> {
|
||||
type Item = (&'a HeaderName, &'a HeaderValue);
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<(&'a HeaderName, &'a HeaderValue)> {
|
||||
if let Some(ref mut item) = self.current {
|
||||
if self.idx < item.1.len() {
|
||||
let item = (item.0, &item.1[self.idx]);
|
||||
self.idx += 1;
|
||||
return Some(item);
|
||||
} else {
|
||||
self.idx = 0;
|
||||
self.current.take();
|
||||
}
|
||||
}
|
||||
if let Some(item) = self.iter.next() {
|
||||
match item.1 {
|
||||
Value::One(ref value) => Some((item.0, value)),
|
||||
Value::Multi(ref vec) => {
|
||||
self.current = Some((item.0, vec));
|
||||
self.next()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,6 @@
|
||||
use std::{fmt, str::FromStr};
|
||||
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use http::header::GetAll;
|
||||
use http::Error as HttpError;
|
||||
use mime::Mime;
|
||||
|
||||
@ -14,12 +13,17 @@ use crate::error::ParseError;
|
||||
use crate::httpmessage::HttpMessage;
|
||||
|
||||
mod common;
|
||||
pub(crate) mod map;
|
||||
mod shared;
|
||||
#[doc(hidden)]
|
||||
pub use self::common::*;
|
||||
#[doc(hidden)]
|
||||
pub use self::shared::*;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use self::map::GetAll;
|
||||
pub use self::map::HeaderMap;
|
||||
|
||||
/// A trait for any object that will represent a header field and value.
|
||||
pub trait Header
|
||||
where
|
||||
@ -220,8 +224,8 @@ impl fmt::Write for Writer {
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
/// Reads a comma-delimited raw header into a Vec.
|
||||
pub fn from_comma_delimited<T: FromStr>(
|
||||
all: GetAll<HeaderValue>,
|
||||
pub fn from_comma_delimited<'a, I: Iterator<Item = &'a HeaderValue> + 'a, T: FromStr>(
|
||||
all: I,
|
||||
) -> Result<Vec<T>, ParseError> {
|
||||
let mut result = Vec::new();
|
||||
for h in all {
|
||||
@ -379,6 +383,17 @@ pub fn http_percent_encode(f: &mut fmt::Formatter, bytes: &[u8]) -> fmt::Result
|
||||
fmt::Display::fmt(&encoded, f)
|
||||
}
|
||||
|
||||
/// Convert http::HeaderMap to a HeaderMap
|
||||
impl From<http::HeaderMap> for HeaderMap {
|
||||
fn from(map: http::HeaderMap) -> HeaderMap {
|
||||
let mut new_map = HeaderMap::with_capacity(map.capacity());
|
||||
for (h, v) in map.iter() {
|
||||
new_map.append(h.clone(), v.clone());
|
||||
}
|
||||
new_map
|
||||
}
|
||||
}
|
||||
|
||||
mod percent_encoding_http {
|
||||
use percent_encoding::{self, define_encode_set};
|
||||
|
||||
|
@ -4,13 +4,13 @@ use std::str;
|
||||
use encoding::all::UTF_8;
|
||||
use encoding::label::encoding_from_whatwg_label;
|
||||
use encoding::EncodingRef;
|
||||
use http::{header, HeaderMap};
|
||||
use http::header;
|
||||
use mime::Mime;
|
||||
|
||||
use crate::cookie::Cookie;
|
||||
use crate::error::{ContentTypeError, CookieParseError, ParseError};
|
||||
use crate::extensions::Extensions;
|
||||
use crate::header::Header;
|
||||
use crate::header::{Header, HeaderMap};
|
||||
use crate::payload::Payload;
|
||||
|
||||
struct Cookies(Vec<Cookie<'static>>);
|
||||
|
@ -45,10 +45,11 @@ pub mod http {
|
||||
// re-exports
|
||||
pub use http::header::{HeaderName, HeaderValue};
|
||||
pub use http::uri::PathAndQuery;
|
||||
pub use http::{uri, Error, HeaderMap, HttpTryFrom, Uri};
|
||||
pub use http::{uri, Error, HttpTryFrom, Uri};
|
||||
pub use http::{Method, StatusCode, Version};
|
||||
|
||||
pub use crate::cookie::{Cookie, CookieBuilder};
|
||||
pub use crate::header::HeaderMap;
|
||||
|
||||
/// Various http headers
|
||||
pub mod header {
|
||||
|
@ -5,7 +5,8 @@ use std::rc::Rc;
|
||||
use bitflags::bitflags;
|
||||
|
||||
use crate::extensions::Extensions;
|
||||
use crate::http::{header, HeaderMap, Method, StatusCode, Uri, Version};
|
||||
use crate::header::HeaderMap;
|
||||
use crate::http::{header, Method, StatusCode, Uri, Version};
|
||||
|
||||
/// Represents various types of connection
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
@ -174,7 +175,7 @@ impl Default for ResponseHead {
|
||||
ResponseHead {
|
||||
version: Version::default(),
|
||||
status: StatusCode::OK,
|
||||
headers: HeaderMap::with_capacity(16),
|
||||
headers: HeaderMap::with_capacity(12),
|
||||
reason: None,
|
||||
flags: Flags::empty(),
|
||||
extensions: RefCell::new(Extensions::new()),
|
||||
@ -182,18 +183,6 @@ impl Default for ResponseHead {
|
||||
}
|
||||
}
|
||||
|
||||
impl Head for ResponseHead {
|
||||
fn clear(&mut self) {
|
||||
self.reason = None;
|
||||
self.flags = Flags::empty();
|
||||
self.headers.clear();
|
||||
}
|
||||
|
||||
fn pool() -> &'static MessagePool<Self> {
|
||||
RESPONSE_POOL.with(|p| *p)
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseHead {
|
||||
/// Message extensions
|
||||
#[inline]
|
||||
@ -300,7 +289,6 @@ impl ResponseHead {
|
||||
|
||||
pub struct Message<T: Head> {
|
||||
head: Rc<T>,
|
||||
pool: &'static MessagePool<T>,
|
||||
}
|
||||
|
||||
impl<T: Head> Message<T> {
|
||||
@ -314,7 +302,6 @@ impl<T: Head> Clone for Message<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Message {
|
||||
head: self.head.clone(),
|
||||
pool: self.pool,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -336,17 +323,52 @@ impl<T: Head> std::ops::DerefMut for Message<T> {
|
||||
impl<T: Head> Drop for Message<T> {
|
||||
fn drop(&mut self) {
|
||||
if Rc::strong_count(&self.head) == 1 {
|
||||
self.pool.release(self.head.clone());
|
||||
T::pool().release(self.head.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct BoxedResponseHead {
|
||||
head: Option<Box<ResponseHead>>,
|
||||
}
|
||||
|
||||
impl BoxedResponseHead {
|
||||
/// Get new message from the pool of objects
|
||||
pub fn new() -> Self {
|
||||
RESPONSE_POOL.with(|p| p.get_message())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for BoxedResponseHead {
|
||||
type Target = ResponseHead;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.head.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for BoxedResponseHead {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.head.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BoxedResponseHead {
|
||||
fn drop(&mut self) {
|
||||
RESPONSE_POOL.with(|p| p.release(self.head.take().unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Request's objects pool
|
||||
pub struct MessagePool<T: Head>(RefCell<VecDeque<Rc<T>>>);
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Request's objects pool
|
||||
pub struct BoxedResponsePool(RefCell<VecDeque<Box<ResponseHead>>>);
|
||||
|
||||
thread_local!(static REQUEST_POOL: &'static MessagePool<RequestHead> = MessagePool::<RequestHead>::create());
|
||||
thread_local!(static RESPONSE_POOL: &'static MessagePool<ResponseHead> = MessagePool::<ResponseHead>::create());
|
||||
thread_local!(static RESPONSE_POOL: &'static BoxedResponsePool = BoxedResponsePool::create());
|
||||
|
||||
impl<T: Head> MessagePool<T> {
|
||||
fn create() -> &'static MessagePool<T> {
|
||||
@ -361,14 +383,10 @@ impl<T: Head> MessagePool<T> {
|
||||
if let Some(r) = Rc::get_mut(&mut msg) {
|
||||
r.clear();
|
||||
}
|
||||
Message {
|
||||
head: msg,
|
||||
pool: self,
|
||||
}
|
||||
Message { head: msg }
|
||||
} else {
|
||||
Message {
|
||||
head: Rc::new(T::default()),
|
||||
pool: self,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -382,3 +400,34 @@ impl<T: Head> MessagePool<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxedResponsePool {
|
||||
fn create() -> &'static BoxedResponsePool {
|
||||
let pool = BoxedResponsePool(RefCell::new(VecDeque::with_capacity(128)));
|
||||
Box::leak(Box::new(pool))
|
||||
}
|
||||
|
||||
/// Get message from the pool
|
||||
#[inline]
|
||||
fn get_message(&'static self) -> BoxedResponseHead {
|
||||
if let Some(mut head) = self.0.borrow_mut().pop_front() {
|
||||
head.reason = None;
|
||||
head.headers.clear();
|
||||
head.flags = Flags::empty();
|
||||
BoxedResponseHead { head: Some(head) }
|
||||
} else {
|
||||
BoxedResponseHead {
|
||||
head: Some(Box::new(ResponseHead::default())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Release request instance
|
||||
fn release(&self, msg: Box<ResponseHead>) {
|
||||
let v = &mut self.0.borrow_mut();
|
||||
if v.len() < 128 {
|
||||
v.push_front(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
use std::cell::{Ref, RefMut};
|
||||
use std::fmt;
|
||||
|
||||
use http::{header, HeaderMap, Method, Uri, Version};
|
||||
use http::{header, Method, Uri, Version};
|
||||
|
||||
use crate::extensions::Extensions;
|
||||
use crate::header::HeaderMap;
|
||||
use crate::httpmessage::HttpMessage;
|
||||
use crate::message::{Message, RequestHead};
|
||||
use crate::payload::{Payload, PayloadStream};
|
||||
@ -161,7 +162,7 @@ impl<P> fmt::Debug for Request<P> {
|
||||
writeln!(f, " query: ?{:?}", q)?;
|
||||
}
|
||||
writeln!(f, " headers:")?;
|
||||
for (key, val) in self.headers().iter() {
|
||||
for (key, val) in self.headers() {
|
||||
writeln!(f, " {:?}: {:?}", key, val)?;
|
||||
}
|
||||
Ok(())
|
||||
|
@ -6,8 +6,6 @@ use std::{fmt, io, str};
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
use futures::future::{ok, FutureResult, IntoFuture};
|
||||
use futures::Stream;
|
||||
use http::header::{self, HeaderName, HeaderValue};
|
||||
use http::{Error as HttpError, HeaderMap, HttpTryFrom, StatusCode};
|
||||
use serde::Serialize;
|
||||
use serde_json;
|
||||
|
||||
@ -16,11 +14,13 @@ use crate::cookie::{Cookie, CookieJar};
|
||||
use crate::error::Error;
|
||||
use crate::extensions::Extensions;
|
||||
use crate::header::{Header, IntoHeaderValue};
|
||||
use crate::message::{ConnectionType, Message, ResponseHead};
|
||||
use crate::http::header::{self, HeaderName, HeaderValue};
|
||||
use crate::http::{Error as HttpError, HeaderMap, HttpTryFrom, StatusCode};
|
||||
use crate::message::{BoxedResponseHead, ConnectionType, ResponseHead};
|
||||
|
||||
/// An HTTP Response
|
||||
pub struct Response<B = Body> {
|
||||
head: Message<ResponseHead>,
|
||||
head: BoxedResponseHead,
|
||||
body: ResponseBody<B>,
|
||||
error: Option<Error>,
|
||||
}
|
||||
@ -41,7 +41,7 @@ impl Response<Body> {
|
||||
/// Constructs a response
|
||||
#[inline]
|
||||
pub fn new(status: StatusCode) -> Response {
|
||||
let mut head: Message<ResponseHead> = Message::new();
|
||||
let mut head = BoxedResponseHead::new();
|
||||
head.status = status;
|
||||
|
||||
Response {
|
||||
@ -93,7 +93,7 @@ impl<B> Response<B> {
|
||||
/// Constructs a response with body
|
||||
#[inline]
|
||||
pub fn with_body(status: StatusCode, body: B) -> Response<B> {
|
||||
let mut head: Message<ResponseHead> = Message::new();
|
||||
let mut head = BoxedResponseHead::new();
|
||||
head.status = status;
|
||||
Response {
|
||||
head,
|
||||
@ -136,7 +136,7 @@ impl<B> Response<B> {
|
||||
#[inline]
|
||||
pub fn cookies(&self) -> CookieIter {
|
||||
CookieIter {
|
||||
iter: self.head.headers.get_all(header::SET_COOKIE).iter(),
|
||||
iter: self.head.headers.get_all(header::SET_COOKIE),
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,7 +158,6 @@ impl<B> Response<B> {
|
||||
let h = &mut self.head.headers;
|
||||
let vals: Vec<HeaderValue> = h
|
||||
.get_all(header::SET_COOKIE)
|
||||
.iter()
|
||||
.map(|v| v.to_owned())
|
||||
.collect();
|
||||
h.remove(header::SET_COOKIE);
|
||||
@ -286,7 +285,7 @@ impl IntoFuture for Response {
|
||||
}
|
||||
|
||||
pub struct CookieIter<'a> {
|
||||
iter: header::ValueIter<'a, HeaderValue>,
|
||||
iter: header::GetAll<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for CookieIter<'a> {
|
||||
@ -320,7 +319,7 @@ impl<'a> io::Write for Writer<'a> {
|
||||
/// This type can be used to construct an instance of `Response` through a
|
||||
/// builder-like pattern.
|
||||
pub struct ResponseBuilder {
|
||||
head: Option<Message<ResponseHead>>,
|
||||
head: Option<BoxedResponseHead>,
|
||||
err: Option<HttpError>,
|
||||
cookies: Option<CookieJar>,
|
||||
}
|
||||
@ -328,7 +327,7 @@ pub struct ResponseBuilder {
|
||||
impl ResponseBuilder {
|
||||
/// Create response builder
|
||||
pub fn new(status: StatusCode) -> Self {
|
||||
let mut head: Message<ResponseHead> = Message::new();
|
||||
let mut head = BoxedResponseHead::new();
|
||||
head.status = status;
|
||||
|
||||
ResponseBuilder {
|
||||
@ -701,13 +700,13 @@ impl ResponseBuilder {
|
||||
|
||||
#[inline]
|
||||
fn parts<'a>(
|
||||
parts: &'a mut Option<Message<ResponseHead>>,
|
||||
parts: &'a mut Option<BoxedResponseHead>,
|
||||
err: &Option<HttpError>,
|
||||
) -> Option<&'a mut Message<ResponseHead>> {
|
||||
) -> Option<&'a mut ResponseHead> {
|
||||
if err.is_some() {
|
||||
return None;
|
||||
}
|
||||
parts.as_mut()
|
||||
parts.as_mut().map(|r| &mut **r)
|
||||
}
|
||||
|
||||
/// Convert `Response` to a `ResponseBuilder`. Body get dropped.
|
||||
@ -740,7 +739,7 @@ impl<'a> From<&'a ResponseHead> for ResponseBuilder {
|
||||
let mut jar: Option<CookieJar> = None;
|
||||
|
||||
let cookies = CookieIter {
|
||||
iter: head.headers.get_all(header::SET_COOKIE).iter(),
|
||||
iter: head.headers.get_all(header::SET_COOKIE),
|
||||
};
|
||||
for c in cookies {
|
||||
if let Some(ref mut j) = jar {
|
||||
@ -752,11 +751,11 @@ impl<'a> From<&'a ResponseHead> for ResponseBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
let mut msg: Message<ResponseHead> = Message::new();
|
||||
let mut msg = BoxedResponseHead::new();
|
||||
msg.version = head.version;
|
||||
msg.status = head.status;
|
||||
msg.reason = head.reason;
|
||||
msg.headers = head.headers.clone();
|
||||
// msg.headers = head.headers.clone();
|
||||
msg.no_chunking(!head.chunked());
|
||||
|
||||
ResponseBuilder {
|
||||
@ -845,7 +844,7 @@ impl From<BytesMut> for Response {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::body::Body;
|
||||
use crate::http::header::{HeaderValue, CONTENT_TYPE, COOKIE};
|
||||
use crate::http::header::{HeaderValue, CONTENT_TYPE, COOKIE, SET_COOKIE};
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
@ -876,13 +875,12 @@ mod tests {
|
||||
.max_age(time::Duration::days(1))
|
||||
.finish(),
|
||||
)
|
||||
.del_cookie(&cookies[0])
|
||||
.del_cookie(&cookies[1])
|
||||
.finish();
|
||||
|
||||
let mut val: Vec<_> = resp
|
||||
.headers()
|
||||
.get_all("Set-Cookie")
|
||||
.iter()
|
||||
.get_all(SET_COOKIE)
|
||||
.map(|v| v.to_str().unwrap().to_owned())
|
||||
.collect();
|
||||
val.sort();
|
||||
@ -911,9 +909,9 @@ mod tests {
|
||||
|
||||
let mut iter = r.cookies();
|
||||
let v = iter.next().unwrap();
|
||||
assert_eq!((v.name(), v.value()), ("original", "val100"));
|
||||
let v = iter.next().unwrap();
|
||||
assert_eq!((v.name(), v.value()), ("cookie3", "val300"));
|
||||
let v = iter.next().unwrap();
|
||||
assert_eq!((v.name(), v.value()), ("original", "val100"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1049,6 +1047,7 @@ mod tests {
|
||||
resp.headers().get(CONTENT_TYPE).unwrap(),
|
||||
HeaderValue::from_static("application/octet-stream")
|
||||
);
|
||||
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
assert_eq!(resp.body().get_ref(), b"test");
|
||||
}
|
||||
|
@ -4,10 +4,11 @@ use std::str::FromStr;
|
||||
|
||||
use bytes::Bytes;
|
||||
use http::header::{self, HeaderName, HeaderValue};
|
||||
use http::{HeaderMap, HttpTryFrom, Method, Uri, Version};
|
||||
use http::{HttpTryFrom, Method, Uri, Version};
|
||||
use percent_encoding::{percent_encode, USERINFO_ENCODE_SET};
|
||||
|
||||
use crate::cookie::{Cookie, CookieJar};
|
||||
use crate::header::HeaderMap;
|
||||
use crate::header::{Header, IntoHeaderValue};
|
||||
use crate::payload::Payload;
|
||||
use crate::Request;
|
||||
|
@ -92,7 +92,7 @@ impl Multipart {
|
||||
|
||||
/// Extract boundary info from headers.
|
||||
fn boundary(headers: &HeaderMap) -> Result<String, MultipartError> {
|
||||
if let Some(content_type) = headers.get(header::CONTENT_TYPE) {
|
||||
if let Some(content_type) = headers.get(&header::CONTENT_TYPE) {
|
||||
if let Ok(content_type) = content_type.to_str() {
|
||||
if let Ok(ct) = content_type.parse::<mime::Mime>() {
|
||||
if let Some(boundary) = ct.get_param(mime::BOUNDARY) {
|
||||
@ -334,7 +334,7 @@ impl InnerMultipart {
|
||||
|
||||
// content type
|
||||
let mut mt = mime::APPLICATION_OCTET_STREAM;
|
||||
if let Some(content_type) = headers.get(header::CONTENT_TYPE) {
|
||||
if let Some(content_type) = headers.get(&header::CONTENT_TYPE) {
|
||||
if let Ok(content_type) = content_type.to_str() {
|
||||
if let Ok(ct) = content_type.parse::<mime::Mime>() {
|
||||
mt = ct;
|
||||
@ -427,7 +427,7 @@ impl Field {
|
||||
pub fn content_disposition(&self) -> Option<ContentDisposition> {
|
||||
// RFC 7578: 'Each part MUST contain a Content-Disposition header field
|
||||
// where the disposition type is "form-data".'
|
||||
if let Some(content_disposition) = self.headers.get(header::CONTENT_DISPOSITION)
|
||||
if let Some(content_disposition) = self.headers.get(&header::CONTENT_DISPOSITION)
|
||||
{
|
||||
ContentDisposition::from_raw(content_disposition).ok()
|
||||
} else {
|
||||
@ -480,7 +480,7 @@ impl InnerField {
|
||||
boundary: String,
|
||||
headers: &HeaderMap,
|
||||
) -> Result<InnerField, PayloadError> {
|
||||
let len = if let Some(len) = headers.get(header::CONTENT_LENGTH) {
|
||||
let len = if let Some(len) = headers.get(&header::CONTENT_LENGTH) {
|
||||
if let Ok(s) = len.to_str() {
|
||||
if let Ok(len) = s.parse::<u64>() {
|
||||
Some(len)
|
||||
|
@ -50,7 +50,7 @@ pub fn handshake(req: &HttpRequest) -> Result<HttpResponseBuilder, HandshakeErro
|
||||
}
|
||||
|
||||
// Check for "UPGRADE" to websocket header
|
||||
let has_hdr = if let Some(hdr) = req.headers().get(header::UPGRADE) {
|
||||
let has_hdr = if let Some(hdr) = req.headers().get(&header::UPGRADE) {
|
||||
if let Ok(s) = hdr.to_str() {
|
||||
s.to_ascii_lowercase().contains("websocket")
|
||||
} else {
|
||||
@ -69,11 +69,11 @@ pub fn handshake(req: &HttpRequest) -> Result<HttpResponseBuilder, HandshakeErro
|
||||
}
|
||||
|
||||
// check supported version
|
||||
if !req.headers().contains_key(header::SEC_WEBSOCKET_VERSION) {
|
||||
if !req.headers().contains_key(&header::SEC_WEBSOCKET_VERSION) {
|
||||
return Err(HandshakeError::NoVersionHeader);
|
||||
}
|
||||
let supported_ver = {
|
||||
if let Some(hdr) = req.headers().get(header::SEC_WEBSOCKET_VERSION) {
|
||||
if let Some(hdr) = req.headers().get(&header::SEC_WEBSOCKET_VERSION) {
|
||||
hdr == "13" || hdr == "8" || hdr == "7"
|
||||
} else {
|
||||
false
|
||||
@ -84,11 +84,11 @@ pub fn handshake(req: &HttpRequest) -> Result<HttpResponseBuilder, HandshakeErro
|
||||
}
|
||||
|
||||
// check client handshake for validity
|
||||
if !req.headers().contains_key(header::SEC_WEBSOCKET_KEY) {
|
||||
if !req.headers().contains_key(&header::SEC_WEBSOCKET_KEY) {
|
||||
return Err(HandshakeError::BadWebsocketKey);
|
||||
}
|
||||
let key = {
|
||||
let key = req.headers().get(header::SEC_WEBSOCKET_KEY).unwrap();
|
||||
let key = req.headers().get(&header::SEC_WEBSOCKET_KEY).unwrap();
|
||||
hash_key(key.as_ref())
|
||||
};
|
||||
|
||||
|
@ -104,7 +104,7 @@ impl Client {
|
||||
{
|
||||
let mut req = ClientRequest::new(method, url, self.0.clone());
|
||||
|
||||
for (key, value) in &self.0.headers {
|
||||
for (key, value) in self.0.headers.iter() {
|
||||
req = req.set_header_if_none(key.clone(), value.clone());
|
||||
}
|
||||
req
|
||||
@ -119,7 +119,7 @@ impl Client {
|
||||
Uri: HttpTryFrom<U>,
|
||||
{
|
||||
let mut req = self.request(head.method.clone(), url);
|
||||
for (key, value) in &head.headers {
|
||||
for (key, value) in head.headers.iter() {
|
||||
req = req.set_header_if_none(key.clone(), value.clone());
|
||||
}
|
||||
req
|
||||
@ -187,7 +187,7 @@ impl Client {
|
||||
Uri: HttpTryFrom<U>,
|
||||
{
|
||||
let mut req = ws::WebsocketsRequest::new(url, self.0.clone());
|
||||
for (key, value) in &self.0.headers {
|
||||
for (key, value) in self.0.headers.iter() {
|
||||
req.head.headers.insert(key.clone(), value.clone());
|
||||
}
|
||||
req
|
||||
|
@ -396,7 +396,7 @@ impl ClientRequest {
|
||||
if self.default_headers {
|
||||
// set request host header
|
||||
if let Some(host) = self.head.uri.host() {
|
||||
if !self.head.headers.contains_key(header::HOST) {
|
||||
if !self.head.headers.contains_key(&header::HOST) {
|
||||
let mut wrt = BytesMut::with_capacity(host.len() + 5).writer();
|
||||
|
||||
let _ = match self.head.uri.port_u16() {
|
||||
@ -414,7 +414,7 @@ impl ClientRequest {
|
||||
}
|
||||
|
||||
// user agent
|
||||
if !self.head.headers.contains_key(header::USER_AGENT) {
|
||||
if !self.head.headers.contains_key(&header::USER_AGENT) {
|
||||
self.head.headers.insert(
|
||||
header::USER_AGENT,
|
||||
HeaderValue::from_static(concat!("awc/", env!("CARGO_PKG_VERSION"))),
|
||||
|
@ -46,7 +46,7 @@ impl<S> HttpMessage for ClientResponse<S> {
|
||||
|
||||
if self.extensions().get::<Cookies>().is_none() {
|
||||
let mut cookies = Vec::new();
|
||||
for hdr in self.headers().get_all(SET_COOKIE) {
|
||||
for hdr in self.headers().get_all(&SET_COOKIE) {
|
||||
let s = std::str::from_utf8(hdr.as_bytes())
|
||||
.map_err(CookieParseError::from)?;
|
||||
cookies.push(Cookie::parse_encoded(s)?.into_owned());
|
||||
@ -160,7 +160,7 @@ where
|
||||
/// Create `MessageBody` for request.
|
||||
pub fn new(res: &mut ClientResponse<S>) -> MessageBody<S> {
|
||||
let mut len = None;
|
||||
if let Some(l) = res.headers().get(CONTENT_LENGTH) {
|
||||
if let Some(l) = res.headers().get(&CONTENT_LENGTH) {
|
||||
if let Ok(s) = l.to_str() {
|
||||
if let Ok(l) = s.parse::<usize>() {
|
||||
len = Some(l)
|
||||
@ -254,7 +254,7 @@ where
|
||||
}
|
||||
|
||||
let mut len = None;
|
||||
if let Some(l) = req.headers().get(CONTENT_LENGTH) {
|
||||
if let Some(l) = req.headers().get(&CONTENT_LENGTH) {
|
||||
if let Ok(s) = l.to_str() {
|
||||
if let Ok(l) = s.parse::<usize>() {
|
||||
len = Some(l)
|
||||
|
@ -236,7 +236,7 @@ impl WebsocketsRequest {
|
||||
let mut slf = if self.default_headers {
|
||||
// set request host header
|
||||
if let Some(host) = self.head.uri.host() {
|
||||
if !self.head.headers.contains_key(header::HOST) {
|
||||
if !self.head.headers.contains_key(&header::HOST) {
|
||||
let mut wrt = BytesMut::with_capacity(host.len() + 5).writer();
|
||||
|
||||
let _ = match self.head.uri.port_u16() {
|
||||
@ -324,7 +324,7 @@ impl WebsocketsRequest {
|
||||
return Err(WsClientError::InvalidResponseStatus(head.status));
|
||||
}
|
||||
// Check for "UPGRADE" to websocket header
|
||||
let has_hdr = if let Some(hdr) = head.headers.get(header::UPGRADE) {
|
||||
let has_hdr = if let Some(hdr) = head.headers.get(&header::UPGRADE) {
|
||||
if let Ok(s) = hdr.to_str() {
|
||||
s.to_ascii_lowercase().contains("websocket")
|
||||
} else {
|
||||
@ -338,7 +338,7 @@ impl WebsocketsRequest {
|
||||
return Err(WsClientError::InvalidUpgradeHeader);
|
||||
}
|
||||
// Check for "CONNECTION" header
|
||||
if let Some(conn) = head.headers.get(header::CONNECTION) {
|
||||
if let Some(conn) = head.headers.get(&header::CONNECTION) {
|
||||
if let Ok(s) = conn.to_str() {
|
||||
if !s.to_ascii_lowercase().contains("upgrade") {
|
||||
log::trace!("Invalid connection header: {}", s);
|
||||
@ -355,7 +355,7 @@ impl WebsocketsRequest {
|
||||
return Err(WsClientError::MissingConnectionHeader);
|
||||
}
|
||||
|
||||
if let Some(hdr_key) = head.headers.get(header::SEC_WEBSOCKET_ACCEPT) {
|
||||
if let Some(hdr_key) = head.headers.get(&header::SEC_WEBSOCKET_ACCEPT) {
|
||||
let encoded = ws::hash_key(key.as_ref());
|
||||
if hdr_key.as_bytes() != encoded.as_bytes() {
|
||||
log::trace!(
|
||||
|
10
src/info.rs
10
src/info.rs
@ -33,7 +33,7 @@ impl ConnectionInfo {
|
||||
let peer = None;
|
||||
|
||||
// load forwarded header
|
||||
for hdr in req.headers.get_all(header::FORWARDED) {
|
||||
for hdr in req.headers.get_all(&header::FORWARDED) {
|
||||
if let Ok(val) = hdr.to_str() {
|
||||
for pair in val.split(';') {
|
||||
for el in pair.split(',') {
|
||||
@ -69,7 +69,7 @@ impl ConnectionInfo {
|
||||
if scheme.is_none() {
|
||||
if let Some(h) = req
|
||||
.headers
|
||||
.get(HeaderName::from_lowercase(X_FORWARDED_PROTO).unwrap())
|
||||
.get(&HeaderName::from_lowercase(X_FORWARDED_PROTO).unwrap())
|
||||
{
|
||||
if let Ok(h) = h.to_str() {
|
||||
scheme = h.split(',').next().map(|v| v.trim());
|
||||
@ -87,14 +87,14 @@ impl ConnectionInfo {
|
||||
if host.is_none() {
|
||||
if let Some(h) = req
|
||||
.headers
|
||||
.get(HeaderName::from_lowercase(X_FORWARDED_HOST).unwrap())
|
||||
.get(&HeaderName::from_lowercase(X_FORWARDED_HOST).unwrap())
|
||||
{
|
||||
if let Ok(h) = h.to_str() {
|
||||
host = h.split(',').next().map(|v| v.trim());
|
||||
}
|
||||
}
|
||||
if host.is_none() {
|
||||
if let Some(h) = req.headers.get(header::HOST) {
|
||||
if let Some(h) = req.headers.get(&header::HOST) {
|
||||
host = h.to_str().ok();
|
||||
}
|
||||
if host.is_none() {
|
||||
@ -110,7 +110,7 @@ impl ConnectionInfo {
|
||||
if remote.is_none() {
|
||||
if let Some(h) = req
|
||||
.headers
|
||||
.get(HeaderName::from_lowercase(X_FORWARDED_FOR).unwrap())
|
||||
.get(&HeaderName::from_lowercase(X_FORWARDED_FOR).unwrap())
|
||||
{
|
||||
if let Ok(h) = h.to_str() {
|
||||
remote = h.split(',').next().map(|v| v.trim());
|
||||
|
@ -109,7 +109,7 @@ where
|
||||
|
||||
fn call(&mut self, req: ServiceRequest<P>) -> Self::Future {
|
||||
// negotiate content-encoding
|
||||
let encoding = if let Some(val) = req.headers().get(ACCEPT_ENCODING) {
|
||||
let encoding = if let Some(val) = req.headers().get(&ACCEPT_ENCODING) {
|
||||
if let Ok(enc) = val.to_str() {
|
||||
AcceptEncoding::parse(enc, self.encoding)
|
||||
} else {
|
||||
|
@ -584,7 +584,7 @@ struct Inner {
|
||||
|
||||
impl Inner {
|
||||
fn validate_origin(&self, req: &RequestHead) -> Result<(), CorsError> {
|
||||
if let Some(hdr) = req.headers().get(header::ORIGIN) {
|
||||
if let Some(hdr) = req.headers().get(&header::ORIGIN) {
|
||||
if let Ok(origin) = hdr.to_str() {
|
||||
return match self.origins {
|
||||
AllOrSome::All => Ok(()),
|
||||
@ -608,7 +608,7 @@ impl Inner {
|
||||
AllOrSome::All => {
|
||||
if self.send_wildcard {
|
||||
Some(HeaderValue::from_static("*"))
|
||||
} else if let Some(origin) = req.headers().get(header::ORIGIN) {
|
||||
} else if let Some(origin) = req.headers().get(&header::ORIGIN) {
|
||||
Some(origin.clone())
|
||||
} else {
|
||||
None
|
||||
@ -617,7 +617,7 @@ impl Inner {
|
||||
AllOrSome::Some(ref origins) => {
|
||||
if let Some(origin) =
|
||||
req.headers()
|
||||
.get(header::ORIGIN)
|
||||
.get(&header::ORIGIN)
|
||||
.filter(|o| match o.to_str() {
|
||||
Ok(os) => origins.contains(os),
|
||||
_ => false,
|
||||
@ -632,7 +632,7 @@ impl Inner {
|
||||
}
|
||||
|
||||
fn validate_allowed_method(&self, req: &RequestHead) -> Result<(), CorsError> {
|
||||
if let Some(hdr) = req.headers().get(header::ACCESS_CONTROL_REQUEST_METHOD) {
|
||||
if let Some(hdr) = req.headers().get(&header::ACCESS_CONTROL_REQUEST_METHOD) {
|
||||
if let Ok(meth) = hdr.to_str() {
|
||||
if let Ok(method) = Method::try_from(meth) {
|
||||
return self
|
||||
@ -653,7 +653,7 @@ impl Inner {
|
||||
AllOrSome::All => Ok(()),
|
||||
AllOrSome::Some(ref allowed_headers) => {
|
||||
if let Some(hdr) =
|
||||
req.headers().get(header::ACCESS_CONTROL_REQUEST_HEADERS)
|
||||
req.headers().get(&header::ACCESS_CONTROL_REQUEST_HEADERS)
|
||||
{
|
||||
if let Ok(headers) = hdr.to_str() {
|
||||
let mut hdrs = HashSet::new();
|
||||
@ -720,7 +720,7 @@ where
|
||||
.unwrap(),
|
||||
)
|
||||
} else if let Some(hdr) =
|
||||
req.headers().get(header::ACCESS_CONTROL_REQUEST_HEADERS)
|
||||
req.headers().get(&header::ACCESS_CONTROL_REQUEST_HEADERS)
|
||||
{
|
||||
Some(hdr.clone())
|
||||
} else {
|
||||
@ -759,7 +759,7 @@ where
|
||||
.into_body();
|
||||
|
||||
Either::A(ok(req.into_response(res)))
|
||||
} else if req.headers().contains_key(header::ORIGIN) {
|
||||
} else if req.headers().contains_key(&header::ORIGIN) {
|
||||
// Only check requests with a origin header.
|
||||
if let Err(e) = self.inner.validate_origin(req.head()) {
|
||||
return Either::A(ok(req.error_response(e)));
|
||||
@ -790,7 +790,7 @@ where
|
||||
}
|
||||
if inner.vary_header {
|
||||
let value =
|
||||
if let Some(hdr) = res.headers_mut().get(header::VARY) {
|
||||
if let Some(hdr) = res.headers_mut().get(&header::VARY) {
|
||||
let mut val: Vec<u8> =
|
||||
Vec::with_capacity(hdr.as_bytes().len() + 8);
|
||||
val.extend(hdr.as_bytes());
|
||||
@ -893,20 +893,20 @@ mod tests {
|
||||
assert_eq!(
|
||||
&b"*"[..],
|
||||
resp.headers()
|
||||
.get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
|
||||
.get(&header::ACCESS_CONTROL_ALLOW_ORIGIN)
|
||||
.unwrap()
|
||||
.as_bytes()
|
||||
);
|
||||
assert_eq!(
|
||||
&b"3600"[..],
|
||||
resp.headers()
|
||||
.get(header::ACCESS_CONTROL_MAX_AGE)
|
||||
.get(&header::ACCESS_CONTROL_MAX_AGE)
|
||||
.unwrap()
|
||||
.as_bytes()
|
||||
);
|
||||
let hdr = resp
|
||||
.headers()
|
||||
.get(header::ACCESS_CONTROL_ALLOW_HEADERS)
|
||||
.get(&header::ACCESS_CONTROL_ALLOW_HEADERS)
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap();
|
||||
|
@ -131,11 +131,11 @@ where
|
||||
// set response headers
|
||||
for (key, value) in inner.headers.iter() {
|
||||
if !res.headers().contains_key(key) {
|
||||
res.headers_mut().insert(key, value.clone());
|
||||
res.headers_mut().insert(key.clone(), value.clone());
|
||||
}
|
||||
}
|
||||
// default content-type
|
||||
if inner.ct && !res.headers().contains_key(CONTENT_TYPE) {
|
||||
if inner.ct && !res.headers().contains_key(&CONTENT_TYPE) {
|
||||
res.headers_mut().insert(
|
||||
CONTENT_TYPE,
|
||||
HeaderValue::from_static("application/octet-stream"),
|
||||
|
@ -14,6 +14,7 @@ use time;
|
||||
|
||||
use crate::dev::{BodySize, MessageBody, ResponseBody};
|
||||
use crate::error::{Error, Result};
|
||||
use crate::http::{HeaderName, HttpTryFrom};
|
||||
use crate::service::{ServiceRequest, ServiceResponse};
|
||||
use crate::HttpResponse;
|
||||
|
||||
@ -288,8 +289,12 @@ impl Format {
|
||||
|
||||
if let Some(key) = cap.get(2) {
|
||||
results.push(match cap.get(3).unwrap().as_str() {
|
||||
"i" => FormatText::RequestHeader(key.as_str().to_owned()),
|
||||
"o" => FormatText::ResponseHeader(key.as_str().to_owned()),
|
||||
"i" => FormatText::RequestHeader(
|
||||
HeaderName::try_from(key.as_str()).unwrap(),
|
||||
),
|
||||
"o" => FormatText::ResponseHeader(
|
||||
HeaderName::try_from(key.as_str()).unwrap(),
|
||||
),
|
||||
"e" => FormatText::EnvironHeader(key.as_str().to_owned()),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
@ -332,8 +337,8 @@ pub enum FormatText {
|
||||
TimeMillis,
|
||||
RemoteAddr,
|
||||
UrlPath,
|
||||
RequestHeader(String),
|
||||
ResponseHeader(String),
|
||||
RequestHeader(HeaderName),
|
||||
ResponseHeader(HeaderName),
|
||||
EnvironHeader(String),
|
||||
}
|
||||
|
||||
|
@ -286,10 +286,10 @@ mod tests {
|
||||
{
|
||||
let cookies = req.cookies().unwrap();
|
||||
assert_eq!(cookies.len(), 2);
|
||||
assert_eq!(cookies[0].name(), "cookie1");
|
||||
assert_eq!(cookies[0].value(), "value1");
|
||||
assert_eq!(cookies[1].name(), "cookie2");
|
||||
assert_eq!(cookies[1].value(), "value2");
|
||||
assert_eq!(cookies[0].name(), "cookie2");
|
||||
assert_eq!(cookies[0].value(), "value2");
|
||||
assert_eq!(cookies[1].name(), "cookie1");
|
||||
assert_eq!(cookies[1].value(), "value1");
|
||||
}
|
||||
|
||||
let cookie = req.cookie("cookie1");
|
||||
|
@ -209,7 +209,7 @@ where
|
||||
};
|
||||
|
||||
let mut len = None;
|
||||
if let Some(l) = req.headers().get(CONTENT_LENGTH) {
|
||||
if let Some(l) = req.headers().get(&CONTENT_LENGTH) {
|
||||
if let Ok(s) = l.to_str() {
|
||||
if let Ok(l) = s.parse::<usize>() {
|
||||
len = Some(l)
|
||||
|
@ -297,7 +297,7 @@ where
|
||||
}
|
||||
|
||||
let mut len = None;
|
||||
if let Some(l) = req.headers().get(CONTENT_LENGTH) {
|
||||
if let Some(l) = req.headers().get(&CONTENT_LENGTH) {
|
||||
if let Ok(s) = l.to_str() {
|
||||
if let Ok(l) = s.parse::<usize>() {
|
||||
len = Some(l)
|
||||
|
@ -313,7 +313,7 @@ where
|
||||
/// Create `MessageBody` for request.
|
||||
pub fn new(req: &mut T) -> HttpMessageBody<T> {
|
||||
let mut len = None;
|
||||
if let Some(l) = req.headers().get(header::CONTENT_LENGTH) {
|
||||
if let Some(l) = req.headers().get(&header::CONTENT_LENGTH) {
|
||||
if let Ok(s) = l.to_str() {
|
||||
if let Ok(l) = s.parse::<usize>() {
|
||||
len = Some(l)
|
||||
|
Loading…
Reference in New Issue
Block a user