mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-27 17:22:57 +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
|
// check last modified
|
||||||
let not_modified = if !none_match(etag.as_ref(), req) {
|
let not_modified = if !none_match(etag.as_ref(), req) {
|
||||||
true
|
true
|
||||||
} else if req.headers().contains_key(header::IF_NONE_MATCH) {
|
} else if req.headers().contains_key(&header::IF_NONE_MATCH) {
|
||||||
false
|
false
|
||||||
} else if let (Some(ref m), Some(header::IfModifiedSince(ref since))) =
|
} else if let (Some(ref m), Some(header::IfModifiedSince(ref since))) =
|
||||||
(last_modified, req.get_header())
|
(last_modified, req.get_header())
|
||||||
@ -378,7 +378,7 @@ impl Responder for NamedFile {
|
|||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
// check for range header
|
// 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(rangesheader) = ranges.to_str() {
|
||||||
if let Ok(rangesvec) = HttpRange::parse(rangesheader, length) {
|
if let Ok(rangesvec) = HttpRange::parse(rangesheader, length) {
|
||||||
length = rangesvec[0].length;
|
length = rangesvec[0].length;
|
||||||
|
@ -60,6 +60,7 @@ bitflags = "1.0"
|
|||||||
bytes = "0.4"
|
bytes = "0.4"
|
||||||
byteorder = "1.2"
|
byteorder = "1.2"
|
||||||
derive_more = "0.14"
|
derive_more = "0.14"
|
||||||
|
either = "1.5.2"
|
||||||
encoding = "0.2"
|
encoding = "0.2"
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
hashbrown = "0.1.8"
|
hashbrown = "0.1.8"
|
||||||
|
@ -107,7 +107,7 @@ where
|
|||||||
let mut head = ResponseHead::default();
|
let mut head = ResponseHead::default();
|
||||||
head.version = parts.version;
|
head.version = parts.version;
|
||||||
head.status = parts.status;
|
head.status = parts.status;
|
||||||
head.headers = parts.headers;
|
head.headers = parts.headers.into();
|
||||||
|
|
||||||
Ok((head, payload))
|
Ok((head, payload))
|
||||||
})
|
})
|
||||||
|
@ -55,7 +55,7 @@ where
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_headers(stream: S, headers: &HeaderMap) -> Decoder<S> {
|
pub fn from_headers(stream: S, headers: &HeaderMap) -> Decoder<S> {
|
||||||
// check content-encoding
|
// 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() {
|
if let Ok(enc) = enc.to_str() {
|
||||||
ContentEncoding::from(enc)
|
ContentEncoding::from(enc)
|
||||||
} else {
|
} else {
|
||||||
|
@ -31,7 +31,7 @@ impl<B: MessageBody> Encoder<B> {
|
|||||||
head: &mut ResponseHead,
|
head: &mut ResponseHead,
|
||||||
body: ResponseBody<B>,
|
body: ResponseBody<B>,
|
||||||
) -> ResponseBody<Encoder<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
|
|| head.status == StatusCode::SWITCHING_PROTOCOLS
|
||||||
|| encoding == ContentEncoding::Identity
|
|| encoding == ContentEncoding::Identity
|
||||||
|| encoding == ContentEncoding::Auto);
|
|| encoding == ContentEncoding::Auto);
|
||||||
|
@ -22,8 +22,8 @@ use crate::response::Response;
|
|||||||
bitflags! {
|
bitflags! {
|
||||||
struct Flags: u8 {
|
struct Flags: u8 {
|
||||||
const HEAD = 0b0000_0001;
|
const HEAD = 0b0000_0001;
|
||||||
const KEEPALIVE_ENABLED = 0b0000_1000;
|
const KEEPALIVE_ENABLED = 0b0000_0010;
|
||||||
const STREAM = 0b0001_0000;
|
const STREAM = 0b0000_0100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,8 +39,8 @@ pub struct Codec {
|
|||||||
|
|
||||||
// encoder part
|
// encoder part
|
||||||
flags: Flags,
|
flags: Flags,
|
||||||
headers_size: u32,
|
|
||||||
encoder: encoder::MessageEncoder<Response<()>>,
|
encoder: encoder::MessageEncoder<Response<()>>,
|
||||||
|
// headers_size: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Codec {
|
impl Default for Codec {
|
||||||
@ -73,7 +73,7 @@ impl Codec {
|
|||||||
ctype: ConnectionType::Close,
|
ctype: ConnectionType::Close,
|
||||||
|
|
||||||
flags,
|
flags,
|
||||||
headers_size: 0,
|
// headers_size: 0,
|
||||||
encoder: encoder::MessageEncoder::default(),
|
encoder: encoder::MessageEncoder::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,9 +159,6 @@ impl Encoder for Codec {
|
|||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
match item {
|
match item {
|
||||||
Message::Item((mut res, length)) => {
|
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
|
// set response version
|
||||||
res.head_mut().version = self.version;
|
res.head_mut().version = self.version;
|
||||||
|
|
||||||
@ -188,8 +185,7 @@ impl Encoder for Codec {
|
|||||||
self.ctype,
|
self.ctype,
|
||||||
&self.config,
|
&self.config,
|
||||||
)?;
|
)?;
|
||||||
self.headers_size = (dst.len() - len) as u32;
|
// self.headers_size = (dst.len() - len) as u32;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Message::Chunk(Some(bytes)) => {
|
Message::Chunk(Some(bytes)) => {
|
||||||
self.encoder.encode_chunk(bytes.as_ref(), dst)?;
|
self.encoder.encode_chunk(bytes.as_ref(), dst)?;
|
||||||
|
@ -5,11 +5,12 @@ use actix_codec::Decoder;
|
|||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use futures::{Async, Poll};
|
use futures::{Async, Poll};
|
||||||
use http::header::{HeaderName, HeaderValue};
|
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 httparse;
|
||||||
use log::{debug, error, trace};
|
use log::{debug, error, trace};
|
||||||
|
|
||||||
use crate::error::ParseError;
|
use crate::error::ParseError;
|
||||||
|
use crate::header::HeaderMap;
|
||||||
use crate::message::{ConnectionType, ResponseHead};
|
use crate::message::{ConnectionType, ResponseHead};
|
||||||
use crate::request::Request;
|
use crate::request::Request;
|
||||||
|
|
||||||
@ -630,6 +631,7 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::error::ParseError;
|
use crate::error::ParseError;
|
||||||
|
use crate::http::header::{HeaderName, SET_COOKIE};
|
||||||
use crate::httpmessage::HttpMessage;
|
use crate::httpmessage::HttpMessage;
|
||||||
|
|
||||||
impl PayloadType {
|
impl PayloadType {
|
||||||
@ -790,7 +792,13 @@ mod tests {
|
|||||||
assert_eq!(req.version(), Version::HTTP_11);
|
assert_eq!(req.version(), Version::HTTP_11);
|
||||||
assert_eq!(*req.method(), Method::GET);
|
assert_eq!(*req.method(), Method::GET);
|
||||||
assert_eq!(req.path(), "/test");
|
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]
|
#[test]
|
||||||
@ -805,12 +813,11 @@ mod tests {
|
|||||||
|
|
||||||
let val: Vec<_> = req
|
let val: Vec<_> = req
|
||||||
.headers()
|
.headers()
|
||||||
.get_all("Set-Cookie")
|
.get_all(SET_COOKIE)
|
||||||
.iter()
|
|
||||||
.map(|v| v.to_str().unwrap().to_owned())
|
.map(|v| v.to_str().unwrap().to_owned())
|
||||||
.collect();
|
.collect();
|
||||||
assert_eq!(val[0], "c1=cookie1");
|
assert_eq!(val[1], "c1=cookie1");
|
||||||
assert_eq!(val[1], "c2=cookie2");
|
assert_eq!(val[0], "c2=cookie2");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -6,15 +6,16 @@ use std::str::FromStr;
|
|||||||
use std::{cmp, fmt, io, mem};
|
use std::{cmp, fmt, io, mem};
|
||||||
|
|
||||||
use bytes::{BufMut, Bytes, BytesMut};
|
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::body::BodySize;
|
||||||
use crate::config::ServiceConfig;
|
use crate::config::ServiceConfig;
|
||||||
|
use crate::header::map;
|
||||||
use crate::header::ContentEncoding;
|
use crate::header::ContentEncoding;
|
||||||
use crate::helpers;
|
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::message::{ConnectionType, Head, RequestHead, ResponseHead};
|
||||||
use crate::request::Request;
|
use crate::request::Request;
|
||||||
use crate::response::Response;
|
use crate::response::Response;
|
||||||
@ -109,7 +110,7 @@ pub(crate) trait MessageType: Sized {
|
|||||||
let mut has_date = false;
|
let mut has_date = false;
|
||||||
let mut remaining = dst.remaining_mut();
|
let mut remaining = dst.remaining_mut();
|
||||||
let mut buf = unsafe { &mut *(dst.bytes_mut() as *mut [u8]) };
|
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 {
|
match *key {
|
||||||
CONNECTION => continue,
|
CONNECTION => continue,
|
||||||
TRANSFER_ENCODING | CONTENT_LENGTH if skip_len => 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();
|
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;
|
let len = k.len() + v.len() + 4;
|
||||||
if len > remaining {
|
if len > remaining {
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -133,7 +135,6 @@ pub(crate) trait MessageType: Sized {
|
|||||||
buf = &mut *(dst.bytes_mut() as *mut _);
|
buf = &mut *(dst.bytes_mut() as *mut _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf[pos..pos + k.len()].copy_from_slice(k);
|
buf[pos..pos + k.len()].copy_from_slice(k);
|
||||||
pos += k.len();
|
pos += k.len();
|
||||||
buf[pos..pos + 2].copy_from_slice(b": ");
|
buf[pos..pos + 2].copy_from_slice(b": ");
|
||||||
@ -144,6 +145,34 @@ pub(crate) trait MessageType: Sized {
|
|||||||
pos += 2;
|
pos += 2;
|
||||||
remaining -= len;
|
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 {
|
unsafe {
|
||||||
dst.advance_mut(pos);
|
dst.advance_mut(pos);
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ where
|
|||||||
head.uri = parts.uri;
|
head.uri = parts.uri;
|
||||||
head.method = parts.method;
|
head.method = parts.method;
|
||||||
head.version = parts.version;
|
head.version = parts.version;
|
||||||
head.headers = parts.headers;
|
head.headers = parts.headers.into();
|
||||||
tokio_current_thread::spawn(ServiceResponse::<S::Future, B> {
|
tokio_current_thread::spawn(ServiceResponse::<S::Future, B> {
|
||||||
state: ServiceResponseState::ServiceCall(
|
state: ServiceResponseState::ServiceCall(
|
||||||
self.service.call(req),
|
self.service.call(req),
|
||||||
|
@ -64,7 +64,7 @@ impl Header for CacheControl {
|
|||||||
where
|
where
|
||||||
T: crate::HttpMessage,
|
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() {
|
if !directives.is_empty() {
|
||||||
Ok(CacheControl(directives))
|
Ok(CacheControl(directives))
|
||||||
} else {
|
} else {
|
||||||
|
@ -444,7 +444,7 @@ impl Header for ContentDisposition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse<T: crate::HttpMessage>(msg: &T) -> Result<Self, crate::error::ParseError> {
|
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)
|
Self::from_raw(&h)
|
||||||
} else {
|
} else {
|
||||||
Err(crate::error::ParseError::Header)
|
Err(crate::error::ParseError::Header)
|
||||||
|
@ -73,12 +73,12 @@ impl Header for IfRange {
|
|||||||
T: HttpMessage,
|
T: HttpMessage,
|
||||||
{
|
{
|
||||||
let etag: Result<EntityTag, _> =
|
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 {
|
if let Ok(etag) = etag {
|
||||||
return Ok(IfRange::EntityTag(etag));
|
return Ok(IfRange::EntityTag(etag));
|
||||||
}
|
}
|
||||||
let date: Result<HttpDate, _> =
|
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 {
|
if let Ok(date) = date {
|
||||||
return Ok(IfRange::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 std::{fmt, str::FromStr};
|
||||||
|
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use http::header::GetAll;
|
|
||||||
use http::Error as HttpError;
|
use http::Error as HttpError;
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
|
|
||||||
@ -14,12 +13,17 @@ use crate::error::ParseError;
|
|||||||
use crate::httpmessage::HttpMessage;
|
use crate::httpmessage::HttpMessage;
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
|
pub(crate) mod map;
|
||||||
mod shared;
|
mod shared;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub use self::common::*;
|
pub use self::common::*;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub use self::shared::*;
|
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.
|
/// A trait for any object that will represent a header field and value.
|
||||||
pub trait Header
|
pub trait Header
|
||||||
where
|
where
|
||||||
@ -220,8 +224,8 @@ impl fmt::Write for Writer {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
/// Reads a comma-delimited raw header into a Vec.
|
/// Reads a comma-delimited raw header into a Vec.
|
||||||
pub fn from_comma_delimited<T: FromStr>(
|
pub fn from_comma_delimited<'a, I: Iterator<Item = &'a HeaderValue> + 'a, T: FromStr>(
|
||||||
all: GetAll<HeaderValue>,
|
all: I,
|
||||||
) -> Result<Vec<T>, ParseError> {
|
) -> Result<Vec<T>, ParseError> {
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
for h in all {
|
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)
|
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 {
|
mod percent_encoding_http {
|
||||||
use percent_encoding::{self, define_encode_set};
|
use percent_encoding::{self, define_encode_set};
|
||||||
|
|
||||||
|
@ -4,13 +4,13 @@ use std::str;
|
|||||||
use encoding::all::UTF_8;
|
use encoding::all::UTF_8;
|
||||||
use encoding::label::encoding_from_whatwg_label;
|
use encoding::label::encoding_from_whatwg_label;
|
||||||
use encoding::EncodingRef;
|
use encoding::EncodingRef;
|
||||||
use http::{header, HeaderMap};
|
use http::header;
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
|
|
||||||
use crate::cookie::Cookie;
|
use crate::cookie::Cookie;
|
||||||
use crate::error::{ContentTypeError, CookieParseError, ParseError};
|
use crate::error::{ContentTypeError, CookieParseError, ParseError};
|
||||||
use crate::extensions::Extensions;
|
use crate::extensions::Extensions;
|
||||||
use crate::header::Header;
|
use crate::header::{Header, HeaderMap};
|
||||||
use crate::payload::Payload;
|
use crate::payload::Payload;
|
||||||
|
|
||||||
struct Cookies(Vec<Cookie<'static>>);
|
struct Cookies(Vec<Cookie<'static>>);
|
||||||
|
@ -45,10 +45,11 @@ pub mod http {
|
|||||||
// re-exports
|
// re-exports
|
||||||
pub use http::header::{HeaderName, HeaderValue};
|
pub use http::header::{HeaderName, HeaderValue};
|
||||||
pub use http::uri::PathAndQuery;
|
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 http::{Method, StatusCode, Version};
|
||||||
|
|
||||||
pub use crate::cookie::{Cookie, CookieBuilder};
|
pub use crate::cookie::{Cookie, CookieBuilder};
|
||||||
|
pub use crate::header::HeaderMap;
|
||||||
|
|
||||||
/// Various http headers
|
/// Various http headers
|
||||||
pub mod header {
|
pub mod header {
|
||||||
|
@ -5,7 +5,8 @@ use std::rc::Rc;
|
|||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
|
||||||
use crate::extensions::Extensions;
|
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
|
/// Represents various types of connection
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
@ -174,7 +175,7 @@ impl Default for ResponseHead {
|
|||||||
ResponseHead {
|
ResponseHead {
|
||||||
version: Version::default(),
|
version: Version::default(),
|
||||||
status: StatusCode::OK,
|
status: StatusCode::OK,
|
||||||
headers: HeaderMap::with_capacity(16),
|
headers: HeaderMap::with_capacity(12),
|
||||||
reason: None,
|
reason: None,
|
||||||
flags: Flags::empty(),
|
flags: Flags::empty(),
|
||||||
extensions: RefCell::new(Extensions::new()),
|
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 {
|
impl ResponseHead {
|
||||||
/// Message extensions
|
/// Message extensions
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -300,7 +289,6 @@ impl ResponseHead {
|
|||||||
|
|
||||||
pub struct Message<T: Head> {
|
pub struct Message<T: Head> {
|
||||||
head: Rc<T>,
|
head: Rc<T>,
|
||||||
pool: &'static MessagePool<T>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Head> Message<T> {
|
impl<T: Head> Message<T> {
|
||||||
@ -314,7 +302,6 @@ impl<T: Head> Clone for Message<T> {
|
|||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Message {
|
Message {
|
||||||
head: self.head.clone(),
|
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> {
|
impl<T: Head> Drop for Message<T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if Rc::strong_count(&self.head) == 1 {
|
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)]
|
#[doc(hidden)]
|
||||||
/// Request's objects pool
|
/// Request's objects pool
|
||||||
pub struct MessagePool<T: Head>(RefCell<VecDeque<Rc<T>>>);
|
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 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> {
|
impl<T: Head> MessagePool<T> {
|
||||||
fn create() -> &'static 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) {
|
if let Some(r) = Rc::get_mut(&mut msg) {
|
||||||
r.clear();
|
r.clear();
|
||||||
}
|
}
|
||||||
Message {
|
Message { head: msg }
|
||||||
head: msg,
|
|
||||||
pool: self,
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Message {
|
Message {
|
||||||
head: Rc::new(T::default()),
|
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::cell::{Ref, RefMut};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use http::{header, HeaderMap, Method, Uri, Version};
|
use http::{header, Method, Uri, Version};
|
||||||
|
|
||||||
use crate::extensions::Extensions;
|
use crate::extensions::Extensions;
|
||||||
|
use crate::header::HeaderMap;
|
||||||
use crate::httpmessage::HttpMessage;
|
use crate::httpmessage::HttpMessage;
|
||||||
use crate::message::{Message, RequestHead};
|
use crate::message::{Message, RequestHead};
|
||||||
use crate::payload::{Payload, PayloadStream};
|
use crate::payload::{Payload, PayloadStream};
|
||||||
@ -161,7 +162,7 @@ impl<P> fmt::Debug for Request<P> {
|
|||||||
writeln!(f, " query: ?{:?}", q)?;
|
writeln!(f, " query: ?{:?}", q)?;
|
||||||
}
|
}
|
||||||
writeln!(f, " headers:")?;
|
writeln!(f, " headers:")?;
|
||||||
for (key, val) in self.headers().iter() {
|
for (key, val) in self.headers() {
|
||||||
writeln!(f, " {:?}: {:?}", key, val)?;
|
writeln!(f, " {:?}: {:?}", key, val)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -6,8 +6,6 @@ use std::{fmt, io, str};
|
|||||||
use bytes::{BufMut, Bytes, BytesMut};
|
use bytes::{BufMut, Bytes, BytesMut};
|
||||||
use futures::future::{ok, FutureResult, IntoFuture};
|
use futures::future::{ok, FutureResult, IntoFuture};
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
use http::header::{self, HeaderName, HeaderValue};
|
|
||||||
use http::{Error as HttpError, HeaderMap, HttpTryFrom, StatusCode};
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
|
||||||
@ -16,11 +14,13 @@ use crate::cookie::{Cookie, CookieJar};
|
|||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::extensions::Extensions;
|
use crate::extensions::Extensions;
|
||||||
use crate::header::{Header, IntoHeaderValue};
|
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
|
/// An HTTP Response
|
||||||
pub struct Response<B = Body> {
|
pub struct Response<B = Body> {
|
||||||
head: Message<ResponseHead>,
|
head: BoxedResponseHead,
|
||||||
body: ResponseBody<B>,
|
body: ResponseBody<B>,
|
||||||
error: Option<Error>,
|
error: Option<Error>,
|
||||||
}
|
}
|
||||||
@ -41,7 +41,7 @@ impl Response<Body> {
|
|||||||
/// Constructs a response
|
/// Constructs a response
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(status: StatusCode) -> Response {
|
pub fn new(status: StatusCode) -> Response {
|
||||||
let mut head: Message<ResponseHead> = Message::new();
|
let mut head = BoxedResponseHead::new();
|
||||||
head.status = status;
|
head.status = status;
|
||||||
|
|
||||||
Response {
|
Response {
|
||||||
@ -93,7 +93,7 @@ impl<B> Response<B> {
|
|||||||
/// Constructs a response with body
|
/// Constructs a response with body
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_body(status: StatusCode, body: B) -> Response<B> {
|
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;
|
head.status = status;
|
||||||
Response {
|
Response {
|
||||||
head,
|
head,
|
||||||
@ -136,7 +136,7 @@ impl<B> Response<B> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn cookies(&self) -> CookieIter {
|
pub fn cookies(&self) -> CookieIter {
|
||||||
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 h = &mut self.head.headers;
|
||||||
let vals: Vec<HeaderValue> = h
|
let vals: Vec<HeaderValue> = h
|
||||||
.get_all(header::SET_COOKIE)
|
.get_all(header::SET_COOKIE)
|
||||||
.iter()
|
|
||||||
.map(|v| v.to_owned())
|
.map(|v| v.to_owned())
|
||||||
.collect();
|
.collect();
|
||||||
h.remove(header::SET_COOKIE);
|
h.remove(header::SET_COOKIE);
|
||||||
@ -286,7 +285,7 @@ impl IntoFuture for Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct CookieIter<'a> {
|
pub struct CookieIter<'a> {
|
||||||
iter: header::ValueIter<'a, HeaderValue>,
|
iter: header::GetAll<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for CookieIter<'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
|
/// This type can be used to construct an instance of `Response` through a
|
||||||
/// builder-like pattern.
|
/// builder-like pattern.
|
||||||
pub struct ResponseBuilder {
|
pub struct ResponseBuilder {
|
||||||
head: Option<Message<ResponseHead>>,
|
head: Option<BoxedResponseHead>,
|
||||||
err: Option<HttpError>,
|
err: Option<HttpError>,
|
||||||
cookies: Option<CookieJar>,
|
cookies: Option<CookieJar>,
|
||||||
}
|
}
|
||||||
@ -328,7 +327,7 @@ pub struct ResponseBuilder {
|
|||||||
impl ResponseBuilder {
|
impl ResponseBuilder {
|
||||||
/// Create response builder
|
/// Create response builder
|
||||||
pub fn new(status: StatusCode) -> Self {
|
pub fn new(status: StatusCode) -> Self {
|
||||||
let mut head: Message<ResponseHead> = Message::new();
|
let mut head = BoxedResponseHead::new();
|
||||||
head.status = status;
|
head.status = status;
|
||||||
|
|
||||||
ResponseBuilder {
|
ResponseBuilder {
|
||||||
@ -701,13 +700,13 @@ impl ResponseBuilder {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parts<'a>(
|
fn parts<'a>(
|
||||||
parts: &'a mut Option<Message<ResponseHead>>,
|
parts: &'a mut Option<BoxedResponseHead>,
|
||||||
err: &Option<HttpError>,
|
err: &Option<HttpError>,
|
||||||
) -> Option<&'a mut Message<ResponseHead>> {
|
) -> Option<&'a mut ResponseHead> {
|
||||||
if err.is_some() {
|
if err.is_some() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
parts.as_mut()
|
parts.as_mut().map(|r| &mut **r)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert `Response` to a `ResponseBuilder`. Body get dropped.
|
/// 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 mut jar: Option<CookieJar> = None;
|
||||||
|
|
||||||
let cookies = CookieIter {
|
let cookies = CookieIter {
|
||||||
iter: head.headers.get_all(header::SET_COOKIE).iter(),
|
iter: head.headers.get_all(header::SET_COOKIE),
|
||||||
};
|
};
|
||||||
for c in cookies {
|
for c in cookies {
|
||||||
if let Some(ref mut j) = jar {
|
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.version = head.version;
|
||||||
msg.status = head.status;
|
msg.status = head.status;
|
||||||
msg.reason = head.reason;
|
msg.reason = head.reason;
|
||||||
msg.headers = head.headers.clone();
|
// msg.headers = head.headers.clone();
|
||||||
msg.no_chunking(!head.chunked());
|
msg.no_chunking(!head.chunked());
|
||||||
|
|
||||||
ResponseBuilder {
|
ResponseBuilder {
|
||||||
@ -845,7 +844,7 @@ impl From<BytesMut> for Response {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::body::Body;
|
use crate::body::Body;
|
||||||
use crate::http::header::{HeaderValue, CONTENT_TYPE, COOKIE};
|
use crate::http::header::{HeaderValue, CONTENT_TYPE, COOKIE, SET_COOKIE};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_debug() {
|
fn test_debug() {
|
||||||
@ -876,13 +875,12 @@ mod tests {
|
|||||||
.max_age(time::Duration::days(1))
|
.max_age(time::Duration::days(1))
|
||||||
.finish(),
|
.finish(),
|
||||||
)
|
)
|
||||||
.del_cookie(&cookies[0])
|
.del_cookie(&cookies[1])
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
let mut val: Vec<_> = resp
|
let mut val: Vec<_> = resp
|
||||||
.headers()
|
.headers()
|
||||||
.get_all("Set-Cookie")
|
.get_all(SET_COOKIE)
|
||||||
.iter()
|
|
||||||
.map(|v| v.to_str().unwrap().to_owned())
|
.map(|v| v.to_str().unwrap().to_owned())
|
||||||
.collect();
|
.collect();
|
||||||
val.sort();
|
val.sort();
|
||||||
@ -911,9 +909,9 @@ mod tests {
|
|||||||
|
|
||||||
let mut iter = r.cookies();
|
let mut iter = r.cookies();
|
||||||
let v = iter.next().unwrap();
|
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"));
|
assert_eq!((v.name(), v.value()), ("cookie3", "val300"));
|
||||||
|
let v = iter.next().unwrap();
|
||||||
|
assert_eq!((v.name(), v.value()), ("original", "val100"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1049,6 +1047,7 @@ mod tests {
|
|||||||
resp.headers().get(CONTENT_TYPE).unwrap(),
|
resp.headers().get(CONTENT_TYPE).unwrap(),
|
||||||
HeaderValue::from_static("application/octet-stream")
|
HeaderValue::from_static("application/octet-stream")
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
assert_eq!(resp.body().get_ref(), b"test");
|
assert_eq!(resp.body().get_ref(), b"test");
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,11 @@ use std::str::FromStr;
|
|||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use http::header::{self, HeaderName, HeaderValue};
|
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 percent_encoding::{percent_encode, USERINFO_ENCODE_SET};
|
||||||
|
|
||||||
use crate::cookie::{Cookie, CookieJar};
|
use crate::cookie::{Cookie, CookieJar};
|
||||||
|
use crate::header::HeaderMap;
|
||||||
use crate::header::{Header, IntoHeaderValue};
|
use crate::header::{Header, IntoHeaderValue};
|
||||||
use crate::payload::Payload;
|
use crate::payload::Payload;
|
||||||
use crate::Request;
|
use crate::Request;
|
||||||
|
@ -92,7 +92,7 @@ impl Multipart {
|
|||||||
|
|
||||||
/// Extract boundary info from headers.
|
/// Extract boundary info from headers.
|
||||||
fn boundary(headers: &HeaderMap) -> Result<String, MultipartError> {
|
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(content_type) = content_type.to_str() {
|
||||||
if let Ok(ct) = content_type.parse::<mime::Mime>() {
|
if let Ok(ct) = content_type.parse::<mime::Mime>() {
|
||||||
if let Some(boundary) = ct.get_param(mime::BOUNDARY) {
|
if let Some(boundary) = ct.get_param(mime::BOUNDARY) {
|
||||||
@ -334,7 +334,7 @@ impl InnerMultipart {
|
|||||||
|
|
||||||
// content type
|
// content type
|
||||||
let mut mt = mime::APPLICATION_OCTET_STREAM;
|
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(content_type) = content_type.to_str() {
|
||||||
if let Ok(ct) = content_type.parse::<mime::Mime>() {
|
if let Ok(ct) = content_type.parse::<mime::Mime>() {
|
||||||
mt = ct;
|
mt = ct;
|
||||||
@ -427,7 +427,7 @@ impl Field {
|
|||||||
pub fn content_disposition(&self) -> Option<ContentDisposition> {
|
pub fn content_disposition(&self) -> Option<ContentDisposition> {
|
||||||
// RFC 7578: 'Each part MUST contain a Content-Disposition header field
|
// RFC 7578: 'Each part MUST contain a Content-Disposition header field
|
||||||
// where the disposition type is "form-data".'
|
// 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()
|
ContentDisposition::from_raw(content_disposition).ok()
|
||||||
} else {
|
} else {
|
||||||
@ -480,7 +480,7 @@ impl InnerField {
|
|||||||
boundary: String,
|
boundary: String,
|
||||||
headers: &HeaderMap,
|
headers: &HeaderMap,
|
||||||
) -> Result<InnerField, PayloadError> {
|
) -> 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(s) = len.to_str() {
|
||||||
if let Ok(len) = s.parse::<u64>() {
|
if let Ok(len) = s.parse::<u64>() {
|
||||||
Some(len)
|
Some(len)
|
||||||
|
@ -50,7 +50,7 @@ pub fn handshake(req: &HttpRequest) -> Result<HttpResponseBuilder, HandshakeErro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for "UPGRADE" to websocket header
|
// 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() {
|
if let Ok(s) = hdr.to_str() {
|
||||||
s.to_ascii_lowercase().contains("websocket")
|
s.to_ascii_lowercase().contains("websocket")
|
||||||
} else {
|
} else {
|
||||||
@ -69,11 +69,11 @@ pub fn handshake(req: &HttpRequest) -> Result<HttpResponseBuilder, HandshakeErro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check supported version
|
// 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);
|
return Err(HandshakeError::NoVersionHeader);
|
||||||
}
|
}
|
||||||
let supported_ver = {
|
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"
|
hdr == "13" || hdr == "8" || hdr == "7"
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@ -84,11 +84,11 @@ pub fn handshake(req: &HttpRequest) -> Result<HttpResponseBuilder, HandshakeErro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check client handshake for validity
|
// 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);
|
return Err(HandshakeError::BadWebsocketKey);
|
||||||
}
|
}
|
||||||
let key = {
|
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())
|
hash_key(key.as_ref())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ impl Client {
|
|||||||
{
|
{
|
||||||
let mut req = ClientRequest::new(method, url, self.0.clone());
|
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 = req.set_header_if_none(key.clone(), value.clone());
|
||||||
}
|
}
|
||||||
req
|
req
|
||||||
@ -119,7 +119,7 @@ impl Client {
|
|||||||
Uri: HttpTryFrom<U>,
|
Uri: HttpTryFrom<U>,
|
||||||
{
|
{
|
||||||
let mut req = self.request(head.method.clone(), url);
|
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 = req.set_header_if_none(key.clone(), value.clone());
|
||||||
}
|
}
|
||||||
req
|
req
|
||||||
@ -187,7 +187,7 @@ impl Client {
|
|||||||
Uri: HttpTryFrom<U>,
|
Uri: HttpTryFrom<U>,
|
||||||
{
|
{
|
||||||
let mut req = ws::WebsocketsRequest::new(url, self.0.clone());
|
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.head.headers.insert(key.clone(), value.clone());
|
||||||
}
|
}
|
||||||
req
|
req
|
||||||
|
@ -396,7 +396,7 @@ impl ClientRequest {
|
|||||||
if self.default_headers {
|
if self.default_headers {
|
||||||
// set request host header
|
// set request host header
|
||||||
if let Some(host) = self.head.uri.host() {
|
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 mut wrt = BytesMut::with_capacity(host.len() + 5).writer();
|
||||||
|
|
||||||
let _ = match self.head.uri.port_u16() {
|
let _ = match self.head.uri.port_u16() {
|
||||||
@ -414,7 +414,7 @@ impl ClientRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// user agent
|
// user agent
|
||||||
if !self.head.headers.contains_key(header::USER_AGENT) {
|
if !self.head.headers.contains_key(&header::USER_AGENT) {
|
||||||
self.head.headers.insert(
|
self.head.headers.insert(
|
||||||
header::USER_AGENT,
|
header::USER_AGENT,
|
||||||
HeaderValue::from_static(concat!("awc/", env!("CARGO_PKG_VERSION"))),
|
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() {
|
if self.extensions().get::<Cookies>().is_none() {
|
||||||
let mut cookies = Vec::new();
|
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())
|
let s = std::str::from_utf8(hdr.as_bytes())
|
||||||
.map_err(CookieParseError::from)?;
|
.map_err(CookieParseError::from)?;
|
||||||
cookies.push(Cookie::parse_encoded(s)?.into_owned());
|
cookies.push(Cookie::parse_encoded(s)?.into_owned());
|
||||||
@ -160,7 +160,7 @@ where
|
|||||||
/// Create `MessageBody` for request.
|
/// Create `MessageBody` for request.
|
||||||
pub fn new(res: &mut ClientResponse<S>) -> MessageBody<S> {
|
pub fn new(res: &mut ClientResponse<S>) -> MessageBody<S> {
|
||||||
let mut len = None;
|
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(s) = l.to_str() {
|
||||||
if let Ok(l) = s.parse::<usize>() {
|
if let Ok(l) = s.parse::<usize>() {
|
||||||
len = Some(l)
|
len = Some(l)
|
||||||
@ -254,7 +254,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut len = None;
|
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(s) = l.to_str() {
|
||||||
if let Ok(l) = s.parse::<usize>() {
|
if let Ok(l) = s.parse::<usize>() {
|
||||||
len = Some(l)
|
len = Some(l)
|
||||||
|
@ -236,7 +236,7 @@ impl WebsocketsRequest {
|
|||||||
let mut slf = if self.default_headers {
|
let mut slf = if self.default_headers {
|
||||||
// set request host header
|
// set request host header
|
||||||
if let Some(host) = self.head.uri.host() {
|
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 mut wrt = BytesMut::with_capacity(host.len() + 5).writer();
|
||||||
|
|
||||||
let _ = match self.head.uri.port_u16() {
|
let _ = match self.head.uri.port_u16() {
|
||||||
@ -324,7 +324,7 @@ impl WebsocketsRequest {
|
|||||||
return Err(WsClientError::InvalidResponseStatus(head.status));
|
return Err(WsClientError::InvalidResponseStatus(head.status));
|
||||||
}
|
}
|
||||||
// Check for "UPGRADE" to websocket header
|
// 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() {
|
if let Ok(s) = hdr.to_str() {
|
||||||
s.to_ascii_lowercase().contains("websocket")
|
s.to_ascii_lowercase().contains("websocket")
|
||||||
} else {
|
} else {
|
||||||
@ -338,7 +338,7 @@ impl WebsocketsRequest {
|
|||||||
return Err(WsClientError::InvalidUpgradeHeader);
|
return Err(WsClientError::InvalidUpgradeHeader);
|
||||||
}
|
}
|
||||||
// Check for "CONNECTION" header
|
// 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 let Ok(s) = conn.to_str() {
|
||||||
if !s.to_ascii_lowercase().contains("upgrade") {
|
if !s.to_ascii_lowercase().contains("upgrade") {
|
||||||
log::trace!("Invalid connection header: {}", s);
|
log::trace!("Invalid connection header: {}", s);
|
||||||
@ -355,7 +355,7 @@ impl WebsocketsRequest {
|
|||||||
return Err(WsClientError::MissingConnectionHeader);
|
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());
|
let encoded = ws::hash_key(key.as_ref());
|
||||||
if hdr_key.as_bytes() != encoded.as_bytes() {
|
if hdr_key.as_bytes() != encoded.as_bytes() {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
|
10
src/info.rs
10
src/info.rs
@ -33,7 +33,7 @@ impl ConnectionInfo {
|
|||||||
let peer = None;
|
let peer = None;
|
||||||
|
|
||||||
// load forwarded header
|
// 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() {
|
if let Ok(val) = hdr.to_str() {
|
||||||
for pair in val.split(';') {
|
for pair in val.split(';') {
|
||||||
for el in pair.split(',') {
|
for el in pair.split(',') {
|
||||||
@ -69,7 +69,7 @@ impl ConnectionInfo {
|
|||||||
if scheme.is_none() {
|
if scheme.is_none() {
|
||||||
if let Some(h) = req
|
if let Some(h) = req
|
||||||
.headers
|
.headers
|
||||||
.get(HeaderName::from_lowercase(X_FORWARDED_PROTO).unwrap())
|
.get(&HeaderName::from_lowercase(X_FORWARDED_PROTO).unwrap())
|
||||||
{
|
{
|
||||||
if let Ok(h) = h.to_str() {
|
if let Ok(h) = h.to_str() {
|
||||||
scheme = h.split(',').next().map(|v| v.trim());
|
scheme = h.split(',').next().map(|v| v.trim());
|
||||||
@ -87,14 +87,14 @@ impl ConnectionInfo {
|
|||||||
if host.is_none() {
|
if host.is_none() {
|
||||||
if let Some(h) = req
|
if let Some(h) = req
|
||||||
.headers
|
.headers
|
||||||
.get(HeaderName::from_lowercase(X_FORWARDED_HOST).unwrap())
|
.get(&HeaderName::from_lowercase(X_FORWARDED_HOST).unwrap())
|
||||||
{
|
{
|
||||||
if let Ok(h) = h.to_str() {
|
if let Ok(h) = h.to_str() {
|
||||||
host = h.split(',').next().map(|v| v.trim());
|
host = h.split(',').next().map(|v| v.trim());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if host.is_none() {
|
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();
|
host = h.to_str().ok();
|
||||||
}
|
}
|
||||||
if host.is_none() {
|
if host.is_none() {
|
||||||
@ -110,7 +110,7 @@ impl ConnectionInfo {
|
|||||||
if remote.is_none() {
|
if remote.is_none() {
|
||||||
if let Some(h) = req
|
if let Some(h) = req
|
||||||
.headers
|
.headers
|
||||||
.get(HeaderName::from_lowercase(X_FORWARDED_FOR).unwrap())
|
.get(&HeaderName::from_lowercase(X_FORWARDED_FOR).unwrap())
|
||||||
{
|
{
|
||||||
if let Ok(h) = h.to_str() {
|
if let Ok(h) = h.to_str() {
|
||||||
remote = h.split(',').next().map(|v| v.trim());
|
remote = h.split(',').next().map(|v| v.trim());
|
||||||
|
@ -109,7 +109,7 @@ where
|
|||||||
|
|
||||||
fn call(&mut self, req: ServiceRequest<P>) -> Self::Future {
|
fn call(&mut self, req: ServiceRequest<P>) -> Self::Future {
|
||||||
// negotiate content-encoding
|
// 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() {
|
if let Ok(enc) = val.to_str() {
|
||||||
AcceptEncoding::parse(enc, self.encoding)
|
AcceptEncoding::parse(enc, self.encoding)
|
||||||
} else {
|
} else {
|
||||||
|
@ -584,7 +584,7 @@ struct Inner {
|
|||||||
|
|
||||||
impl Inner {
|
impl Inner {
|
||||||
fn validate_origin(&self, req: &RequestHead) -> Result<(), CorsError> {
|
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() {
|
if let Ok(origin) = hdr.to_str() {
|
||||||
return match self.origins {
|
return match self.origins {
|
||||||
AllOrSome::All => Ok(()),
|
AllOrSome::All => Ok(()),
|
||||||
@ -608,7 +608,7 @@ impl Inner {
|
|||||||
AllOrSome::All => {
|
AllOrSome::All => {
|
||||||
if self.send_wildcard {
|
if self.send_wildcard {
|
||||||
Some(HeaderValue::from_static("*"))
|
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())
|
Some(origin.clone())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -617,7 +617,7 @@ impl Inner {
|
|||||||
AllOrSome::Some(ref origins) => {
|
AllOrSome::Some(ref origins) => {
|
||||||
if let Some(origin) =
|
if let Some(origin) =
|
||||||
req.headers()
|
req.headers()
|
||||||
.get(header::ORIGIN)
|
.get(&header::ORIGIN)
|
||||||
.filter(|o| match o.to_str() {
|
.filter(|o| match o.to_str() {
|
||||||
Ok(os) => origins.contains(os),
|
Ok(os) => origins.contains(os),
|
||||||
_ => false,
|
_ => false,
|
||||||
@ -632,7 +632,7 @@ impl Inner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn validate_allowed_method(&self, req: &RequestHead) -> Result<(), CorsError> {
|
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(meth) = hdr.to_str() {
|
||||||
if let Ok(method) = Method::try_from(meth) {
|
if let Ok(method) = Method::try_from(meth) {
|
||||||
return self
|
return self
|
||||||
@ -653,7 +653,7 @@ impl Inner {
|
|||||||
AllOrSome::All => Ok(()),
|
AllOrSome::All => Ok(()),
|
||||||
AllOrSome::Some(ref allowed_headers) => {
|
AllOrSome::Some(ref allowed_headers) => {
|
||||||
if let Some(hdr) =
|
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() {
|
if let Ok(headers) = hdr.to_str() {
|
||||||
let mut hdrs = HashSet::new();
|
let mut hdrs = HashSet::new();
|
||||||
@ -720,7 +720,7 @@ where
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
)
|
)
|
||||||
} else if let Some(hdr) =
|
} else if let Some(hdr) =
|
||||||
req.headers().get(header::ACCESS_CONTROL_REQUEST_HEADERS)
|
req.headers().get(&header::ACCESS_CONTROL_REQUEST_HEADERS)
|
||||||
{
|
{
|
||||||
Some(hdr.clone())
|
Some(hdr.clone())
|
||||||
} else {
|
} else {
|
||||||
@ -759,7 +759,7 @@ where
|
|||||||
.into_body();
|
.into_body();
|
||||||
|
|
||||||
Either::A(ok(req.into_response(res)))
|
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.
|
// Only check requests with a origin header.
|
||||||
if let Err(e) = self.inner.validate_origin(req.head()) {
|
if let Err(e) = self.inner.validate_origin(req.head()) {
|
||||||
return Either::A(ok(req.error_response(e)));
|
return Either::A(ok(req.error_response(e)));
|
||||||
@ -790,7 +790,7 @@ where
|
|||||||
}
|
}
|
||||||
if inner.vary_header {
|
if inner.vary_header {
|
||||||
let value =
|
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> =
|
let mut val: Vec<u8> =
|
||||||
Vec::with_capacity(hdr.as_bytes().len() + 8);
|
Vec::with_capacity(hdr.as_bytes().len() + 8);
|
||||||
val.extend(hdr.as_bytes());
|
val.extend(hdr.as_bytes());
|
||||||
@ -893,20 +893,20 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
&b"*"[..],
|
&b"*"[..],
|
||||||
resp.headers()
|
resp.headers()
|
||||||
.get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
|
.get(&header::ACCESS_CONTROL_ALLOW_ORIGIN)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&b"3600"[..],
|
&b"3600"[..],
|
||||||
resp.headers()
|
resp.headers()
|
||||||
.get(header::ACCESS_CONTROL_MAX_AGE)
|
.get(&header::ACCESS_CONTROL_MAX_AGE)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
);
|
);
|
||||||
let hdr = resp
|
let hdr = resp
|
||||||
.headers()
|
.headers()
|
||||||
.get(header::ACCESS_CONTROL_ALLOW_HEADERS)
|
.get(&header::ACCESS_CONTROL_ALLOW_HEADERS)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -131,11 +131,11 @@ where
|
|||||||
// set response headers
|
// set response headers
|
||||||
for (key, value) in inner.headers.iter() {
|
for (key, value) in inner.headers.iter() {
|
||||||
if !res.headers().contains_key(key) {
|
if !res.headers().contains_key(key) {
|
||||||
res.headers_mut().insert(key, value.clone());
|
res.headers_mut().insert(key.clone(), value.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// default content-type
|
// 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(
|
res.headers_mut().insert(
|
||||||
CONTENT_TYPE,
|
CONTENT_TYPE,
|
||||||
HeaderValue::from_static("application/octet-stream"),
|
HeaderValue::from_static("application/octet-stream"),
|
||||||
|
@ -14,6 +14,7 @@ use time;
|
|||||||
|
|
||||||
use crate::dev::{BodySize, MessageBody, ResponseBody};
|
use crate::dev::{BodySize, MessageBody, ResponseBody};
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
|
use crate::http::{HeaderName, HttpTryFrom};
|
||||||
use crate::service::{ServiceRequest, ServiceResponse};
|
use crate::service::{ServiceRequest, ServiceResponse};
|
||||||
use crate::HttpResponse;
|
use crate::HttpResponse;
|
||||||
|
|
||||||
@ -288,8 +289,12 @@ impl Format {
|
|||||||
|
|
||||||
if let Some(key) = cap.get(2) {
|
if let Some(key) = cap.get(2) {
|
||||||
results.push(match cap.get(3).unwrap().as_str() {
|
results.push(match cap.get(3).unwrap().as_str() {
|
||||||
"i" => FormatText::RequestHeader(key.as_str().to_owned()),
|
"i" => FormatText::RequestHeader(
|
||||||
"o" => FormatText::ResponseHeader(key.as_str().to_owned()),
|
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()),
|
"e" => FormatText::EnvironHeader(key.as_str().to_owned()),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
})
|
})
|
||||||
@ -332,8 +337,8 @@ pub enum FormatText {
|
|||||||
TimeMillis,
|
TimeMillis,
|
||||||
RemoteAddr,
|
RemoteAddr,
|
||||||
UrlPath,
|
UrlPath,
|
||||||
RequestHeader(String),
|
RequestHeader(HeaderName),
|
||||||
ResponseHeader(String),
|
ResponseHeader(HeaderName),
|
||||||
EnvironHeader(String),
|
EnvironHeader(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,10 +286,10 @@ mod tests {
|
|||||||
{
|
{
|
||||||
let cookies = req.cookies().unwrap();
|
let cookies = req.cookies().unwrap();
|
||||||
assert_eq!(cookies.len(), 2);
|
assert_eq!(cookies.len(), 2);
|
||||||
assert_eq!(cookies[0].name(), "cookie1");
|
assert_eq!(cookies[0].name(), "cookie2");
|
||||||
assert_eq!(cookies[0].value(), "value1");
|
assert_eq!(cookies[0].value(), "value2");
|
||||||
assert_eq!(cookies[1].name(), "cookie2");
|
assert_eq!(cookies[1].name(), "cookie1");
|
||||||
assert_eq!(cookies[1].value(), "value2");
|
assert_eq!(cookies[1].value(), "value1");
|
||||||
}
|
}
|
||||||
|
|
||||||
let cookie = req.cookie("cookie1");
|
let cookie = req.cookie("cookie1");
|
||||||
|
@ -209,7 +209,7 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut len = None;
|
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(s) = l.to_str() {
|
||||||
if let Ok(l) = s.parse::<usize>() {
|
if let Ok(l) = s.parse::<usize>() {
|
||||||
len = Some(l)
|
len = Some(l)
|
||||||
|
@ -297,7 +297,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut len = None;
|
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(s) = l.to_str() {
|
||||||
if let Ok(l) = s.parse::<usize>() {
|
if let Ok(l) = s.parse::<usize>() {
|
||||||
len = Some(l)
|
len = Some(l)
|
||||||
|
@ -313,7 +313,7 @@ where
|
|||||||
/// Create `MessageBody` for request.
|
/// Create `MessageBody` for request.
|
||||||
pub fn new(req: &mut T) -> HttpMessageBody<T> {
|
pub fn new(req: &mut T) -> HttpMessageBody<T> {
|
||||||
let mut len = None;
|
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(s) = l.to_str() {
|
||||||
if let Ok(l) = s.parse::<usize>() {
|
if let Ok(l) = s.parse::<usize>() {
|
||||||
len = Some(l)
|
len = Some(l)
|
||||||
|
Loading…
Reference in New Issue
Block a user