mirror of
https://github.com/fafhrd91/actix-web
synced 2024-11-24 16:32:59 +01:00
refactor decoder
This commit is contained in:
parent
3b7bc41418
commit
625469f0f4
@ -13,7 +13,7 @@ use super::{Connect, Connection};
|
|||||||
use body::{BodyType, MessageBody, PayloadStream};
|
use body::{BodyType, MessageBody, PayloadStream};
|
||||||
use error::PayloadError;
|
use error::PayloadError;
|
||||||
use h1;
|
use h1;
|
||||||
use request::RequestHead;
|
use message::RequestHead;
|
||||||
|
|
||||||
pub(crate) fn send_request<T, I, B>(
|
pub(crate) fn send_request<T, I, B>(
|
||||||
head: RequestHead,
|
head: RequestHead,
|
||||||
|
@ -16,7 +16,7 @@ use http::{
|
|||||||
uri, Error as HttpError, HeaderMap, HeaderName, HeaderValue, HttpTryFrom, Method,
|
uri, Error as HttpError, HeaderMap, HeaderName, HeaderValue, HttpTryFrom, Method,
|
||||||
Uri, Version,
|
Uri, Version,
|
||||||
};
|
};
|
||||||
use request::RequestHead;
|
use message::RequestHead;
|
||||||
|
|
||||||
use super::response::ClientResponse;
|
use super::response::ClientResponse;
|
||||||
use super::{pipeline, Connect, Connection, ConnectorError, SendRequestError};
|
use super::{pipeline, Connect, Connection, ConnectorError, SendRequestError};
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use std::cell::{Cell, Ref, RefCell, RefMut};
|
use std::cell::RefCell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::{Async, Poll, Stream};
|
use futures::{Async, Poll, Stream};
|
||||||
@ -8,16 +7,14 @@ use http::{HeaderMap, StatusCode, Version};
|
|||||||
|
|
||||||
use body::PayloadStream;
|
use body::PayloadStream;
|
||||||
use error::PayloadError;
|
use error::PayloadError;
|
||||||
use extensions::Extensions;
|
|
||||||
use httpmessage::HttpMessage;
|
use httpmessage::HttpMessage;
|
||||||
use request::{Message, MessageFlags, MessagePool, RequestHead};
|
use message::{MessageFlags, ResponseHead};
|
||||||
use uri::Url;
|
|
||||||
|
|
||||||
use super::pipeline::Payload;
|
use super::pipeline::Payload;
|
||||||
|
|
||||||
/// Client Response
|
/// Client Response
|
||||||
pub struct ClientResponse {
|
pub struct ClientResponse {
|
||||||
pub(crate) inner: Rc<Message>,
|
pub(crate) head: ResponseHead,
|
||||||
pub(crate) payload: RefCell<Option<PayloadStream>>,
|
pub(crate) payload: RefCell<Option<PayloadStream>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,7 +22,7 @@ impl HttpMessage for ClientResponse {
|
|||||||
type Stream = PayloadStream;
|
type Stream = PayloadStream;
|
||||||
|
|
||||||
fn headers(&self) -> &HeaderMap {
|
fn headers(&self) -> &HeaderMap {
|
||||||
&self.inner.head.headers
|
&self.head.headers
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -41,75 +38,50 @@ impl HttpMessage for ClientResponse {
|
|||||||
impl ClientResponse {
|
impl ClientResponse {
|
||||||
/// Create new Request instance
|
/// Create new Request instance
|
||||||
pub fn new() -> ClientResponse {
|
pub fn new() -> ClientResponse {
|
||||||
ClientResponse::with_pool(MessagePool::pool())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create new Request instance with pool
|
|
||||||
pub(crate) fn with_pool(pool: &'static MessagePool) -> ClientResponse {
|
|
||||||
ClientResponse {
|
ClientResponse {
|
||||||
inner: Rc::new(Message {
|
head: ResponseHead::default(),
|
||||||
pool,
|
|
||||||
head: RequestHead::default(),
|
|
||||||
status: StatusCode::OK,
|
|
||||||
url: Url::default(),
|
|
||||||
flags: Cell::new(MessageFlags::empty()),
|
|
||||||
payload: RefCell::new(None),
|
|
||||||
extensions: RefCell::new(Extensions::new()),
|
|
||||||
}),
|
|
||||||
payload: RefCell::new(None),
|
payload: RefCell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn inner(&self) -> &Message {
|
pub(crate) fn head(&self) -> &ResponseHead {
|
||||||
self.inner.as_ref()
|
&self.head
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn inner_mut(&mut self) -> &mut Message {
|
pub(crate) fn head_mut(&mut self) -> &mut ResponseHead {
|
||||||
Rc::get_mut(&mut self.inner).expect("Multiple copies exist")
|
&mut self.head
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the Request Version.
|
/// Read the Request Version.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn version(&self) -> Version {
|
pub fn version(&self) -> Version {
|
||||||
self.inner().head.version
|
self.head().version.clone().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the status from the server.
|
/// Get the status from the server.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn status(&self) -> StatusCode {
|
pub fn status(&self) -> StatusCode {
|
||||||
self.inner().status
|
self.head().status
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Returns Request's headers.
|
/// Returns Request's headers.
|
||||||
pub fn headers(&self) -> &HeaderMap {
|
pub fn headers(&self) -> &HeaderMap {
|
||||||
&self.inner().head.headers
|
&self.head().headers
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Returns mutable Request's headers.
|
/// Returns mutable Request's headers.
|
||||||
pub fn headers_mut(&mut self) -> &mut HeaderMap {
|
pub fn headers_mut(&mut self) -> &mut HeaderMap {
|
||||||
&mut self.inner_mut().head.headers
|
&mut self.head_mut().headers
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if a connection should be kept alive.
|
/// Checks if a connection should be kept alive.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn keep_alive(&self) -> bool {
|
pub fn keep_alive(&self) -> bool {
|
||||||
self.inner().flags.get().contains(MessageFlags::KEEPALIVE)
|
self.head().flags.contains(MessageFlags::KEEPALIVE)
|
||||||
}
|
|
||||||
|
|
||||||
/// Request extensions
|
|
||||||
#[inline]
|
|
||||||
pub fn extensions(&self) -> Ref<Extensions> {
|
|
||||||
self.inner().extensions.borrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mutable reference to a the request's extensions
|
|
||||||
#[inline]
|
|
||||||
pub fn extensions_mut(&self) -> RefMut<Extensions> {
|
|
||||||
self.inner().extensions.borrow_mut()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,14 +98,6 @@ impl Stream for ClientResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for ClientResponse {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if Rc::strong_count(&self.inner) == 1 {
|
|
||||||
self.inner.pool.release(self.inner.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for ClientResponse {
|
impl fmt::Debug for ClientResponse {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
writeln!(f, "\nClientResponse {:?} {}", self.version(), self.status(),)?;
|
writeln!(f, "\nClientResponse {:?} {}", self.version(), self.status(),)?;
|
||||||
|
@ -4,7 +4,7 @@ use std::io::{self, Write};
|
|||||||
use bytes::{BufMut, Bytes, BytesMut};
|
use bytes::{BufMut, Bytes, BytesMut};
|
||||||
use tokio_codec::{Decoder, Encoder};
|
use tokio_codec::{Decoder, Encoder};
|
||||||
|
|
||||||
use super::decoder::{PayloadDecoder, PayloadItem, PayloadType, ResponseDecoder};
|
use super::decoder::{MessageDecoder, PayloadDecoder, PayloadItem, PayloadType};
|
||||||
use super::encoder::{RequestEncoder, ResponseLength};
|
use super::encoder::{RequestEncoder, ResponseLength};
|
||||||
use super::{Message, MessageType};
|
use super::{Message, MessageType};
|
||||||
use body::{Binary, Body, BodyType};
|
use body::{Binary, Body, BodyType};
|
||||||
@ -16,7 +16,7 @@ use http::header::{
|
|||||||
HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING, UPGRADE,
|
HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING, UPGRADE,
|
||||||
};
|
};
|
||||||
use http::{Method, Version};
|
use http::{Method, Version};
|
||||||
use request::{MessagePool, RequestHead};
|
use message::{MessagePool, RequestHead};
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
struct Flags: u8 {
|
struct Flags: u8 {
|
||||||
@ -42,7 +42,7 @@ pub struct ClientPayloadCodec {
|
|||||||
|
|
||||||
struct ClientCodecInner {
|
struct ClientCodecInner {
|
||||||
config: ServiceConfig,
|
config: ServiceConfig,
|
||||||
decoder: ResponseDecoder,
|
decoder: MessageDecoder<ClientResponse>,
|
||||||
payload: Option<PayloadDecoder>,
|
payload: Option<PayloadDecoder>,
|
||||||
version: Version,
|
version: Version,
|
||||||
|
|
||||||
@ -63,11 +63,6 @@ impl ClientCodec {
|
|||||||
///
|
///
|
||||||
/// `keepalive_enabled` how response `connection` header get generated.
|
/// `keepalive_enabled` how response `connection` header get generated.
|
||||||
pub fn new(config: ServiceConfig) -> Self {
|
pub fn new(config: ServiceConfig) -> Self {
|
||||||
ClientCodec::with_pool(MessagePool::pool(), config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create HTTP/1 codec with request's pool
|
|
||||||
pub(crate) fn with_pool(pool: &'static MessagePool, config: ServiceConfig) -> Self {
|
|
||||||
let flags = if config.keep_alive_enabled() {
|
let flags = if config.keep_alive_enabled() {
|
||||||
Flags::KEEPALIVE_ENABLED
|
Flags::KEEPALIVE_ENABLED
|
||||||
} else {
|
} else {
|
||||||
@ -76,7 +71,7 @@ impl ClientCodec {
|
|||||||
ClientCodec {
|
ClientCodec {
|
||||||
inner: ClientCodecInner {
|
inner: ClientCodecInner {
|
||||||
config,
|
config,
|
||||||
decoder: ResponseDecoder::with_pool(pool),
|
decoder: MessageDecoder::default(),
|
||||||
payload: None,
|
payload: None,
|
||||||
version: Version::HTTP_11,
|
version: Version::HTTP_11,
|
||||||
|
|
||||||
@ -185,10 +180,10 @@ impl Decoder for ClientCodec {
|
|||||||
debug_assert!(!self.inner.payload.is_some(), "Payload decoder is set");
|
debug_assert!(!self.inner.payload.is_some(), "Payload decoder is set");
|
||||||
|
|
||||||
if let Some((req, payload)) = self.inner.decoder.decode(src)? {
|
if let Some((req, payload)) = self.inner.decoder.decode(src)? {
|
||||||
self.inner
|
// self.inner
|
||||||
.flags
|
// .flags
|
||||||
.set(Flags::HEAD, req.inner.head.method == Method::HEAD);
|
// .set(Flags::HEAD, req.head.method == Method::HEAD);
|
||||||
self.inner.version = req.inner.head.version;
|
// self.inner.version = req.head.version;
|
||||||
if self.inner.flags.contains(Flags::KEEPALIVE_ENABLED) {
|
if self.inner.flags.contains(Flags::KEEPALIVE_ENABLED) {
|
||||||
self.inner.flags.set(Flags::KEEPALIVE, req.keep_alive());
|
self.inner.flags.set(Flags::KEEPALIVE, req.keep_alive());
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use std::io::{self, Write};
|
|||||||
use bytes::{BufMut, Bytes, BytesMut};
|
use bytes::{BufMut, Bytes, BytesMut};
|
||||||
use tokio_codec::{Decoder, Encoder};
|
use tokio_codec::{Decoder, Encoder};
|
||||||
|
|
||||||
use super::decoder::{PayloadDecoder, PayloadItem, PayloadType, RequestDecoder};
|
use super::decoder::{MessageDecoder, PayloadDecoder, PayloadItem, PayloadType};
|
||||||
use super::encoder::{ResponseEncoder, ResponseLength};
|
use super::encoder::{ResponseEncoder, ResponseLength};
|
||||||
use super::{Message, MessageType};
|
use super::{Message, MessageType};
|
||||||
use body::{Binary, Body};
|
use body::{Binary, Body};
|
||||||
@ -14,7 +14,7 @@ use error::ParseError;
|
|||||||
use helpers;
|
use helpers;
|
||||||
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING};
|
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING};
|
||||||
use http::{Method, Version};
|
use http::{Method, Version};
|
||||||
use request::{MessagePool, Request};
|
use request::Request;
|
||||||
use response::Response;
|
use response::Response;
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
@ -32,7 +32,7 @@ const AVERAGE_HEADER_SIZE: usize = 30;
|
|||||||
/// HTTP/1 Codec
|
/// HTTP/1 Codec
|
||||||
pub struct Codec {
|
pub struct Codec {
|
||||||
config: ServiceConfig,
|
config: ServiceConfig,
|
||||||
decoder: RequestDecoder,
|
decoder: MessageDecoder<Request>,
|
||||||
payload: Option<PayloadDecoder>,
|
payload: Option<PayloadDecoder>,
|
||||||
version: Version,
|
version: Version,
|
||||||
|
|
||||||
@ -59,11 +59,6 @@ impl Codec {
|
|||||||
///
|
///
|
||||||
/// `keepalive_enabled` how response `connection` header get generated.
|
/// `keepalive_enabled` how response `connection` header get generated.
|
||||||
pub fn new(config: ServiceConfig) -> Self {
|
pub fn new(config: ServiceConfig) -> Self {
|
||||||
Codec::with_pool(MessagePool::pool(), config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create HTTP/1 codec with request's pool
|
|
||||||
pub(crate) fn with_pool(pool: &'static MessagePool, config: ServiceConfig) -> Self {
|
|
||||||
let flags = if config.keep_alive_enabled() {
|
let flags = if config.keep_alive_enabled() {
|
||||||
Flags::KEEPALIVE_ENABLED
|
Flags::KEEPALIVE_ENABLED
|
||||||
} else {
|
} else {
|
||||||
@ -71,7 +66,7 @@ impl Codec {
|
|||||||
};
|
};
|
||||||
Codec {
|
Codec {
|
||||||
config,
|
config,
|
||||||
decoder: RequestDecoder::with_pool(pool),
|
decoder: MessageDecoder::default(),
|
||||||
payload: None,
|
payload: None,
|
||||||
version: Version::HTTP_11,
|
version: Version::HTTP_11,
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use std::marker::PhantomData;
|
||||||
use std::{io, mem};
|
use std::{io, mem};
|
||||||
|
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
@ -8,327 +9,305 @@ use tokio_codec::Decoder;
|
|||||||
use client::ClientResponse;
|
use client::ClientResponse;
|
||||||
use error::ParseError;
|
use error::ParseError;
|
||||||
use http::header::{HeaderName, HeaderValue};
|
use http::header::{HeaderName, HeaderValue};
|
||||||
use http::{header, HttpTryFrom, Method, StatusCode, Uri, Version};
|
use http::{header, HeaderMap, HttpTryFrom, Method, StatusCode, Uri, Version};
|
||||||
use request::{MessageFlags, MessagePool, Request};
|
use message::MessageFlags;
|
||||||
use uri::Url;
|
use request::Request;
|
||||||
|
|
||||||
const MAX_BUFFER_SIZE: usize = 131_072;
|
const MAX_BUFFER_SIZE: usize = 131_072;
|
||||||
const MAX_HEADERS: usize = 96;
|
const MAX_HEADERS: usize = 96;
|
||||||
|
|
||||||
/// Client request decoder
|
/// Incoming messagd decoder
|
||||||
pub struct RequestDecoder(&'static MessagePool);
|
pub(crate) struct MessageDecoder<T: MessageTypeDecoder>(PhantomData<T>);
|
||||||
|
|
||||||
/// Server response decoder
|
|
||||||
pub struct ResponseDecoder(&'static MessagePool);
|
|
||||||
|
|
||||||
/// Incoming request type
|
/// Incoming request type
|
||||||
pub enum PayloadType {
|
pub(crate) enum PayloadType {
|
||||||
None,
|
None,
|
||||||
Payload(PayloadDecoder),
|
Payload(PayloadDecoder),
|
||||||
Stream(PayloadDecoder),
|
Stream(PayloadDecoder),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RequestDecoder {
|
impl<T: MessageTypeDecoder> Default for MessageDecoder<T> {
|
||||||
pub(crate) fn with_pool(pool: &'static MessagePool) -> RequestDecoder {
|
fn default() -> Self {
|
||||||
RequestDecoder(pool)
|
MessageDecoder(PhantomData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RequestDecoder {
|
impl<T: MessageTypeDecoder> Decoder for MessageDecoder<T> {
|
||||||
fn default() -> RequestDecoder {
|
type Item = (T, PayloadType);
|
||||||
RequestDecoder::with_pool(MessagePool::pool())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Decoder for RequestDecoder {
|
|
||||||
type Item = (Request, PayloadType);
|
|
||||||
type Error = ParseError;
|
type Error = ParseError;
|
||||||
|
|
||||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||||
// Parse http message
|
T::decode(src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) enum PayloadLength {
|
||||||
|
None,
|
||||||
|
Chunked,
|
||||||
|
Upgrade,
|
||||||
|
Length(u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait MessageTypeDecoder: Sized {
|
||||||
|
fn keep_alive(&mut self);
|
||||||
|
|
||||||
|
fn headers_mut(&mut self) -> &mut HeaderMap;
|
||||||
|
|
||||||
|
fn decode(src: &mut BytesMut) -> Result<Option<(Self, PayloadType)>, ParseError>;
|
||||||
|
|
||||||
|
fn process_headers(
|
||||||
|
&mut self,
|
||||||
|
slice: &Bytes,
|
||||||
|
version: Version,
|
||||||
|
raw_headers: &[HeaderIndex],
|
||||||
|
) -> Result<PayloadLength, ParseError> {
|
||||||
|
let mut ka = version != Version::HTTP_10;
|
||||||
let mut has_upgrade = false;
|
let mut has_upgrade = false;
|
||||||
let mut chunked = false;
|
let mut chunked = false;
|
||||||
let mut content_length = None;
|
let mut content_length = None;
|
||||||
|
|
||||||
let msg = {
|
{
|
||||||
// Unsafe: we read only this data only after httparse parses headers into.
|
let headers = self.headers_mut();
|
||||||
// performance bump for pipeline benchmarks.
|
|
||||||
let mut headers: [HeaderIndex; MAX_HEADERS] =
|
for idx in raw_headers.iter() {
|
||||||
|
if let Ok(name) = HeaderName::from_bytes(&slice[idx.name.0..idx.name.1])
|
||||||
|
{
|
||||||
|
// Unsafe: httparse check header value for valid utf-8
|
||||||
|
let value = unsafe {
|
||||||
|
HeaderValue::from_shared_unchecked(
|
||||||
|
slice.slice(idx.value.0, idx.value.1),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
match name {
|
||||||
|
header::CONTENT_LENGTH => {
|
||||||
|
if let Ok(s) = value.to_str() {
|
||||||
|
if let Ok(len) = s.parse::<u64>() {
|
||||||
|
content_length = Some(len);
|
||||||
|
} else {
|
||||||
|
debug!("illegal Content-Length: {:?}", s);
|
||||||
|
return Err(ParseError::Header);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug!("illegal Content-Length: {:?}", value);
|
||||||
|
return Err(ParseError::Header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// transfer-encoding
|
||||||
|
header::TRANSFER_ENCODING => {
|
||||||
|
if let Ok(s) = value.to_str() {
|
||||||
|
chunked = s.to_lowercase().contains("chunked");
|
||||||
|
} else {
|
||||||
|
return Err(ParseError::Header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// connection keep-alive state
|
||||||
|
header::CONNECTION => {
|
||||||
|
ka = if let Ok(conn) = value.to_str() {
|
||||||
|
if version == Version::HTTP_10
|
||||||
|
&& conn.contains("keep-alive")
|
||||||
|
{
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
version == Version::HTTP_11 && !(conn
|
||||||
|
.contains("close")
|
||||||
|
|| conn.contains("upgrade"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header::UPGRADE => {
|
||||||
|
has_upgrade = true;
|
||||||
|
// check content-length, some clients (dart)
|
||||||
|
// sends "content-length: 0" with websocket upgrade
|
||||||
|
if let Ok(val) = value.to_str() {
|
||||||
|
if val == "websocket" {
|
||||||
|
content_length = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
headers.append(name, value);
|
||||||
|
} else {
|
||||||
|
return Err(ParseError::Header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ka {
|
||||||
|
self.keep_alive();
|
||||||
|
}
|
||||||
|
|
||||||
|
if chunked {
|
||||||
|
Ok(PayloadLength::Chunked)
|
||||||
|
} else if let Some(len) = content_length {
|
||||||
|
Ok(PayloadLength::Length(len))
|
||||||
|
} else if has_upgrade {
|
||||||
|
Ok(PayloadLength::Upgrade)
|
||||||
|
} else {
|
||||||
|
Ok(PayloadLength::None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageTypeDecoder for Request {
|
||||||
|
fn keep_alive(&mut self) {
|
||||||
|
self.inner_mut().flags.set(MessageFlags::KEEPALIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn headers_mut(&mut self) -> &mut HeaderMap {
|
||||||
|
self.headers_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode(src: &mut BytesMut) -> Result<Option<(Self, PayloadType)>, ParseError> {
|
||||||
|
// Unsafe: we read only this data only after httparse parses headers into.
|
||||||
|
// performance bump for pipeline benchmarks.
|
||||||
|
let mut headers: [HeaderIndex; MAX_HEADERS] = unsafe { mem::uninitialized() };
|
||||||
|
|
||||||
|
let (len, method, uri, version, headers_len) = {
|
||||||
|
let mut parsed: [httparse::Header; MAX_HEADERS] =
|
||||||
unsafe { mem::uninitialized() };
|
unsafe { mem::uninitialized() };
|
||||||
|
|
||||||
let (len, method, path, version, headers_len) = {
|
let mut req = httparse::Request::new(&mut parsed);
|
||||||
let mut parsed: [httparse::Header; MAX_HEADERS] =
|
match req.parse(src)? {
|
||||||
unsafe { mem::uninitialized() };
|
httparse::Status::Complete(len) => {
|
||||||
|
let method = Method::from_bytes(req.method.unwrap().as_bytes())
|
||||||
let mut req = httparse::Request::new(&mut parsed);
|
.map_err(|_| ParseError::Method)?;
|
||||||
match req.parse(src)? {
|
let uri = Uri::try_from(req.path.unwrap())?;
|
||||||
httparse::Status::Complete(len) => {
|
let version = if req.version.unwrap() == 1 {
|
||||||
let method = Method::from_bytes(req.method.unwrap().as_bytes())
|
Version::HTTP_11
|
||||||
.map_err(|_| ParseError::Method)?;
|
|
||||||
let path = Url::new(Uri::try_from(req.path.unwrap())?);
|
|
||||||
let version = if req.version.unwrap() == 1 {
|
|
||||||
Version::HTTP_11
|
|
||||||
} else {
|
|
||||||
Version::HTTP_10
|
|
||||||
};
|
|
||||||
HeaderIndex::record(src, req.headers, &mut headers);
|
|
||||||
|
|
||||||
(len, method, path, version, req.headers.len())
|
|
||||||
}
|
|
||||||
httparse::Status::Partial => return Ok(None),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let slice = src.split_to(len).freeze();
|
|
||||||
|
|
||||||
// convert headers
|
|
||||||
let mut msg = MessagePool::get_request(self.0);
|
|
||||||
{
|
|
||||||
let inner = msg.inner_mut();
|
|
||||||
inner
|
|
||||||
.flags
|
|
||||||
.get_mut()
|
|
||||||
.set(MessageFlags::KEEPALIVE, version != Version::HTTP_10);
|
|
||||||
|
|
||||||
for idx in headers[..headers_len].iter() {
|
|
||||||
if let Ok(name) =
|
|
||||||
HeaderName::from_bytes(&slice[idx.name.0..idx.name.1])
|
|
||||||
{
|
|
||||||
// Unsafe: httparse check header value for valid utf-8
|
|
||||||
let value = unsafe {
|
|
||||||
HeaderValue::from_shared_unchecked(
|
|
||||||
slice.slice(idx.value.0, idx.value.1),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
match name {
|
|
||||||
header::CONTENT_LENGTH => {
|
|
||||||
if let Ok(s) = value.to_str() {
|
|
||||||
if let Ok(len) = s.parse::<u64>() {
|
|
||||||
content_length = Some(len);
|
|
||||||
} else {
|
|
||||||
debug!("illegal Content-Length: {:?}", len);
|
|
||||||
return Err(ParseError::Header);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
debug!("illegal Content-Length: {:?}", len);
|
|
||||||
return Err(ParseError::Header);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// transfer-encoding
|
|
||||||
header::TRANSFER_ENCODING => {
|
|
||||||
if let Ok(s) = value.to_str() {
|
|
||||||
chunked = s.to_lowercase().contains("chunked");
|
|
||||||
} else {
|
|
||||||
return Err(ParseError::Header);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// connection keep-alive state
|
|
||||||
header::CONNECTION => {
|
|
||||||
let ka = if let Ok(conn) = value.to_str() {
|
|
||||||
if version == Version::HTTP_10
|
|
||||||
&& conn.contains("keep-alive")
|
|
||||||
{
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
version == Version::HTTP_11 && !(conn
|
|
||||||
.contains("close")
|
|
||||||
|| conn.contains("upgrade"))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
inner.flags.get_mut().set(MessageFlags::KEEPALIVE, ka);
|
|
||||||
}
|
|
||||||
header::UPGRADE => {
|
|
||||||
has_upgrade = true;
|
|
||||||
// check content-length, some clients (dart)
|
|
||||||
// sends "content-length: 0" with websocket upgrade
|
|
||||||
if let Ok(val) = value.to_str() {
|
|
||||||
if val == "websocket" {
|
|
||||||
content_length = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
inner.head.headers.append(name, value);
|
|
||||||
} else {
|
} else {
|
||||||
return Err(ParseError::Header);
|
Version::HTTP_10
|
||||||
}
|
};
|
||||||
}
|
HeaderIndex::record(src, req.headers, &mut headers);
|
||||||
|
|
||||||
inner.url = path;
|
(len, method, uri, version, req.headers.len())
|
||||||
inner.head.method = method;
|
}
|
||||||
inner.head.version = version;
|
httparse::Status::Partial => return Ok(None),
|
||||||
}
|
}
|
||||||
msg
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// convert headers
|
||||||
|
let mut msg = Request::new();
|
||||||
|
|
||||||
|
let len = msg.process_headers(
|
||||||
|
&src.split_to(len).freeze(),
|
||||||
|
version,
|
||||||
|
&headers[..headers_len],
|
||||||
|
)?;
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc7230#section-3.3.3
|
// https://tools.ietf.org/html/rfc7230#section-3.3.3
|
||||||
let decoder = if chunked {
|
let decoder = match len {
|
||||||
// Chunked encoding
|
PayloadLength::Chunked => {
|
||||||
PayloadType::Payload(PayloadDecoder::chunked())
|
// Chunked encoding
|
||||||
} else if let Some(len) = content_length {
|
PayloadType::Payload(PayloadDecoder::chunked())
|
||||||
// Content-Length
|
}
|
||||||
PayloadType::Payload(PayloadDecoder::length(len))
|
PayloadLength::Length(len) => {
|
||||||
} else if has_upgrade || msg.inner.head.method == Method::CONNECT {
|
// Content-Length
|
||||||
// upgrade(websocket) or connect
|
PayloadType::Payload(PayloadDecoder::length(len))
|
||||||
PayloadType::Stream(PayloadDecoder::eof())
|
}
|
||||||
} else if src.len() >= MAX_BUFFER_SIZE {
|
PayloadLength::Upgrade => {
|
||||||
error!("MAX_BUFFER_SIZE unprocessed data reached, closing");
|
// upgrade(websocket) or connect
|
||||||
return Err(ParseError::TooLarge);
|
PayloadType::Stream(PayloadDecoder::eof())
|
||||||
} else {
|
}
|
||||||
PayloadType::None
|
PayloadLength::None => {
|
||||||
|
if method == Method::CONNECT {
|
||||||
|
// upgrade(websocket) or connect
|
||||||
|
PayloadType::Stream(PayloadDecoder::eof())
|
||||||
|
} else if src.len() >= MAX_BUFFER_SIZE {
|
||||||
|
error!("MAX_BUFFER_SIZE unprocessed data reached, closing");
|
||||||
|
return Err(ParseError::TooLarge);
|
||||||
|
} else {
|
||||||
|
PayloadType::None
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let inner = msg.inner_mut();
|
||||||
|
inner.url.update(&uri);
|
||||||
|
inner.head.uri = uri;
|
||||||
|
inner.head.method = method;
|
||||||
|
inner.head.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Some((msg, decoder)))
|
Ok(Some((msg, decoder)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseDecoder {
|
impl MessageTypeDecoder for ClientResponse {
|
||||||
pub(crate) fn with_pool(pool: &'static MessagePool) -> ResponseDecoder {
|
fn keep_alive(&mut self) {
|
||||||
ResponseDecoder(pool)
|
self.head.flags.insert(MessageFlags::KEEPALIVE);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ResponseDecoder {
|
fn headers_mut(&mut self) -> &mut HeaderMap {
|
||||||
fn default() -> ResponseDecoder {
|
self.headers_mut()
|
||||||
ResponseDecoder::with_pool(MessagePool::pool())
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Decoder for ResponseDecoder {
|
fn decode(src: &mut BytesMut) -> Result<Option<(Self, PayloadType)>, ParseError> {
|
||||||
type Item = (ClientResponse, PayloadType);
|
// Unsafe: we read only this data only after httparse parses headers into.
|
||||||
type Error = ParseError;
|
// performance bump for pipeline benchmarks.
|
||||||
|
let mut headers: [HeaderIndex; MAX_HEADERS] = unsafe { mem::uninitialized() };
|
||||||
|
|
||||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
let (len, version, status, headers_len) = {
|
||||||
// Parse http message
|
let mut parsed: [httparse::Header; MAX_HEADERS] =
|
||||||
let mut chunked = false;
|
|
||||||
let mut content_length = None;
|
|
||||||
|
|
||||||
let msg = {
|
|
||||||
// Unsafe: we read only this data only after httparse parses headers into.
|
|
||||||
// performance bump for pipeline benchmarks.
|
|
||||||
let mut headers: [HeaderIndex; MAX_HEADERS] =
|
|
||||||
unsafe { mem::uninitialized() };
|
unsafe { mem::uninitialized() };
|
||||||
|
|
||||||
let (len, version, status, headers_len) = {
|
let mut res = httparse::Response::new(&mut parsed);
|
||||||
let mut parsed: [httparse::Header; MAX_HEADERS] =
|
match res.parse(src)? {
|
||||||
unsafe { mem::uninitialized() };
|
httparse::Status::Complete(len) => {
|
||||||
|
let version = if res.version.unwrap() == 1 {
|
||||||
let mut res = httparse::Response::new(&mut parsed);
|
Version::HTTP_11
|
||||||
match res.parse(src)? {
|
|
||||||
httparse::Status::Complete(len) => {
|
|
||||||
let version = if res.version.unwrap() == 1 {
|
|
||||||
Version::HTTP_11
|
|
||||||
} else {
|
|
||||||
Version::HTTP_10
|
|
||||||
};
|
|
||||||
let status = StatusCode::from_u16(res.code.unwrap())
|
|
||||||
.map_err(|_| ParseError::Status)?;
|
|
||||||
HeaderIndex::record(src, res.headers, &mut headers);
|
|
||||||
|
|
||||||
(len, version, status, res.headers.len())
|
|
||||||
}
|
|
||||||
httparse::Status::Partial => return Ok(None),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let slice = src.split_to(len).freeze();
|
|
||||||
|
|
||||||
// convert headers
|
|
||||||
let mut msg = MessagePool::get_response(self.0);
|
|
||||||
{
|
|
||||||
let inner = msg.inner_mut();
|
|
||||||
inner
|
|
||||||
.flags
|
|
||||||
.get_mut()
|
|
||||||
.set(MessageFlags::KEEPALIVE, version != Version::HTTP_10);
|
|
||||||
|
|
||||||
for idx in headers[..headers_len].iter() {
|
|
||||||
if let Ok(name) =
|
|
||||||
HeaderName::from_bytes(&slice[idx.name.0..idx.name.1])
|
|
||||||
{
|
|
||||||
// Unsafe: httparse check header value for valid utf-8
|
|
||||||
let value = unsafe {
|
|
||||||
HeaderValue::from_shared_unchecked(
|
|
||||||
slice.slice(idx.value.0, idx.value.1),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
match name {
|
|
||||||
header::CONTENT_LENGTH => {
|
|
||||||
if let Ok(s) = value.to_str() {
|
|
||||||
if let Ok(len) = s.parse::<u64>() {
|
|
||||||
content_length = Some(len);
|
|
||||||
} else {
|
|
||||||
debug!("illegal Content-Length: {:?}", len);
|
|
||||||
return Err(ParseError::Header);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
debug!("illegal Content-Length: {:?}", len);
|
|
||||||
return Err(ParseError::Header);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// transfer-encoding
|
|
||||||
header::TRANSFER_ENCODING => {
|
|
||||||
if let Ok(s) = value.to_str() {
|
|
||||||
chunked = s.to_lowercase().contains("chunked");
|
|
||||||
} else {
|
|
||||||
return Err(ParseError::Header);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// connection keep-alive state
|
|
||||||
header::CONNECTION => {
|
|
||||||
let ka = if let Ok(conn) = value.to_str() {
|
|
||||||
if version == Version::HTTP_10
|
|
||||||
&& conn.contains("keep-alive")
|
|
||||||
{
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
version == Version::HTTP_11 && !(conn
|
|
||||||
.contains("close")
|
|
||||||
|| conn.contains("upgrade"))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
inner.flags.get_mut().set(MessageFlags::KEEPALIVE, ka);
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
inner.head.headers.append(name, value);
|
|
||||||
} else {
|
} else {
|
||||||
return Err(ParseError::Header);
|
Version::HTTP_10
|
||||||
}
|
};
|
||||||
}
|
let status = StatusCode::from_u16(res.code.unwrap())
|
||||||
|
.map_err(|_| ParseError::Status)?;
|
||||||
|
HeaderIndex::record(src, res.headers, &mut headers);
|
||||||
|
|
||||||
inner.status = status;
|
(len, version, status, res.headers.len())
|
||||||
inner.head.version = version;
|
}
|
||||||
|
httparse::Status::Partial => return Ok(None),
|
||||||
}
|
}
|
||||||
msg
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut msg = ClientResponse::new();
|
||||||
|
|
||||||
|
// convert headers
|
||||||
|
let len = msg.process_headers(
|
||||||
|
&src.split_to(len).freeze(),
|
||||||
|
version,
|
||||||
|
&headers[..headers_len],
|
||||||
|
)?;
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc7230#section-3.3.3
|
// https://tools.ietf.org/html/rfc7230#section-3.3.3
|
||||||
let decoder = if chunked {
|
let decoder = match len {
|
||||||
// Chunked encoding
|
PayloadLength::Chunked => {
|
||||||
PayloadType::Payload(PayloadDecoder::chunked())
|
// Chunked encoding
|
||||||
} else if let Some(len) = content_length {
|
PayloadType::Payload(PayloadDecoder::chunked())
|
||||||
// Content-Length
|
}
|
||||||
PayloadType::Payload(PayloadDecoder::length(len))
|
PayloadLength::Length(len) => {
|
||||||
} else if msg.inner.status == StatusCode::SWITCHING_PROTOCOLS
|
// Content-Length
|
||||||
|| msg.inner.head.method == Method::CONNECT
|
PayloadType::Payload(PayloadDecoder::length(len))
|
||||||
{
|
}
|
||||||
// switching protocol or connect
|
_ => {
|
||||||
PayloadType::Stream(PayloadDecoder::eof())
|
if status == StatusCode::SWITCHING_PROTOCOLS {
|
||||||
} else if src.len() >= MAX_BUFFER_SIZE {
|
// switching protocol or connect
|
||||||
error!("MAX_BUFFER_SIZE unprocessed data reached, closing");
|
PayloadType::Stream(PayloadDecoder::eof())
|
||||||
return Err(ParseError::TooLarge);
|
} else if src.len() >= MAX_BUFFER_SIZE {
|
||||||
} else {
|
error!("MAX_BUFFER_SIZE unprocessed data reached, closing");
|
||||||
PayloadType::None
|
return Err(ParseError::TooLarge);
|
||||||
|
} else {
|
||||||
|
PayloadType::None
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
msg.head.status = status;
|
||||||
|
msg.head.version = Some(version);
|
||||||
|
|
||||||
Ok(Some((msg, decoder)))
|
Ok(Some((msg, decoder)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -690,7 +669,7 @@ mod tests {
|
|||||||
|
|
||||||
macro_rules! parse_ready {
|
macro_rules! parse_ready {
|
||||||
($e:expr) => {{
|
($e:expr) => {{
|
||||||
match RequestDecoder::default().decode($e) {
|
match MessageDecoder::<Request>::default().decode($e) {
|
||||||
Ok(Some((msg, _))) => msg,
|
Ok(Some((msg, _))) => msg,
|
||||||
Ok(_) => unreachable!("Eof during parsing http request"),
|
Ok(_) => unreachable!("Eof during parsing http request"),
|
||||||
Err(err) => unreachable!("Error during parsing http request: {:?}", err),
|
Err(err) => unreachable!("Error during parsing http request: {:?}", err),
|
||||||
@ -700,7 +679,7 @@ mod tests {
|
|||||||
|
|
||||||
macro_rules! expect_parse_err {
|
macro_rules! expect_parse_err {
|
||||||
($e:expr) => {{
|
($e:expr) => {{
|
||||||
match RequestDecoder::default().decode($e) {
|
match MessageDecoder::<Request>::default().decode($e) {
|
||||||
Err(err) => match err {
|
Err(err) => match err {
|
||||||
ParseError::Io(_) => unreachable!("Parse error expected"),
|
ParseError::Io(_) => unreachable!("Parse error expected"),
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -779,7 +758,7 @@ mod tests {
|
|||||||
fn test_parse() {
|
fn test_parse() {
|
||||||
let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n\r\n");
|
let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n\r\n");
|
||||||
|
|
||||||
let mut reader = RequestDecoder::default();
|
let mut reader = MessageDecoder::<Request>::default();
|
||||||
match reader.decode(&mut buf) {
|
match reader.decode(&mut buf) {
|
||||||
Ok(Some((req, _))) => {
|
Ok(Some((req, _))) => {
|
||||||
assert_eq!(req.version(), Version::HTTP_11);
|
assert_eq!(req.version(), Version::HTTP_11);
|
||||||
@ -794,7 +773,7 @@ mod tests {
|
|||||||
fn test_parse_partial() {
|
fn test_parse_partial() {
|
||||||
let mut buf = BytesMut::from("PUT /test HTTP/1");
|
let mut buf = BytesMut::from("PUT /test HTTP/1");
|
||||||
|
|
||||||
let mut reader = RequestDecoder::default();
|
let mut reader = MessageDecoder::<Request>::default();
|
||||||
assert!(reader.decode(&mut buf).unwrap().is_none());
|
assert!(reader.decode(&mut buf).unwrap().is_none());
|
||||||
|
|
||||||
buf.extend(b".1\r\n\r\n");
|
buf.extend(b".1\r\n\r\n");
|
||||||
@ -808,7 +787,7 @@ mod tests {
|
|||||||
fn test_parse_post() {
|
fn test_parse_post() {
|
||||||
let mut buf = BytesMut::from("POST /test2 HTTP/1.0\r\n\r\n");
|
let mut buf = BytesMut::from("POST /test2 HTTP/1.0\r\n\r\n");
|
||||||
|
|
||||||
let mut reader = RequestDecoder::default();
|
let mut reader = MessageDecoder::<Request>::default();
|
||||||
let (req, _) = reader.decode(&mut buf).unwrap().unwrap();
|
let (req, _) = reader.decode(&mut buf).unwrap().unwrap();
|
||||||
assert_eq!(req.version(), Version::HTTP_10);
|
assert_eq!(req.version(), Version::HTTP_10);
|
||||||
assert_eq!(*req.method(), Method::POST);
|
assert_eq!(*req.method(), Method::POST);
|
||||||
@ -820,7 +799,7 @@ mod tests {
|
|||||||
let mut buf =
|
let mut buf =
|
||||||
BytesMut::from("GET /test HTTP/1.1\r\nContent-Length: 4\r\n\r\nbody");
|
BytesMut::from("GET /test HTTP/1.1\r\nContent-Length: 4\r\n\r\nbody");
|
||||||
|
|
||||||
let mut reader = RequestDecoder::default();
|
let mut reader = MessageDecoder::<Request>::default();
|
||||||
let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();
|
let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();
|
||||||
let mut pl = pl.unwrap();
|
let mut pl = pl.unwrap();
|
||||||
assert_eq!(req.version(), Version::HTTP_11);
|
assert_eq!(req.version(), Version::HTTP_11);
|
||||||
@ -837,7 +816,7 @@ mod tests {
|
|||||||
let mut buf =
|
let mut buf =
|
||||||
BytesMut::from("\r\nGET /test HTTP/1.1\r\nContent-Length: 4\r\n\r\nbody");
|
BytesMut::from("\r\nGET /test HTTP/1.1\r\nContent-Length: 4\r\n\r\nbody");
|
||||||
|
|
||||||
let mut reader = RequestDecoder::default();
|
let mut reader = MessageDecoder::<Request>::default();
|
||||||
let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();
|
let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();
|
||||||
let mut pl = pl.unwrap();
|
let mut pl = pl.unwrap();
|
||||||
assert_eq!(req.version(), Version::HTTP_11);
|
assert_eq!(req.version(), Version::HTTP_11);
|
||||||
@ -852,7 +831,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_partial_eof() {
|
fn test_parse_partial_eof() {
|
||||||
let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n");
|
let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n");
|
||||||
let mut reader = RequestDecoder::default();
|
let mut reader = MessageDecoder::<Request>::default();
|
||||||
assert!(reader.decode(&mut buf).unwrap().is_none());
|
assert!(reader.decode(&mut buf).unwrap().is_none());
|
||||||
|
|
||||||
buf.extend(b"\r\n");
|
buf.extend(b"\r\n");
|
||||||
@ -866,7 +845,7 @@ mod tests {
|
|||||||
fn test_headers_split_field() {
|
fn test_headers_split_field() {
|
||||||
let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n");
|
let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n");
|
||||||
|
|
||||||
let mut reader = RequestDecoder::default();
|
let mut reader = MessageDecoder::<Request>::default();
|
||||||
assert!{ reader.decode(&mut buf).unwrap().is_none() }
|
assert!{ reader.decode(&mut buf).unwrap().is_none() }
|
||||||
|
|
||||||
buf.extend(b"t");
|
buf.extend(b"t");
|
||||||
@ -890,7 +869,7 @@ mod tests {
|
|||||||
Set-Cookie: c1=cookie1\r\n\
|
Set-Cookie: c1=cookie1\r\n\
|
||||||
Set-Cookie: c2=cookie2\r\n\r\n",
|
Set-Cookie: c2=cookie2\r\n\r\n",
|
||||||
);
|
);
|
||||||
let mut reader = RequestDecoder::default();
|
let mut reader = MessageDecoder::<Request>::default();
|
||||||
let (req, _) = reader.decode(&mut buf).unwrap().unwrap();
|
let (req, _) = reader.decode(&mut buf).unwrap().unwrap();
|
||||||
|
|
||||||
let val: Vec<_> = req
|
let val: Vec<_> = req
|
||||||
@ -1090,7 +1069,7 @@ mod tests {
|
|||||||
upgrade: websocket\r\n\r\n\
|
upgrade: websocket\r\n\r\n\
|
||||||
some raw data",
|
some raw data",
|
||||||
);
|
);
|
||||||
let mut reader = RequestDecoder::default();
|
let mut reader = MessageDecoder::<Request>::default();
|
||||||
let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();
|
let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();
|
||||||
assert!(!req.keep_alive());
|
assert!(!req.keep_alive());
|
||||||
assert!(req.upgrade());
|
assert!(req.upgrade());
|
||||||
@ -1139,7 +1118,7 @@ mod tests {
|
|||||||
"GET /test HTTP/1.1\r\n\
|
"GET /test HTTP/1.1\r\n\
|
||||||
transfer-encoding: chunked\r\n\r\n",
|
transfer-encoding: chunked\r\n\r\n",
|
||||||
);
|
);
|
||||||
let mut reader = RequestDecoder::default();
|
let mut reader = MessageDecoder::<Request>::default();
|
||||||
let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();
|
let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();
|
||||||
let mut pl = pl.unwrap();
|
let mut pl = pl.unwrap();
|
||||||
assert!(req.chunked().unwrap());
|
assert!(req.chunked().unwrap());
|
||||||
@ -1162,7 +1141,7 @@ mod tests {
|
|||||||
"GET /test HTTP/1.1\r\n\
|
"GET /test HTTP/1.1\r\n\
|
||||||
transfer-encoding: chunked\r\n\r\n",
|
transfer-encoding: chunked\r\n\r\n",
|
||||||
);
|
);
|
||||||
let mut reader = RequestDecoder::default();
|
let mut reader = MessageDecoder::<Request>::default();
|
||||||
let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();
|
let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();
|
||||||
let mut pl = pl.unwrap();
|
let mut pl = pl.unwrap();
|
||||||
assert!(req.chunked().unwrap());
|
assert!(req.chunked().unwrap());
|
||||||
@ -1193,7 +1172,7 @@ mod tests {
|
|||||||
transfer-encoding: chunked\r\n\r\n",
|
transfer-encoding: chunked\r\n\r\n",
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut reader = RequestDecoder::default();
|
let mut reader = MessageDecoder::<Request>::default();
|
||||||
let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();
|
let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();
|
||||||
let mut pl = pl.unwrap();
|
let mut pl = pl.unwrap();
|
||||||
assert!(req.chunked().unwrap());
|
assert!(req.chunked().unwrap());
|
||||||
@ -1238,7 +1217,7 @@ mod tests {
|
|||||||
transfer-encoding: chunked\r\n\r\n"[..],
|
transfer-encoding: chunked\r\n\r\n"[..],
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut reader = RequestDecoder::default();
|
let mut reader = MessageDecoder::<Request>::default();
|
||||||
let (msg, pl) = reader.decode(&mut buf).unwrap().unwrap();
|
let (msg, pl) = reader.decode(&mut buf).unwrap().unwrap();
|
||||||
let mut pl = pl.unwrap();
|
let mut pl = pl.unwrap();
|
||||||
assert!(msg.chunked().unwrap());
|
assert!(msg.chunked().unwrap());
|
||||||
|
@ -11,7 +11,8 @@ use http::{StatusCode, Version};
|
|||||||
use body::{Binary, Body};
|
use body::{Binary, Body};
|
||||||
use header::ContentEncoding;
|
use header::ContentEncoding;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
use request::{Request, RequestHead};
|
use message::RequestHead;
|
||||||
|
use request::Request;
|
||||||
use response::Response;
|
use response::Response;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -11,7 +11,6 @@ mod service;
|
|||||||
|
|
||||||
pub use self::client::{ClientCodec, ClientPayloadCodec};
|
pub use self::client::{ClientCodec, ClientPayloadCodec};
|
||||||
pub use self::codec::Codec;
|
pub use self::codec::Codec;
|
||||||
pub use self::decoder::{PayloadDecoder, RequestDecoder};
|
|
||||||
pub use self::dispatcher::Dispatcher;
|
pub use self::dispatcher::Dispatcher;
|
||||||
pub use self::service::{H1Service, H1ServiceHandler, OneRequest};
|
pub use self::service::{H1Service, H1ServiceHandler, OneRequest};
|
||||||
|
|
||||||
|
@ -117,6 +117,7 @@ mod header;
|
|||||||
mod httpcodes;
|
mod httpcodes;
|
||||||
mod httpmessage;
|
mod httpmessage;
|
||||||
mod json;
|
mod json;
|
||||||
|
mod message;
|
||||||
mod payload;
|
mod payload;
|
||||||
mod request;
|
mod request;
|
||||||
mod response;
|
mod response;
|
||||||
|
163
src/message.rs
Normal file
163
src/message.rs
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
use std::cell::{Cell, RefCell};
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use http::{HeaderMap, Method, StatusCode, Uri, Version};
|
||||||
|
|
||||||
|
use extensions::Extensions;
|
||||||
|
use payload::Payload;
|
||||||
|
use uri::Url;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub trait Head: Default + 'static {
|
||||||
|
fn clear(&mut self);
|
||||||
|
|
||||||
|
fn pool() -> &'static MessagePool<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub(crate) struct MessageFlags: u8 {
|
||||||
|
const KEEPALIVE = 0b0000_0001;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RequestHead {
|
||||||
|
pub uri: Uri,
|
||||||
|
pub method: Method,
|
||||||
|
pub version: Version,
|
||||||
|
pub headers: HeaderMap,
|
||||||
|
pub(crate) flags: MessageFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RequestHead {
|
||||||
|
fn default() -> RequestHead {
|
||||||
|
RequestHead {
|
||||||
|
uri: Uri::default(),
|
||||||
|
method: Method::default(),
|
||||||
|
version: Version::HTTP_11,
|
||||||
|
headers: HeaderMap::with_capacity(16),
|
||||||
|
flags: MessageFlags::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Head for RequestHead {
|
||||||
|
fn clear(&mut self) {
|
||||||
|
self.headers.clear();
|
||||||
|
self.flags = MessageFlags::empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pool() -> &'static MessagePool<Self> {
|
||||||
|
REQUEST_POOL.with(|p| *p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ResponseHead {
|
||||||
|
pub version: Option<Version>,
|
||||||
|
pub status: StatusCode,
|
||||||
|
pub headers: HeaderMap,
|
||||||
|
pub reason: Option<&'static str>,
|
||||||
|
pub(crate) flags: MessageFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ResponseHead {
|
||||||
|
fn default() -> ResponseHead {
|
||||||
|
ResponseHead {
|
||||||
|
version: None,
|
||||||
|
status: StatusCode::OK,
|
||||||
|
headers: HeaderMap::with_capacity(16),
|
||||||
|
reason: None,
|
||||||
|
flags: MessageFlags::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Head for ResponseHead {
|
||||||
|
fn clear(&mut self) {
|
||||||
|
self.headers.clear();
|
||||||
|
self.flags = MessageFlags::empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pool() -> &'static MessagePool<Self> {
|
||||||
|
RESPONSE_POOL.with(|p| *p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Message<T: Head> {
|
||||||
|
pub head: T,
|
||||||
|
pub url: Url,
|
||||||
|
pub status: StatusCode,
|
||||||
|
pub extensions: RefCell<Extensions>,
|
||||||
|
pub payload: RefCell<Option<Payload>>,
|
||||||
|
pub(crate) pool: &'static MessagePool<T>,
|
||||||
|
pub(crate) flags: Cell<MessageFlags>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Head> Message<T> {
|
||||||
|
#[inline]
|
||||||
|
/// Reset request instance
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
self.head.clear();
|
||||||
|
self.extensions.borrow_mut().clear();
|
||||||
|
self.flags.set(MessageFlags::empty());
|
||||||
|
*self.payload.borrow_mut() = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Head> Default for Message<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Message {
|
||||||
|
pool: T::pool(),
|
||||||
|
url: Url::default(),
|
||||||
|
head: T::default(),
|
||||||
|
status: StatusCode::OK,
|
||||||
|
flags: Cell::new(MessageFlags::empty()),
|
||||||
|
payload: RefCell::new(None),
|
||||||
|
extensions: RefCell::new(Extensions::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
/// Request's objects pool
|
||||||
|
pub struct MessagePool<T: Head>(RefCell<VecDeque<Rc<Message<T>>>>);
|
||||||
|
|
||||||
|
thread_local!(static REQUEST_POOL: &'static MessagePool<RequestHead> = MessagePool::<RequestHead>::create());
|
||||||
|
thread_local!(static RESPONSE_POOL: &'static MessagePool<ResponseHead> = MessagePool::<ResponseHead>::create());
|
||||||
|
|
||||||
|
impl MessagePool<RequestHead> {
|
||||||
|
/// Get default request's pool
|
||||||
|
pub fn pool() -> &'static MessagePool<RequestHead> {
|
||||||
|
REQUEST_POOL.with(|p| *p)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get Request object
|
||||||
|
#[inline]
|
||||||
|
pub fn get_message() -> Rc<Message<RequestHead>> {
|
||||||
|
REQUEST_POOL.with(|pool| {
|
||||||
|
if let Some(mut msg) = pool.0.borrow_mut().pop_front() {
|
||||||
|
if let Some(r) = Rc::get_mut(&mut msg) {
|
||||||
|
r.reset();
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
Rc::new(Message::default())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Head> MessagePool<T> {
|
||||||
|
fn create() -> &'static MessagePool<T> {
|
||||||
|
let pool = MessagePool(RefCell::new(VecDeque::with_capacity(128)));
|
||||||
|
Box::leak(Box::new(pool))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Release request instance
|
||||||
|
pub(crate) fn release(&self, msg: Rc<Message<T>>) {
|
||||||
|
let v = &mut self.0.borrow_mut();
|
||||||
|
if v.len() < 128 {
|
||||||
|
v.push_front(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
148
src/request.rs
148
src/request.rs
@ -1,65 +1,18 @@
|
|||||||
use std::cell::{Cell, Ref, RefCell, RefMut};
|
use std::cell::{Ref, RefMut};
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use http::{header, HeaderMap, Method, StatusCode, Uri, Version};
|
use http::{header, HeaderMap, Method, Uri, Version};
|
||||||
|
|
||||||
use client::ClientResponse;
|
|
||||||
use extensions::Extensions;
|
use extensions::Extensions;
|
||||||
use httpmessage::HttpMessage;
|
use httpmessage::HttpMessage;
|
||||||
use payload::Payload;
|
use payload::Payload;
|
||||||
use uri::Url;
|
|
||||||
|
|
||||||
bitflags! {
|
use message::{Message, MessageFlags, MessagePool, RequestHead};
|
||||||
pub(crate) struct MessageFlags: u8 {
|
|
||||||
const KEEPALIVE = 0b0000_0001;
|
|
||||||
const CONN_INFO = 0b0000_0010;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Request
|
/// Request
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
pub(crate) inner: Rc<Message>,
|
pub(crate) inner: Rc<Message<RequestHead>>,
|
||||||
}
|
|
||||||
|
|
||||||
pub struct RequestHead {
|
|
||||||
pub uri: Uri,
|
|
||||||
pub method: Method,
|
|
||||||
pub version: Version,
|
|
||||||
pub headers: HeaderMap,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for RequestHead {
|
|
||||||
fn default() -> RequestHead {
|
|
||||||
RequestHead {
|
|
||||||
uri: Uri::default(),
|
|
||||||
method: Method::default(),
|
|
||||||
version: Version::HTTP_11,
|
|
||||||
headers: HeaderMap::with_capacity(16),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Message {
|
|
||||||
pub head: RequestHead,
|
|
||||||
pub url: Url,
|
|
||||||
pub status: StatusCode,
|
|
||||||
pub extensions: RefCell<Extensions>,
|
|
||||||
pub payload: RefCell<Option<Payload>>,
|
|
||||||
pub(crate) pool: &'static MessagePool,
|
|
||||||
pub(crate) flags: Cell<MessageFlags>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Message {
|
|
||||||
#[inline]
|
|
||||||
/// Reset request instance
|
|
||||||
pub fn reset(&mut self) {
|
|
||||||
self.head.clear();
|
|
||||||
self.extensions.borrow_mut().clear();
|
|
||||||
self.flags.set(MessageFlags::empty());
|
|
||||||
*self.payload.borrow_mut() = None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HttpMessage for Request {
|
impl HttpMessage for Request {
|
||||||
@ -82,33 +35,35 @@ impl HttpMessage for Request {
|
|||||||
impl Request {
|
impl Request {
|
||||||
/// Create new Request instance
|
/// Create new Request instance
|
||||||
pub fn new() -> Request {
|
pub fn new() -> Request {
|
||||||
Request::with_pool(MessagePool::pool())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create new Request instance with pool
|
|
||||||
pub(crate) fn with_pool(pool: &'static MessagePool) -> Request {
|
|
||||||
Request {
|
Request {
|
||||||
inner: Rc::new(Message {
|
inner: MessagePool::get_message(),
|
||||||
pool,
|
|
||||||
url: Url::default(),
|
|
||||||
head: RequestHead::default(),
|
|
||||||
status: StatusCode::OK,
|
|
||||||
flags: Cell::new(MessageFlags::empty()),
|
|
||||||
payload: RefCell::new(None),
|
|
||||||
extensions: RefCell::new(Extensions::new()),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// /// Create new Request instance with pool
|
||||||
|
// pub(crate) fn with_pool(pool: &'static MessagePool) -> Request {
|
||||||
|
// Request {
|
||||||
|
// inner: Rc::new(Message {
|
||||||
|
// pool,
|
||||||
|
// url: Url::default(),
|
||||||
|
// head: RequestHead::default(),
|
||||||
|
// status: StatusCode::OK,
|
||||||
|
// flags: Cell::new(MessageFlags::empty()),
|
||||||
|
// payload: RefCell::new(None),
|
||||||
|
// extensions: RefCell::new(Extensions::new()),
|
||||||
|
// }),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn inner(&self) -> &Message {
|
pub fn inner(&self) -> &Message<RequestHead> {
|
||||||
self.inner.as_ref()
|
self.inner.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn inner_mut(&mut self) -> &mut Message {
|
pub fn inner_mut(&mut self) -> &mut Message<RequestHead> {
|
||||||
Rc::get_mut(&mut self.inner).expect("Multiple copies exist")
|
Rc::get_mut(&mut self.inner).expect("Multiple copies exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,7 +94,11 @@ impl Request {
|
|||||||
/// The target path of this Request.
|
/// The target path of this Request.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn path(&self) -> &str {
|
pub fn path(&self) -> &str {
|
||||||
self.inner().url.path()
|
if let Some(path) = self.inner().url.path() {
|
||||||
|
path
|
||||||
|
} else {
|
||||||
|
self.inner().head.uri.path()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -219,56 +178,3 @@ impl fmt::Debug for Request {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request's objects pool
|
|
||||||
pub(crate) struct MessagePool(RefCell<VecDeque<Rc<Message>>>);
|
|
||||||
|
|
||||||
thread_local!(static POOL: &'static MessagePool = MessagePool::create());
|
|
||||||
|
|
||||||
impl MessagePool {
|
|
||||||
fn create() -> &'static MessagePool {
|
|
||||||
let pool = MessagePool(RefCell::new(VecDeque::with_capacity(128)));
|
|
||||||
Box::leak(Box::new(pool))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get default request's pool
|
|
||||||
pub fn pool() -> &'static MessagePool {
|
|
||||||
POOL.with(|p| *p)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get Request object
|
|
||||||
#[inline]
|
|
||||||
pub fn get_request(pool: &'static MessagePool) -> Request {
|
|
||||||
if let Some(mut msg) = pool.0.borrow_mut().pop_front() {
|
|
||||||
if let Some(r) = Rc::get_mut(&mut msg) {
|
|
||||||
r.reset();
|
|
||||||
}
|
|
||||||
return Request { inner: msg };
|
|
||||||
}
|
|
||||||
Request::with_pool(pool)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get Client Response object
|
|
||||||
#[inline]
|
|
||||||
pub fn get_response(pool: &'static MessagePool) -> ClientResponse {
|
|
||||||
if let Some(mut msg) = pool.0.borrow_mut().pop_front() {
|
|
||||||
if let Some(r) = Rc::get_mut(&mut msg) {
|
|
||||||
r.reset();
|
|
||||||
}
|
|
||||||
return ClientResponse {
|
|
||||||
inner: msg,
|
|
||||||
payload: RefCell::new(None),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
ClientResponse::with_pool(pool)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Release request instance
|
|
||||||
pub(crate) fn release(&self, msg: Rc<Message>) {
|
|
||||||
let v = &mut self.0.borrow_mut();
|
|
||||||
if v.len() < 128 {
|
|
||||||
v.push_front(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -392,7 +392,7 @@ impl TestRequest {
|
|||||||
{
|
{
|
||||||
let inner = req.inner_mut();
|
let inner = req.inner_mut();
|
||||||
inner.head.method = method;
|
inner.head.method = method;
|
||||||
inner.url = InnerUrl::new(uri);
|
inner.url = InnerUrl::new(&uri);
|
||||||
inner.head.version = version;
|
inner.head.version = version;
|
||||||
inner.head.headers = headers;
|
inner.head.headers = headers;
|
||||||
*inner.payload.borrow_mut() = payload;
|
*inner.payload.borrow_mut() = payload;
|
||||||
|
17
src/uri.rs
17
src/uri.rs
@ -37,27 +37,22 @@ lazy_static! {
|
|||||||
|
|
||||||
#[derive(Default, Clone, Debug)]
|
#[derive(Default, Clone, Debug)]
|
||||||
pub struct Url {
|
pub struct Url {
|
||||||
uri: Uri,
|
|
||||||
path: Option<Rc<String>>,
|
path: Option<Rc<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Url {
|
impl Url {
|
||||||
pub fn new(uri: Uri) -> Url {
|
pub fn new(uri: &Uri) -> Url {
|
||||||
let path = DEFAULT_QUOTER.requote(uri.path().as_bytes());
|
let path = DEFAULT_QUOTER.requote(uri.path().as_bytes());
|
||||||
|
|
||||||
Url { uri, path }
|
Url { path }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uri(&self) -> &Uri {
|
pub(crate) fn update(&mut self, uri: &Uri) {
|
||||||
&self.uri
|
self.path = DEFAULT_QUOTER.requote(uri.path().as_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn path(&self) -> &str {
|
pub fn path(&self) -> Option<&str> {
|
||||||
if let Some(ref s) = self.path {
|
self.path.as_ref().map(|s| s.as_str())
|
||||||
s
|
|
||||||
} else {
|
|
||||||
self.uri.path()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user