Various fixes in actix-http crate

This commit is contained in:
Igor Aleksanov 2021-06-22 19:49:39 +03:00
parent 282a66f327
commit 4cc8eafd3e
23 changed files with 100 additions and 46 deletions

View File

@ -78,12 +78,12 @@ impl HeaderIndex {
// test cases taken from:
// https://github.com/seanmonstar/httparse/blob/master/benches/parse.rs
const REQ_SHORT: &'static [u8] = b"\
const REQ_SHORT: &[u8] = b"\
GET / HTTP/1.0\r\n\
Host: example.com\r\n\
Cookie: session=60; user_id=1\r\n\r\n";
const REQ: &'static [u8] = b"\
const REQ: &[u8] = b"\
GET /wp-content/uploads/2010/03/hello-kitty-darth-vader-pink.jpg HTTP/1.1\r\n\
Host: www.kittyhell.com\r\n\
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; ja-JP-mac; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 Pathtraq/0.9\r\n\
@ -119,6 +119,8 @@ mod _original {
use std::mem::MaybeUninit;
pub fn parse_headers(src: &mut BytesMut) -> usize {
#![allow(clippy::uninit_assumed_init)]
let mut headers: [HeaderIndex; MAX_HEADERS] =
unsafe { MaybeUninit::uninit().assume_init() };

View File

@ -32,6 +32,7 @@ pub enum AnyBody {
impl AnyBody {
/// Create body from slice (copy)
#[must_use]
pub fn from_slice(s: &[u8]) -> Self {
Self::Bytes(Bytes::copy_from_slice(s))
}

View File

@ -36,6 +36,7 @@ where
<S::Service as Service<Request>>::Future: 'static,
{
/// Create instance of `ServiceConfigBuilder`
#[must_use]
pub fn new() -> Self {
HttpServiceBuilder {
keep_alive: KeepAlive::Timeout(5),

View File

@ -64,6 +64,7 @@ pub struct Connector<T> {
impl Connector<()> {
#[allow(clippy::new_ret_no_self, clippy::let_unit_value)]
#[must_use]
pub fn new() -> Connector<
impl Service<
TcpConnect<Uri>,
@ -85,7 +86,7 @@ impl Connector<()> {
use bytes::{BufMut, BytesMut};
let mut alpn = BytesMut::with_capacity(20);
for proto in protocols.iter() {
for proto in &protocols {
alpn.put_u8(proto.len() as u8);
alpn.put(proto.as_slice());
}
@ -290,8 +291,7 @@ where
let h2 = sock
.ssl()
.selected_alpn_protocol()
.map(|protos| protos.windows(2).any(|w| w == H2))
.unwrap_or(false);
.map_or(false, |protos| protos.windows(2).any(|w| w == H2));
if h2 {
(Box::new(sock), Protocol::Http2)
} else {
@ -325,8 +325,7 @@ where
.get_ref()
.1
.get_alpn_protocol()
.map(|protos| protos.windows(2).any(|w| w == H2))
.unwrap_or(false);
.map_or(false, |protos| protos.windows(2).any(|w| w == H2));
if h2 {
(Box::new(sock), Protocol::Http2)
} else {

View File

@ -168,14 +168,13 @@ where
if let Err(e) = send.send_data(bytes, false) {
return Err(e.into());
} else {
if !b.is_empty() {
send.reserve_capacity(b.len());
} else {
buf = None;
}
continue;
}
if !b.is_empty() {
send.reserve_capacity(b.len());
} else {
buf = None;
}
continue;
}
Some(Err(e)) => return Err(e.into()),
}

View File

@ -68,6 +68,7 @@ impl Default for ServiceConfig {
impl ServiceConfig {
/// Create instance of `ServiceConfig`
#[must_use]
pub fn new(
keep_alive: KeepAlive,
client_timeout: u64,
@ -99,30 +100,35 @@ impl ServiceConfig {
/// Returns true if connection is secure (HTTPS)
#[inline]
#[must_use]
pub fn secure(&self) -> bool {
self.0.secure
}
/// Returns the local address that this server is bound to.
#[inline]
#[must_use]
pub fn local_addr(&self) -> Option<net::SocketAddr> {
self.0.local_addr
}
/// Keep alive duration if configured.
#[inline]
#[must_use]
pub fn keep_alive(&self) -> Option<Duration> {
self.0.keep_alive
}
/// Return state of connection keep-alive functionality
#[inline]
#[must_use]
pub fn keep_alive_enabled(&self) -> bool {
self.0.ka_enabled
}
/// Client timeout for first request.
#[inline]
#[must_use]
pub fn client_timer(&self) -> Option<Sleep> {
let delay_time = self.0.client_timeout;
if delay_time != 0 {
@ -133,6 +139,7 @@ impl ServiceConfig {
}
/// Client timeout for first request.
#[must_use]
pub fn client_timer_expire(&self) -> Option<Instant> {
let delay = self.0.client_timeout;
if delay != 0 {
@ -143,6 +150,7 @@ impl ServiceConfig {
}
/// Client disconnect timer
#[must_use]
pub fn client_disconnect_timer(&self) -> Option<Instant> {
let delay = self.0.client_disconnect;
if delay != 0 {
@ -152,13 +160,15 @@ impl ServiceConfig {
}
}
#[inline]
/// Return keep-alive timer delay is configured.
#[inline]
#[must_use]
pub fn keep_alive_timer(&self) -> Option<Sleep> {
self.keep_alive().map(|ka| sleep_until(self.now() + ka))
}
/// Keep-alive expire time
#[must_use]
pub fn keep_alive_expire(&self) -> Option<Instant> {
self.keep_alive().map(|ka| self.now() + ka)
}
@ -365,11 +375,11 @@ mod tests {
let clone3 = service.clone();
drop(clone1);
assert_eq!(false, notify_on_drop::is_dropped());
assert!(!notify_on_drop::is_dropped());
drop(clone2);
assert_eq!(false, notify_on_drop::is_dropped());
assert!(!notify_on_drop::is_dropped());
drop(clone3);
assert_eq!(false, notify_on_drop::is_dropped());
assert!(!notify_on_drop::is_dropped());
drop(service);
assert!(notify_on_drop::is_dropped());

View File

@ -125,7 +125,7 @@ impl fmt::Display for Error {
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.inner.cause.as_ref().map(|err| err.as_ref())
self.inner.cause.as_ref().map(Box::as_ref)
}
}

View File

@ -18,6 +18,7 @@ pub struct Extensions {
impl Extensions {
/// Creates an empty `Extensions`.
#[inline]
#[must_use]
pub fn new() -> Extensions {
Extensions {
map: AHashMap::default(),
@ -52,6 +53,7 @@ impl Extensions {
/// assert_eq!(map.insert(1u32), None);
/// assert!(map.contains::<u32>());
/// ```
#[must_use]
pub fn contains<T: 'static>(&self) -> bool {
self.map.contains_key(&TypeId::of::<T>())
}
@ -64,6 +66,7 @@ impl Extensions {
/// map.insert(1u32);
/// assert_eq!(map.get::<u32>(), Some(&1u32));
/// ```
#[must_use]
pub fn get<T: 'static>(&self) -> Option<&T> {
self.map
.get(&TypeId::of::<T>())

View File

@ -53,6 +53,7 @@ impl ClientCodec {
/// Create HTTP/1 codec.
///
/// `keepalive_enabled` how response `connection` header get generated.
#[must_use]
pub fn new(config: ServiceConfig) -> Self {
let flags = if config.keep_alive_enabled() {
Flags::KEEPALIVE_ENABLED
@ -74,16 +75,19 @@ impl ClientCodec {
}
/// Check if request is upgrade
#[must_use]
pub fn upgrade(&self) -> bool {
self.inner.ctype == ConnectionType::Upgrade
}
/// Check if last response is keep-alive
#[must_use]
pub fn keepalive(&self) -> bool {
self.inner.ctype == ConnectionType::KeepAlive
}
/// Check last request's message type
#[must_use]
pub fn message_type(&self) -> MessageType {
if self.inner.flags.contains(Flags::STREAM) {
MessageType::Stream
@ -95,6 +99,7 @@ impl ClientCodec {
}
/// Convert message codec to a payload codec
#[must_use]
pub fn into_payload_codec(self) -> ClientPayloadCodec {
ClientPayloadCodec { inner: self.inner }
}
@ -102,11 +107,13 @@ impl ClientCodec {
impl ClientPayloadCodec {
/// Check if last response is keep-alive
#[must_use]
pub fn keepalive(&self) -> bool {
self.inner.ctype == ConnectionType::KeepAlive
}
/// Transform payload codec to a message codec
#[must_use]
pub fn into_message_codec(self) -> ClientCodec {
ClientCodec { inner: self.inner }
}

View File

@ -52,6 +52,7 @@ impl Codec {
/// Create HTTP/1 codec.
///
/// `keepalive_enabled` how response `connection` header get generated.
#[must_use]
pub fn new(config: ServiceConfig) -> Self {
let flags = if config.keep_alive_enabled() {
Flags::KEEPALIVE_ENABLED
@ -72,24 +73,28 @@ impl Codec {
/// Check if request is upgrade.
#[inline]
#[must_use]
pub fn upgrade(&self) -> bool {
self.ctype == ConnectionType::Upgrade
}
/// Check if last response is keep-alive.
#[inline]
#[must_use]
pub fn keepalive(&self) -> bool {
self.ctype == ConnectionType::KeepAlive
}
/// Check if keep-alive enabled on server level.
#[inline]
#[must_use]
pub fn keepalive_enabled(&self) -> bool {
self.flags.contains(Flags::KEEPALIVE_ENABLED)
}
/// Check last request's message type.
#[inline]
#[must_use]
pub fn message_type(&self) -> MessageType {
if self.flags.contains(Flags::STREAM) {
MessageType::Stream
@ -101,6 +106,7 @@ impl Codec {
}
#[inline]
#[must_use]
pub fn config(&self) -> &ServiceConfig {
&self.config
}

View File

@ -102,7 +102,7 @@ pub(crate) trait MessageType: Sized {
}
// transfer-encoding
header::TRANSFER_ENCODING => {
if let Ok(s) = value.to_str().map(|s| s.trim()) {
if let Ok(s) = value.to_str().map(str::trim) {
chunked = s.eq_ignore_ascii_case("chunked");
} else {
return Err(ParseError::Header);
@ -110,7 +110,7 @@ pub(crate) trait MessageType: Sized {
}
// connection keep-alive state
header::CONNECTION => {
ka = if let Ok(conn) = value.to_str().map(|conn| conn.trim()) {
ka = if let Ok(conn) = value.to_str().map(str::trim) {
if conn.eq_ignore_ascii_case("keep-alive") {
Some(ConnectionType::KeepAlive)
} else if conn.eq_ignore_ascii_case("close") {
@ -125,7 +125,7 @@ pub(crate) trait MessageType: Sized {
};
}
header::UPGRADE => {
if let Ok(val) = value.to_str().map(|val| val.trim()) {
if let Ok(val) = value.to_str().map(str::trim) {
if val.eq_ignore_ascii_case("websocket") {
has_upgrade_websocket = true;
}

View File

@ -515,14 +515,13 @@ where
cx: &mut Context<'_>,
) -> Result<(), DispatchError> {
// Handle `EXPECT: 100-Continue` header
// set dispatcher state so the future is pinned.
let mut this = self.as_mut().project();
if req.head().expect() {
// set dispatcher state so the future is pinned.
let mut this = self.as_mut().project();
let task = this.flow.expect.call(req);
this.state.set(State::ExpectCall(task));
} else {
// the same as above.
let mut this = self.as_mut().project();
let task = this.flow.service.call(req);
this.state.set(State::ServiceCall(task));
};

View File

@ -41,6 +41,7 @@ impl Payload {
/// * `PayloadSender` - *Sender* side of the stream
///
/// * `Payload` - *Receiver* side of the stream
#[must_use]
pub fn create(eof: bool) -> (PayloadSender, Payload) {
let shared = Rc::new(RefCell::new(Inner::new(eof)));
@ -54,6 +55,7 @@ impl Payload {
/// Create empty payload
#[doc(hidden)]
#[must_use]
pub fn empty() -> Payload {
Payload {
inner: Rc::new(RefCell::new(Inner::new(true))),
@ -186,8 +188,7 @@ impl Inner {
if self
.task
.as_ref()
.map(|w| !cx.waker().will_wake(w))
.unwrap_or(true)
.map_or(true, |w| !cx.waker().will_wake(w))
{
self.task = Some(cx.waker().clone());
}
@ -199,8 +200,7 @@ impl Inner {
if self
.io_task
.as_ref()
.map(|w| !cx.waker().will_wake(w))
.unwrap_or(true)
.map_or(true, |w| !cx.waker().will_wake(w))
{
self.io_task = Some(cx.waker().clone());
}

View File

@ -81,6 +81,7 @@ impl HeaderMap {
/// assert!(map.is_empty());
/// assert_eq!(0, map.capacity());
/// ```
#[must_use]
pub fn new() -> Self {
HeaderMap::default()
}
@ -98,6 +99,7 @@ impl HeaderMap {
/// assert!(map.is_empty());
/// assert!(map.capacity() >= 16);
/// ```
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
HeaderMap {
inner: AHashMap::with_capacity(capacity),
@ -150,6 +152,7 @@ impl HeaderMap {
/// map.append(header::SET_COOKIE, HeaderValue::from_static("two=2"));
/// assert_eq!(map.len(), 3);
/// ```
#[must_use]
pub fn len(&self) -> usize {
self.inner
.iter()
@ -173,6 +176,7 @@ impl HeaderMap {
/// map.append(header::SET_COOKIE, HeaderValue::from_static("two=2"));
/// assert_eq!(map.len_keys(), 2);
/// ```
#[must_use]
pub fn len_keys(&self) -> usize {
self.inner.len()
}
@ -188,6 +192,7 @@ impl HeaderMap {
/// map.insert(header::ACCEPT, HeaderValue::from_static("text/plain"));
/// assert!(!map.is_empty());
/// ```
#[must_use]
pub fn is_empty(&self) -> bool {
self.inner.len() == 0
}
@ -249,7 +254,7 @@ impl HeaderMap {
/// assert!(map.get("INVALID HEADER NAME").is_none());
/// ```
pub fn get(&self, key: impl AsHeaderName) -> Option<&HeaderValue> {
self.get_value(key).map(|val| val.first())
self.get_value(key).map(Value::first)
}
/// Returns a mutable reference to the _first_ value associated a header name.
@ -280,8 +285,8 @@ impl HeaderMap {
/// ```
pub fn get_mut(&mut self, key: impl AsHeaderName) -> Option<&mut HeaderValue> {
match key.try_as_name(super::as_name::Seal).ok()? {
Cow::Borrowed(name) => self.inner.get_mut(name).map(|v| v.first_mut()),
Cow::Owned(name) => self.inner.get_mut(&name).map(|v| v.first_mut()),
Cow::Borrowed(name) => self.inner.get_mut(name).map(Value::first_mut),
Cow::Owned(name) => self.inner.get_mut(&name).map(Value::first_mut),
}
}
@ -434,6 +439,7 @@ impl HeaderMap {
/// assert!(map.is_empty());
/// assert!(map.capacity() >= 16);
/// ```
#[must_use]
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
@ -489,6 +495,7 @@ impl HeaderMap {
/// assert!(pairs.contains(&(&header::SET_COOKIE, &HeaderValue::from_static("one=1"))));
/// assert!(pairs.contains(&(&header::SET_COOKIE, &HeaderValue::from_static("two=2"))));
/// ```
#[must_use]
pub fn iter(&self) -> Iter<'_> {
Iter::new(self.inner.iter())
}
@ -515,6 +522,7 @@ impl HeaderMap {
/// assert!(keys.contains(&header::HOST));
/// assert!(keys.contains(&header::SET_COOKIE));
/// ```
#[must_use]
pub fn keys(&self) -> Keys<'_> {
Keys(self.inner.keys())
}

View File

@ -33,12 +33,14 @@ pub enum ContentEncoding {
impl ContentEncoding {
/// Is the content compressed?
#[inline]
#[must_use]
pub fn is_compression(self) -> bool {
matches!(self, ContentEncoding::Identity | ContentEncoding::Auto)
}
/// Convert content encoding to string
#[inline]
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
ContentEncoding::Br => "br",
@ -51,6 +53,7 @@ impl ContentEncoding {
/// Default Q-factor (quality) value.
#[inline]
#[must_use]
pub fn quality(self) -> f64 {
match self {
ContentEncoding::Br => 1.1,

View File

@ -152,15 +152,16 @@ impl RequestHead {
/// Connection upgrade status
pub fn upgrade(&self) -> bool {
if let Some(hdr) = self.headers().get(header::CONNECTION) {
if let Ok(s) = hdr.to_str() {
s.to_ascii_lowercase().contains("upgrade")
} else {
false
}
} else {
false
}
self.headers()
.get(header::CONNECTION)
.map(|hdr| {
if let Ok(s) = hdr.to_str() {
s.to_ascii_lowercase().contains("upgrade")
} else {
false
}
})
.unwrap_or(false)
}
#[inline]
@ -233,6 +234,7 @@ pub struct ResponseHead {
impl ResponseHead {
/// Create new instance of `ResponseHead` type
#[inline]
#[must_use]
pub fn new(status: StatusCode) -> ResponseHead {
ResponseHead {
status,
@ -308,13 +310,11 @@ impl ResponseHead {
/// Get custom reason for the response
#[inline]
pub fn reason(&self) -> &str {
if let Some(reason) = self.reason {
reason
} else {
self.reason.unwrap_or_else(|| {
self.status
.canonical_reason()
.unwrap_or("<unknown status code>")
}
})
}
#[inline]
@ -355,8 +355,9 @@ pub struct Message<T: Head> {
impl<T: Head> Message<T> {
/// Get new message from the pool of objects
#[must_use]
pub fn new() -> Self {
T::with_pool(|p| p.get_message())
T::with_pool(MessagePool::get_message)
}
}

View File

@ -57,6 +57,7 @@ impl From<Message<RequestHead>> for Request<PayloadStream> {
impl Request<PayloadStream> {
/// Create new Request instance
#[must_use]
pub fn new() -> Request<PayloadStream> {
Request {
head: Message::new(),

View File

@ -25,6 +25,7 @@ pub struct Response<B> {
impl Response<AnyBody> {
/// Constructs a new response with default body.
#[inline]
#[must_use]
pub fn new(status: StatusCode) -> Self {
Response {
head: BoxedResponseHead::new(status),
@ -34,6 +35,7 @@ impl Response<AnyBody> {
/// Constructs a new response builder.
#[inline]
#[must_use]
pub fn build(status: StatusCode) -> ResponseBuilder {
ResponseBuilder::new(status)
}
@ -43,24 +45,28 @@ impl Response<AnyBody> {
/// Constructs a new response with status 200 OK.
#[inline]
#[must_use]
pub fn ok() -> Self {
Response::new(StatusCode::OK)
}
/// Constructs a new response with status 400 Bad Request.
#[inline]
#[must_use]
pub fn bad_request() -> Self {
Response::new(StatusCode::BAD_REQUEST)
}
/// Constructs a new response with status 404 Not Found.
#[inline]
#[must_use]
pub fn not_found() -> Self {
Response::new(StatusCode::NOT_FOUND)
}
/// Constructs a new response with status 500 Internal Server Error.
#[inline]
#[must_use]
pub fn internal_server_error() -> Self {
Response::new(StatusCode::INTERNAL_SERVER_ERROR)
}

View File

@ -62,6 +62,7 @@ impl ResponseBuilder {
/// assert_eq!(res.status(), StatusCode::OK);
/// ```
#[inline]
#[must_use]
pub fn new(status: StatusCode) -> Self {
ResponseBuilder {
head: Some(BoxedResponseHead::new(status)),
@ -220,6 +221,7 @@ impl ResponseBuilder {
/// Responses extensions
#[inline]
#[must_use]
pub fn extensions(&self) -> Ref<'_, Extensions> {
let head = self.head.as_ref().expect("cannot reuse response builder");
head.extensions.borrow()

View File

@ -47,6 +47,7 @@ where
B: MessageBody + 'static,
{
/// Create builder for `HttpService` instance.
#[must_use]
pub fn build() -> HttpServiceBuilder<T, S> {
HttpServiceBuilder::new()
}

View File

@ -80,6 +80,7 @@ bitflags! {
impl Codec {
/// Create new WebSocket frames decoder.
#[must_use]
pub const fn new() -> Codec {
Codec {
max_size: 65_536,
@ -90,6 +91,7 @@ impl Codec {
/// Set max frame size.
///
/// By default max size is set to 64kB.
#[must_use]
pub fn max_size(mut self, size: usize) -> Self {
self.max_size = size;
self
@ -98,6 +100,7 @@ impl Codec {
/// Set decoder to client mode.
///
/// By default decoder works in server mode.
#[must_use]
pub fn client_mode(mut self) -> Self {
self.flags.remove(Flags::SERVER);
self

View File

@ -139,6 +139,7 @@ impl Parser {
}
/// Parse the payload of a close frame.
#[must_use]
pub fn parse_close_payload(payload: &[u8]) -> Option<CloseReason> {
if payload.len() >= 2 {
let raw_code = u16::from_be_bytes(TryFrom::try_from(&payload[..2]).unwrap());

View File

@ -226,6 +226,7 @@ static WS_GUID: &[u8] = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
/// Hashes the `Sec-WebSocket-Key` header according to the WebSocket spec.
///
/// Result is a Base64 encoded byte array. `base64(sha1(input))` is always 28 bytes.
#[must_use]
pub fn hash_key(key: &[u8]) -> [u8; 28] {
let hash = {
use sha1::Digest as _;