1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-06-11 01:39:33 +00:00

use custom headers map; more optimizations

This commit is contained in:
Nikolay Kim 2019-04-06 15:02:02 -07:00
parent 4ef46e26f9
commit 748289f0ff
35 changed files with 668 additions and 180 deletions

View file

@ -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;

View file

@ -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"

View file

@ -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))
})

View file

@ -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 {

View file

@ -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);

View file

@ -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,37 +159,33 @@ 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;
// set response version
res.head_mut().version = self.version;
// connection status
self.ctype = if let Some(ct) = res.head().ctype() {
if ct == ConnectionType::KeepAlive {
self.ctype
} else {
ct
}
} else {
// connection status
self.ctype = if let Some(ct) = res.head().ctype() {
if ct == ConnectionType::KeepAlive {
self.ctype
};
} else {
ct
}
} else {
self.ctype
};
// encode message
let len = dst.len();
self.encoder.encode(
dst,
&mut res,
self.flags.contains(Flags::HEAD),
self.flags.contains(Flags::STREAM),
self.version,
length,
self.ctype,
&self.config,
)?;
self.headers_size = (dst.len() - len) as u32;
}
// encode message
let len = dst.len();
self.encoder.encode(
dst,
&mut res,
self.flags.contains(Flags::HEAD),
self.flags.contains(Flags::STREAM),
self.version,
length,
self.ctype,
&self.config,
)?;
// self.headers_size = (dst.len() - len) as u32;
}
Message::Chunk(Some(bytes)) => {
self.encoder.encode_chunk(bytes.as_ref(), dst)?;

View file

@ -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]

View file

@ -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,31 +119,59 @@ pub(crate) trait MessageType: Sized {
}
_ => (),
}
let v = value.as_ref();
let k = key.as_str().as_bytes();
let len = k.len() + v.len() + 4;
if len > remaining {
unsafe {
dst.advance_mut(pos);
match value {
map::Value::One(ref val) => {
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;
}
pos = 0;
dst.reserve(len);
remaining = dst.remaining_mut();
unsafe {
buf = &mut *(dst.bytes_mut() as *mut _);
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;
}
}
}
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);

View file

@ -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),

View file

@ -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 {

View file

@ -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)

View file

@ -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));
}

View 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
}
}
}

View file

@ -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};

View file

@ -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>>);

View file

@ -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 {

View file

@ -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);
}
}
}

View file

@ -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(())

View file

@ -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");
}

View file

@ -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;

View file

@ -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)

View file

@ -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())
};

View file

@ -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

View file

@ -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"))),

View file

@ -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)

View file

@ -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!(

View file

@ -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());

View file

@ -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 {

View file

@ -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();

View file

@ -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"),

View file

@ -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),
}

View file

@ -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");

View file

@ -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)

View file

@ -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)

View file

@ -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)