1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-07-01 16:55:08 +02:00

Merge branch 'master' into asonix/play-with-h1-encoding

This commit is contained in:
asonix
2024-11-04 17:36:28 -06:00
167 changed files with 4670 additions and 2263 deletions

View File

@ -2,6 +2,27 @@
## Unreleased
### Added
- Add `header::CLEAR_SITE_DATA` constant.
### Changed
- Update `brotli` dependency to `7`.
- Minimum supported Rust version (MSRV) is now 1.75.
## 3.9.0
### Added
- Implement `FromIterator<(HeaderName, HeaderValue)>` for `HeaderMap`.
## 3.8.0
### Added
- Add `error::InvalidStatusCode` re-export.
## 3.7.0
### Added

View File

@ -1,6 +1,6 @@
[package]
name = "actix-http"
version = "3.7.0"
version = "3.9.0"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>",
@ -34,51 +34,72 @@ features = [
"compress-zstd",
]
[lib]
name = "actix_http"
path = "src/lib.rs"
[package.metadata.cargo_check_external_types]
allowed_external_types = [
"actix_codec::*",
"actix_service::*",
"actix_tls::*",
"actix_utils::*",
"bytes::*",
"bytestring::*",
"encoding_rs::*",
"futures_core::*",
"h2::*",
"http::*",
"httparse::*",
"language_tags::*",
"mime::*",
"openssl::*",
"rustls::*",
"tokio_util::*",
"tokio::*",
]
[features]
default = []
# HTTP/2 protocol support
http2 = ["h2"]
http2 = ["dep:h2"]
# WebSocket protocol implementation
ws = [
"local-channel",
"base64",
"rand",
"sha1",
"dep:local-channel",
"dep:base64",
"dep:rand",
"dep:sha1",
]
# TLS via OpenSSL
openssl = ["actix-tls/accept", "actix-tls/openssl"]
openssl = ["__tls", "actix-tls/accept", "actix-tls/openssl"]
# TLS via Rustls v0.20
rustls = ["rustls-0_20"]
rustls = ["__tls", "rustls-0_20"]
# TLS via Rustls v0.20
rustls-0_20 = ["actix-tls/accept", "actix-tls/rustls-0_20"]
rustls-0_20 = ["__tls", "actix-tls/accept", "actix-tls/rustls-0_20"]
# TLS via Rustls v0.21
rustls-0_21 = ["actix-tls/accept", "actix-tls/rustls-0_21"]
rustls-0_21 = ["__tls", "actix-tls/accept", "actix-tls/rustls-0_21"]
# TLS via Rustls v0.22
rustls-0_22 = ["actix-tls/accept", "actix-tls/rustls-0_22"]
rustls-0_22 = ["__tls", "actix-tls/accept", "actix-tls/rustls-0_22"]
# TLS via Rustls v0.23
rustls-0_23 = ["actix-tls/accept", "actix-tls/rustls-0_23"]
rustls-0_23 = ["__tls", "actix-tls/accept", "actix-tls/rustls-0_23"]
# Compression codecs
compress-brotli = ["__compress", "brotli"]
compress-gzip = ["__compress", "flate2"]
compress-zstd = ["__compress", "zstd"]
compress-brotli = ["__compress", "dep:brotli"]
compress-gzip = ["__compress", "dep:flate2"]
compress-zstd = ["__compress", "dep:zstd"]
# Internal (PRIVATE!) features used to aid testing and checking feature status.
# Don't rely on these whatsoever. They are semver-exempt and may disappear at anytime.
__compress = []
# Internal (PRIVATE!) features used to aid checking feature status.
# Don't rely on these whatsoever. They may disappear at anytime.
__tls = []
[dependencies]
actix-service = "2"
actix-codec = "0.5"
@ -89,7 +110,7 @@ ahash = "0.8"
bitflags = "2"
bytes = "1.7"
bytestring = "1"
derive_more = "0.99.5"
derive_more = { version = "1", features = ["as_ref", "deref", "deref_mut", "display", "error", "from"] }
encoding_rs = "0.8"
futures-core = { version = "0.3.17", default-features = false, features = ["alloc"] }
http = "0.2.7"
@ -106,7 +127,7 @@ tokio-util = { version = "0.7", features = ["io", "codec"] }
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
# http2
h2 = { version = "0.3.24", optional = true }
h2 = { version = "0.3.26", optional = true }
# websockets
local-channel = { version = "0.1", optional = true }
@ -118,7 +139,7 @@ sha1 = { version = "0.10", optional = true }
actix-tls = { version = "3.4", default-features = false, optional = true }
# compress-*
brotli = { version = "6", optional = true }
brotli = { version = "7", optional = true }
flate2 = { version = "1.0.13", optional = true }
zstd = { version = "0.13", optional = true }
@ -139,13 +160,16 @@ rcgen = "0.13"
regex = "1.3"
rustversion = "1"
rustls-pemfile = "2"
serde = { version = "1.0", features = ["derive"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1.0"
static_assertions = "1"
tls-openssl = { package = "openssl", version = "0.10.55" }
tls-rustls_023 = { package = "rustls", version = "0.23" }
tokio = { version = "1.24.2", features = ["net", "rt", "macros"] }
[lints]
workspace = true
[[example]]
name = "ws"
required-features = ["ws", "rustls-0_23"]

View File

@ -5,11 +5,11 @@
<!-- prettier-ignore-start -->
[![crates.io](https://img.shields.io/crates/v/actix-http?label=latest)](https://crates.io/crates/actix-http)
[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.7.0)](https://docs.rs/actix-http/3.7.0)
[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.9.0)](https://docs.rs/actix-http/3.9.0)
![Version](https://img.shields.io/badge/rustc-1.72+-ab6000.svg)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http.svg)
<br />
[![dependency status](https://deps.rs/crate/actix-http/3.7.0/status.svg)](https://deps.rs/crate/actix-http/3.7.0)
[![dependency status](https://deps.rs/crate/actix-http/3.9.0/status.svg)](https://deps.rs/crate/actix-http/3.9.0)
[![Download](https://img.shields.io/crates/d/actix-http.svg)](https://crates.io/crates/actix-http)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)

View File

@ -3,13 +3,13 @@ use std::sync::OnceLock;
use actix_http::HttpService;
use actix_server::Server;
use actix_service::map_config;
use actix_web::{dev::AppConfig, get, App};
use actix_web::{dev::AppConfig, get, App, Responder};
static MEDIUM: OnceLock<String> = OnceLock::new();
static LARGE: OnceLock<String> = OnceLock::new();
#[get("/")]
async fn index() -> &'static str {
async fn index() -> impl Responder {
"Hello, world. From Actix Web!"
}

View File

@ -23,7 +23,7 @@ async fn main() -> io::Result<()> {
body.extend_from_slice(&item?);
}
info!("request body: {:?}", body);
info!("request body: {body:?}");
let res = Response::build(StatusCode::OK)
.insert_header(("x-head", HeaderValue::from_static("dummy value!")))
@ -31,8 +31,7 @@ async fn main() -> io::Result<()> {
Ok::<_, Error>(res)
})
// No TLS
.tcp()
.tcp() // No TLS
})?
.run()
.await

View File

@ -17,7 +17,7 @@ async fn main() -> io::Result<()> {
ext.insert(42u32);
})
.finish(|req: Request| async move {
info!("{:?}", req);
info!("{req:?}");
let mut res = Response::build(StatusCode::OK);
res.insert_header(("x-head", HeaderValue::from_static("dummy value!")));

View File

@ -22,16 +22,16 @@ async fn main() -> io::Result<()> {
.bind("streaming-error", ("127.0.0.1", 8080), || {
HttpService::build()
.finish(|req| async move {
info!("{:?}", req);
info!("{req:?}");
let res = Response::ok();
Ok::<_, Infallible>(res.set_body(BodyStream::new(stream! {
yield Ok(Bytes::from("123"));
yield Ok(Bytes::from("456"));
actix_rt::time::sleep(Duration::from_millis(1000)).await;
actix_rt::time::sleep(Duration::from_secs(1)).await;
yield Err(io::Error::new(io::ErrorKind::Other, ""));
yield Err(io::Error::new(io::ErrorKind::Other, "abc"));
})))
})
.tcp()

View File

@ -17,7 +17,6 @@ use bytes::{Bytes, BytesMut};
use bytestring::ByteString;
use futures_core::{ready, Stream};
use tokio_util::codec::Encoder;
use tracing::{info, trace};
#[actix_rt::main]
async fn main() -> io::Result<()> {
@ -37,12 +36,12 @@ async fn main() -> io::Result<()> {
}
async fn handler(req: Request) -> Result<Response<BodyStream<Heartbeat>>, Error> {
info!("handshaking");
tracing::info!("handshaking");
let mut res = ws::handshake(req.head())?;
// handshake will always fail under HTTP/2
info!("responding");
tracing::info!("responding");
res.message_body(BodyStream::new(Heartbeat::new(ws::Codec::new())))
}
@ -64,7 +63,7 @@ impl Stream for Heartbeat {
type Item = Result<Bytes, Error>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
trace!("poll");
tracing::trace!("poll");
ready!(self.as_mut().interval.poll_tick(cx));

View File

@ -75,7 +75,7 @@ mod tests {
time::{sleep, Sleep},
};
use actix_utils::future::poll_fn;
use derive_more::{Display, Error};
use derive_more::derive::{Display, Error};
use futures_core::ready;
use futures_util::{stream, FutureExt as _};
use pin_project_lite::pin_project;
@ -131,7 +131,7 @@ mod tests {
assert_eq!(to_bytes(body).await.ok(), Some(Bytes::from("12")));
}
#[derive(Debug, Display, Error)]
#[display(fmt = "stream error")]
#[display("stream error")]
struct StreamErr;
#[actix_rt::test]

View File

@ -3,7 +3,7 @@ use std::task::Poll;
use actix_rt::pin;
use actix_utils::future::poll_fn;
use bytes::{Bytes, BytesMut};
use derive_more::{Display, Error};
use derive_more::derive::{Display, Error};
use futures_core::ready;
use super::{BodySize, MessageBody};
@ -38,7 +38,7 @@ pub async fn to_bytes<B: MessageBody>(body: B) -> Result<Bytes, B::Error> {
/// Error type returned from [`to_bytes_limited`] when body produced exceeds limit.
#[derive(Debug, Display, Error)]
#[display(fmt = "limit exceeded while collecting body bytes")]
#[display("limit exceeded while collecting body bytes")]
#[non_exhaustive]
pub struct BodyLimitExceeded;

View File

@ -10,7 +10,7 @@ use std::{
use actix_rt::task::{spawn_blocking, JoinHandle};
use bytes::Bytes;
use derive_more::Display;
use derive_more::derive::Display;
#[cfg(feature = "compress-gzip")]
use flate2::write::{GzEncoder, ZlibEncoder};
use futures_core::ready;
@ -415,11 +415,11 @@ fn new_brotli_compressor() -> Box<brotli::CompressorWriter<Writer>> {
#[non_exhaustive]
pub enum EncoderError {
/// Wrapped body stream error.
#[display(fmt = "body")]
#[display("body")]
Body(Box<dyn StdError>),
/// Generic I/O error.
#[display(fmt = "io")]
#[display("io")]
Io(io::Error),
}

View File

@ -2,8 +2,8 @@
use std::{error::Error as StdError, fmt, io, str::Utf8Error, string::FromUtf8Error};
use derive_more::{Display, Error, From};
pub use http::Error as HttpError;
use derive_more::derive::{Display, Error, From};
pub use http::{status::InvalidStatusCode, Error as HttpError};
use http::{uri::InvalidUri, StatusCode};
use crate::{body::BoxBody, Response};
@ -80,28 +80,28 @@ impl From<Error> for Response<BoxBody> {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Display)]
pub(crate) enum Kind {
#[display(fmt = "error processing HTTP")]
#[display("error processing HTTP")]
Http,
#[display(fmt = "error parsing HTTP message")]
#[display("error parsing HTTP message")]
Parse,
#[display(fmt = "request payload read error")]
#[display("request payload read error")]
Payload,
#[display(fmt = "response body write error")]
#[display("response body write error")]
Body,
#[display(fmt = "send response error")]
#[display("send response error")]
SendResponse,
#[display(fmt = "error in WebSocket process")]
#[display("error in WebSocket process")]
Ws,
#[display(fmt = "connection error")]
#[display("connection error")]
Io,
#[display(fmt = "encoder error")]
#[display("encoder error")]
Encoder,
}
@ -160,44 +160,44 @@ impl From<crate::ws::ProtocolError> for Error {
#[non_exhaustive]
pub enum ParseError {
/// An invalid `Method`, such as `GE.T`.
#[display(fmt = "invalid method specified")]
#[display("invalid method specified")]
Method,
/// An invalid `Uri`, such as `exam ple.domain`.
#[display(fmt = "URI error: {}", _0)]
#[display("URI error: {}", _0)]
Uri(InvalidUri),
/// An invalid `HttpVersion`, such as `HTP/1.1`
#[display(fmt = "invalid HTTP version specified")]
#[display("invalid HTTP version specified")]
Version,
/// An invalid `Header`.
#[display(fmt = "invalid Header provided")]
#[display("invalid Header provided")]
Header,
/// A message head is too large to be reasonable.
#[display(fmt = "message head is too large")]
#[display("message head is too large")]
TooLarge,
/// A message reached EOF, but is not complete.
#[display(fmt = "message is incomplete")]
#[display("message is incomplete")]
Incomplete,
/// An invalid `Status`, such as `1337 ELITE`.
#[display(fmt = "invalid status provided")]
#[display("invalid status provided")]
Status,
/// A timeout occurred waiting for an IO event.
#[allow(dead_code)]
#[display(fmt = "timeout")]
#[display("timeout")]
Timeout,
/// An I/O error that occurred while trying to read or write to a network stream.
#[display(fmt = "I/O error: {}", _0)]
#[display("I/O error: {}", _0)]
Io(io::Error),
/// Parsing a field as string failed.
#[display(fmt = "UTF-8 error: {}", _0)]
#[display("UTF-8 error: {}", _0)]
Utf8(Utf8Error),
}
@ -256,28 +256,28 @@ impl From<ParseError> for Response<BoxBody> {
#[non_exhaustive]
pub enum PayloadError {
/// A payload reached EOF, but is not complete.
#[display(fmt = "payload reached EOF before completing: {:?}", _0)]
#[display("payload reached EOF before completing: {:?}", _0)]
Incomplete(Option<io::Error>),
/// Content encoding stream corruption.
#[display(fmt = "can not decode content-encoding")]
#[display("can not decode content-encoding")]
EncodingCorrupted,
/// Payload reached size limit.
#[display(fmt = "payload reached size limit")]
#[display("payload reached size limit")]
Overflow,
/// Payload length is unknown.
#[display(fmt = "payload length is unknown")]
#[display("payload length is unknown")]
UnknownLength,
/// HTTP/2 payload error.
#[cfg(feature = "http2")]
#[display(fmt = "{}", _0)]
#[display("{}", _0)]
Http2Payload(::h2::Error),
/// Generic I/O error.
#[display(fmt = "{}", _0)]
#[display("{}", _0)]
Io(io::Error),
}
@ -326,44 +326,44 @@ impl From<PayloadError> for Error {
#[non_exhaustive]
pub enum DispatchError {
/// Service error.
#[display(fmt = "service error")]
#[display("service error")]
Service(Response<BoxBody>),
/// Body streaming error.
#[display(fmt = "body error: {}", _0)]
#[display("body error: {}", _0)]
Body(Box<dyn StdError>),
/// Upgrade service error.
#[display(fmt = "upgrade error")]
#[display("upgrade error")]
Upgrade,
/// An `io::Error` that occurred while trying to read or write to a network stream.
#[display(fmt = "I/O error: {}", _0)]
#[display("I/O error: {}", _0)]
Io(io::Error),
/// Request parse error.
#[display(fmt = "request parse error: {}", _0)]
#[display("request parse error: {}", _0)]
Parse(ParseError),
/// HTTP/2 error.
#[display(fmt = "{}", _0)]
#[display("{}", _0)]
#[cfg(feature = "http2")]
H2(h2::Error),
/// The first request did not complete within the specified timeout.
#[display(fmt = "request did not complete within the specified timeout")]
#[display("request did not complete within the specified timeout")]
SlowRequestTimeout,
/// Disconnect timeout. Makes sense for TLS streams.
#[display(fmt = "connection shutdown timeout")]
#[display("connection shutdown timeout")]
DisconnectTimeout,
/// Handler dropped payload before reading EOF.
#[display(fmt = "handler dropped payload before reading EOF")]
#[display("handler dropped payload before reading EOF")]
HandlerDroppedPayload,
/// Internal error.
#[display(fmt = "internal error")]
#[display("internal error")]
InternalError,
}
@ -389,11 +389,11 @@ impl StdError for DispatchError {
#[non_exhaustive]
pub enum ContentTypeError {
/// Can not parse content type.
#[display(fmt = "could not parse content type")]
#[display("could not parse content type")]
ParseError,
/// Unknown content encoding.
#[display(fmt = "unknown content encoding")]
#[display("unknown content encoding")]
UnknownEncoding,
}

View File

@ -314,7 +314,7 @@ impl MessageType for RequestHeadType {
_ => return Err(io::Error::new(io::ErrorKind::Other, "unsupported version")),
}
)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
}
}
@ -499,7 +499,7 @@ impl TransferEncoding {
buf.extend_from_slice(b"0\r\n\r\n");
} else {
writeln!(helpers::MutWriter(buf), "{:X}\r", msg.len())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
buf.reserve(msg.len() + 2);
buf.extend_from_slice(msg);

View File

@ -480,15 +480,15 @@ where
let cfg = self.cfg.clone();
Box::pin(async move {
let expect = expect
.await
.map_err(|e| error!("Init http expect service error: {:?}", e))?;
let expect = expect.await.map_err(|err| {
tracing::error!("Initialization of HTTP expect service error: {err:?}");
})?;
let upgrade = match upgrade {
Some(upgrade) => {
let upgrade = upgrade
.await
.map_err(|e| error!("Init http upgrade service error: {:?}", e))?;
let upgrade = upgrade.await.map_err(|err| {
tracing::error!("Initialization of HTTP upgrade service error: {err:?}");
})?;
Some(upgrade)
}
None => None,
@ -496,7 +496,7 @@ where
let service = service
.await
.map_err(|e| error!("Init http service error: {:?}", e))?;
.map_err(|err| error!("Initialization of HTTP service error: {err:?}"))?;
Ok(H1ServiceHandler::new(
cfg,
@ -541,6 +541,6 @@ where
fn call(&self, (io, addr): (T, Option<net::SocketAddr>)) -> Self::Future {
let conn_data = OnConnectData::from_io(&io, self.on_connect_ext.as_deref());
Dispatcher::new(io, self.flow.clone(), self.cfg.clone(), addr, conn_data)
Dispatcher::new(io, Rc::clone(&self.flow), self.cfg.clone(), addr, conn_data)
}
}

View File

@ -434,7 +434,7 @@ where
H2ServiceHandlerResponse {
state: State::Handshake(
Some(self.flow.clone()),
Some(Rc::clone(&self.flow)),
Some(self.cfg.clone()),
addr,
on_connect_data,

View File

@ -18,6 +18,14 @@ pub const CACHE_STATUS: HeaderName = HeaderName::from_static("cache-status");
// TODO(breaking): replace with http's version
pub const CDN_CACHE_CONTROL: HeaderName = HeaderName::from_static("cdn-cache-control");
/// Response header field that sends a signal to the user agent that it ought to remove all data of
/// a certain set of types.
///
/// See the [W3C Clear-Site-Data spec] for full semantics.
///
/// [W3C Clear-Site-Data spec]: https://www.w3.org/TR/clear-site-data/#header
pub const CLEAR_SITE_DATA: HeaderName = HeaderName::from_static("clear-site-data");
/// Response header that prevents a document from loading any cross-origin resources that don't
/// explicitly grant the document permission (using [CORP] or [CORS]).
///

View File

@ -13,8 +13,9 @@ use super::AsHeaderName;
/// `HeaderMap` is a "multi-map" of [`HeaderName`] to one or more [`HeaderValue`]s.
///
/// # Examples
///
/// ```
/// use actix_http::header::{self, HeaderMap, HeaderValue};
/// # use actix_http::header::{self, HeaderMap, HeaderValue};
///
/// let mut map = HeaderMap::new();
///
@ -29,6 +30,21 @@ use super::AsHeaderName;
///
/// assert!(!map.contains_key(header::ORIGIN));
/// ```
///
/// Construct a header map using the [`FromIterator`] implementation. Note that it uses the append
/// strategy, so duplicate header names are preserved.
///
/// ```
/// use actix_http::header::{self, HeaderMap, HeaderValue};
///
/// let headers = HeaderMap::from_iter([
/// (header::CONTENT_TYPE, HeaderValue::from_static("text/plain")),
/// (header::COOKIE, HeaderValue::from_static("foo=1")),
/// (header::COOKIE, HeaderValue::from_static("bar=1")),
/// ]);
///
/// assert_eq!(headers.len(), 3);
/// ```
#[derive(Debug, Clone, Default)]
pub struct HeaderMap {
pub(crate) inner: AHashMap<HeaderName, Value>,
@ -368,8 +384,8 @@ impl HeaderMap {
/// let removed = map.insert(header::ACCEPT, HeaderValue::from_static("text/html"));
/// assert!(!removed.is_empty());
/// ```
pub fn insert(&mut self, key: HeaderName, val: HeaderValue) -> Removed {
let value = self.inner.insert(key, Value::one(val));
pub fn insert(&mut self, name: HeaderName, val: HeaderValue) -> Removed {
let value = self.inner.insert(name, Value::one(val));
Removed::new(value)
}
@ -636,6 +652,16 @@ impl<'a> IntoIterator for &'a HeaderMap {
}
}
impl FromIterator<(HeaderName, HeaderValue)> for HeaderMap {
fn from_iter<T: IntoIterator<Item = (HeaderName, HeaderValue)>>(iter: T) -> Self {
iter.into_iter()
.fold(Self::new(), |mut map, (name, value)| {
map.append(name, value);
map
})
}
}
/// Convert a `http::HeaderMap` to our `HeaderMap`.
impl From<http::HeaderMap> for HeaderMap {
fn from(mut map: http::HeaderMap) -> Self {

View File

@ -42,9 +42,9 @@ pub use self::{
as_name::AsHeaderName,
// re-export list is explicit so that any updates to `http` do not conflict with this set
common::{
CACHE_STATUS, CDN_CACHE_CONTROL, CROSS_ORIGIN_EMBEDDER_POLICY, CROSS_ORIGIN_OPENER_POLICY,
CROSS_ORIGIN_RESOURCE_POLICY, PERMISSIONS_POLICY, X_FORWARDED_FOR, X_FORWARDED_HOST,
X_FORWARDED_PROTO,
CACHE_STATUS, CDN_CACHE_CONTROL, CLEAR_SITE_DATA, CROSS_ORIGIN_EMBEDDER_POLICY,
CROSS_ORIGIN_OPENER_POLICY, CROSS_ORIGIN_RESOURCE_POLICY, PERMISSIONS_POLICY,
X_FORWARDED_FOR, X_FORWARDED_HOST, X_FORWARDED_PROTO,
},
into_pair::TryIntoHeaderPair,
into_value::TryIntoHeaderValue,

View File

@ -1,6 +1,6 @@
use std::str::FromStr;
use derive_more::{Display, Error};
use derive_more::derive::{Display, Error};
use http::header::InvalidHeaderValue;
use crate::{
@ -11,7 +11,7 @@ use crate::{
/// Error returned when a content encoding is unknown.
#[derive(Debug, Display, Error)]
#[display(fmt = "unsupported content encoding")]
#[display("unsupported content encoding")]
pub struct ContentEncodingParseError;
/// Represents a supported content encoding.

View File

@ -1,6 +1,6 @@
use std::fmt;
use derive_more::{Display, Error};
use derive_more::derive::{Display, Error};
const MAX_QUALITY_INT: u16 = 1000;
const MAX_QUALITY_FLOAT: f32 = 1.0;
@ -125,7 +125,7 @@ pub fn itoa_fmt<W: fmt::Write, V: itoa::Integer>(mut wr: W, value: V) -> fmt::Re
}
#[derive(Debug, Clone, Display, Error)]
#[display(fmt = "quality out of bounds")]
#[display("quality out of bounds")]
#[non_exhaustive]
pub struct QualityOutOfBounds;

View File

@ -6,10 +6,10 @@
//! | ------------------- | ------------------------------------------- |
//! | `http2` | HTTP/2 support via [h2]. |
//! | `openssl` | TLS support via [OpenSSL]. |
//! | `rustls` | TLS support via [rustls] 0.20. |
//! | `rustls-0_21` | TLS support via [rustls] 0.21. |
//! | `rustls-0_22` | TLS support via [rustls] 0.22. |
//! | `rustls-0_23` | TLS support via [rustls] 0.23. |
//! | `rustls-0_20` | TLS support via rustls 0.20. |
//! | `rustls-0_21` | TLS support via rustls 0.21. |
//! | `rustls-0_22` | TLS support via rustls 0.22. |
//! | `rustls-0_23` | TLS support via [rustls] 0.23. |
//! | `compress-brotli` | Payload compression support: Brotli. |
//! | `compress-gzip` | Payload compression support: Deflate, Gzip. |
//! | `compress-zstd` | Payload compression support: Zstd. |
@ -20,8 +20,6 @@
//! [rustls]: https://crates.io/crates/rustls
//! [trust-dns]: https://crates.io/crates/trust-dns
#![deny(rust_2018_idioms, nonstandard_style)]
#![warn(future_incompatible)]
#![allow(
clippy::type_complexity,
clippy::too_many_arguments,
@ -62,13 +60,7 @@ pub mod ws;
#[allow(deprecated)]
pub use self::payload::PayloadStream;
#[cfg(any(
feature = "openssl",
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "rustls-0_23",
))]
#[cfg(feature = "__tls")]
pub use self::service::TlsAcceptorConfig;
pub use self::{
builder::HttpServiceBuilder,

View File

@ -66,7 +66,7 @@ impl<T: Head> ops::DerefMut for Message<T> {
impl<T: Head> Drop for Message<T> {
fn drop(&mut self) {
T::with_pool(|p| p.release(self.head.clone()))
T::with_pool(|p| p.release(Rc::clone(&self.head)))
}
}

View File

@ -351,12 +351,9 @@ mod tests {
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "text/plain");
let resp = Response::build(StatusCode::OK)
.content_type(mime::APPLICATION_JAVASCRIPT_UTF_8)
.content_type(mime::TEXT_JAVASCRIPT)
.body(Bytes::new());
assert_eq!(
resp.headers().get(CONTENT_TYPE).unwrap(),
"application/javascript; charset=utf-8"
);
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "text/javascript");
}
#[test]

View File

@ -241,25 +241,13 @@ where
}
/// Configuration options used when accepting TLS connection.
#[cfg(any(
feature = "openssl",
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "rustls-0_23",
))]
#[cfg(feature = "__tls")]
#[derive(Debug, Default)]
pub struct TlsAcceptorConfig {
pub(crate) handshake_timeout: Option<std::time::Duration>,
}
#[cfg(any(
feature = "openssl",
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "rustls-0_23",
))]
#[cfg(feature = "__tls")]
impl TlsAcceptorConfig {
/// Set TLS handshake timeout duration.
pub fn handshake_timeout(self, dur: std::time::Duration) -> Self {
@ -787,23 +775,23 @@ where
let cfg = self.cfg.clone();
Box::pin(async move {
let expect = expect
.await
.map_err(|e| error!("Init http expect service error: {:?}", e))?;
let expect = expect.await.map_err(|err| {
tracing::error!("Initialization of HTTP expect service error: {err:?}");
})?;
let upgrade = match upgrade {
Some(upgrade) => {
let upgrade = upgrade
.await
.map_err(|e| error!("Init http upgrade service error: {:?}", e))?;
let upgrade = upgrade.await.map_err(|err| {
tracing::error!("Initialization of HTTP upgrade service error: {err:?}");
})?;
Some(upgrade)
}
None => None,
};
let service = service
.await
.map_err(|e| error!("Init http service error: {:?}", e))?;
let service = service.await.map_err(|err| {
tracing::error!("Initialization of HTTP service error: {err:?}");
})?;
Ok(HttpServiceHandler::new(
cfg,
@ -922,7 +910,7 @@ where
handshake: Some((
crate::h2::handshake_with_timeout(io, &self.cfg),
self.cfg.clone(),
self.flow.clone(),
Rc::clone(&self.flow),
conn_data,
peer_addr,
)),
@ -938,7 +926,7 @@ where
state: State::H1 {
dispatcher: h1::Dispatcher::new(
io,
self.flow.clone(),
Rc::clone(&self.flow),
self.cfg.clone(),
peer_addr,
conn_data,

View File

@ -159,8 +159,8 @@ impl TestBuffer {
#[allow(dead_code)]
pub(crate) fn clone(&self) -> Self {
Self {
read_buf: self.read_buf.clone(),
write_buf: self.write_buf.clone(),
read_buf: Rc::clone(&self.read_buf),
write_buf: Rc::clone(&self.write_buf),
err: self.err.clone(),
}
}

View File

@ -114,14 +114,14 @@ mod inner {
{
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
DispatcherError::Service(ref e) => {
write!(fmt, "DispatcherError::Service({:?})", e)
DispatcherError::Service(ref err) => {
write!(fmt, "DispatcherError::Service({err:?})")
}
DispatcherError::Encoder(ref e) => {
write!(fmt, "DispatcherError::Encoder({:?})", e)
DispatcherError::Encoder(ref err) => {
write!(fmt, "DispatcherError::Encoder({err:?})")
}
DispatcherError::Decoder(ref e) => {
write!(fmt, "DispatcherError::Decoder({:?})", e)
DispatcherError::Decoder(ref err) => {
write!(fmt, "DispatcherError::Decoder({err:?})")
}
}
}
@ -136,9 +136,9 @@ mod inner {
{
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
DispatcherError::Service(ref e) => write!(fmt, "{}", e),
DispatcherError::Encoder(ref e) => write!(fmt, "{:?}", e),
DispatcherError::Decoder(ref e) => write!(fmt, "{:?}", e),
DispatcherError::Service(ref err) => write!(fmt, "{err}"),
DispatcherError::Encoder(ref err) => write!(fmt, "{err:?}"),
DispatcherError::Decoder(ref err) => write!(fmt, "{err:?}"),
}
}
}

View File

@ -178,17 +178,14 @@ impl Parser {
};
if payload_len < 126 {
dst.buffer_mut()
.reserve(p_len + 2 + if mask { 4 } else { 0 });
dst.buffer_mut().reserve(p_len + 2);
dst.buffer_mut().put_slice(&[one, two | payload_len as u8]);
} else if payload_len <= 65_535 {
dst.buffer_mut()
.reserve(p_len + 4 + if mask { 4 } else { 0 });
dst.buffer_mut().reserve(p_len + 4);
dst.buffer_mut().put_slice(&[one, two | 126]);
dst.buffer_mut().put_u16(payload_len as u16);
} else {
dst.buffer_mut()
.reserve(p_len + 10 + if mask { 4 } else { 0 });
dst.buffer_mut().reserve(p_len + 10);
dst.buffer_mut().put_slice(&[one, two | 127]);
dst.buffer_mut().put_u64(payload_len as u64);
};

View File

@ -5,7 +5,7 @@
use std::io;
use derive_more::{Display, Error, From};
use derive_more::derive::{Display, Error, From};
use http::{header, Method, StatusCode};
use crate::{body::BoxBody, header::HeaderValue, RequestHead, Response, ResponseBuilder};
@ -27,43 +27,43 @@ pub use self::{
#[derive(Debug, Display, Error, From)]
pub enum ProtocolError {
/// Received an unmasked frame from client.
#[display(fmt = "received an unmasked frame from client")]
#[display("received an unmasked frame from client")]
UnmaskedFrame,
/// Received a masked frame from server.
#[display(fmt = "received a masked frame from server")]
#[display("received a masked frame from server")]
MaskedFrame,
/// Encountered invalid opcode.
#[display(fmt = "invalid opcode ({})", _0)]
#[display("invalid opcode ({})", _0)]
InvalidOpcode(#[error(not(source))] u8),
/// Invalid control frame length
#[display(fmt = "invalid control frame length ({})", _0)]
#[display("invalid control frame length ({})", _0)]
InvalidLength(#[error(not(source))] usize),
/// Bad opcode.
#[display(fmt = "bad opcode")]
#[display("bad opcode")]
BadOpCode,
/// A payload reached size limit.
#[display(fmt = "payload reached size limit")]
#[display("payload reached size limit")]
Overflow,
/// Continuation has not started.
#[display(fmt = "continuation has not started")]
#[display("continuation has not started")]
ContinuationNotStarted,
/// Received new continuation but it is already started.
#[display(fmt = "received new continuation but it has already started")]
#[display("received new continuation but it has already started")]
ContinuationStarted,
/// Unknown continuation fragment.
#[display(fmt = "unknown continuation fragment: {}", _0)]
#[display("unknown continuation fragment: {}", _0)]
ContinuationFragment(#[error(not(source))] OpCode),
/// I/O error.
#[display(fmt = "I/O error: {}", _0)]
#[display("I/O error: {}", _0)]
Io(io::Error),
}
@ -71,27 +71,27 @@ pub enum ProtocolError {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Display, Error)]
pub enum HandshakeError {
/// Only get method is allowed.
#[display(fmt = "method not allowed")]
#[display("method not allowed")]
GetMethodRequired,
/// Upgrade header if not set to WebSocket.
#[display(fmt = "WebSocket upgrade is expected")]
#[display("WebSocket upgrade is expected")]
NoWebsocketUpgrade,
/// Connection header is not set to upgrade.
#[display(fmt = "connection upgrade is expected")]
#[display("connection upgrade is expected")]
NoConnectionUpgrade,
/// WebSocket version header is not set.
#[display(fmt = "WebSocket version header is required")]
#[display("WebSocket version header is required")]
NoVersionHeader,
/// Unsupported WebSocket version.
#[display(fmt = "unsupported WebSocket version")]
#[display("unsupported WebSocket version")]
UnsupportedVersion,
/// WebSocket key is not set or wrong.
#[display(fmt = "unknown WebSocket key")]
#[display("unknown WebSocket key")]
BadWebsocketKey,
}

View File

@ -5,7 +5,7 @@ use actix_http_test::test_server;
use actix_service::ServiceFactoryExt;
use actix_utils::future;
use bytes::Bytes;
use derive_more::{Display, Error};
use derive_more::derive::{Display, Error};
use futures_util::StreamExt as _;
const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
@ -94,7 +94,7 @@ async fn with_query_parameter() {
}
#[derive(Debug, Display, Error)]
#[display(fmt = "expect failed")]
#[display("expect failed")]
struct ExpectFailed;
impl From<ExpectFailed> for Response<BoxBody> {

View File

@ -14,7 +14,7 @@ use actix_http_test::test_server;
use actix_service::{fn_service, ServiceFactoryExt};
use actix_utils::future::{err, ok, ready};
use bytes::{Bytes, BytesMut};
use derive_more::{Display, Error};
use derive_more::derive::{Display, Error};
use futures_core::Stream;
use futures_util::{stream::once, StreamExt as _};
use openssl::{
@ -398,7 +398,7 @@ async fn h2_response_http_error_handling() {
}
#[derive(Debug, Display, Error)]
#[display(fmt = "error")]
#[display("error")]
struct BadRequest;
impl From<BadRequest> for Response<BoxBody> {

View File

@ -23,7 +23,7 @@ use actix_service::{fn_factory_with_config, fn_service};
use actix_tls::connect::rustls_0_23::webpki_roots_cert_store;
use actix_utils::future::{err, ok, poll_fn};
use bytes::{Bytes, BytesMut};
use derive_more::{Display, Error};
use derive_more::derive::{Display, Error};
use futures_core::{ready, Stream};
use futures_util::stream::once;
use rustls::{pki_types::ServerName, ServerConfig as RustlsServerConfig};
@ -480,7 +480,7 @@ async fn h2_response_http_error_handling() {
}
#[derive(Debug, Display, Error)]
#[display(fmt = "error")]
#[display("error")]
struct BadRequest;
impl From<BadRequest> for Response<BoxBody> {

View File

@ -14,7 +14,7 @@ use actix_rt::{net::TcpStream, time::sleep};
use actix_service::fn_service;
use actix_utils::future::{err, ok, ready};
use bytes::Bytes;
use derive_more::{Display, Error};
use derive_more::derive::{Display, Error};
use futures_util::{stream::once, FutureExt as _, StreamExt as _};
use regex::Regex;
@ -62,7 +62,7 @@ async fn h1_2() {
}
#[derive(Debug, Display, Error)]
#[display(fmt = "expect failed")]
#[display("expect failed")]
struct ExpectFailed;
impl From<ExpectFailed> for Response<BoxBody> {
@ -723,7 +723,7 @@ async fn h1_response_http_error_handling() {
}
#[derive(Debug, Display, Error)]
#[display(fmt = "error")]
#[display("error")]
struct BadRequest;
impl From<BadRequest> for Response<BoxBody> {

View File

@ -14,7 +14,7 @@ use actix_http::{
use actix_http_test::test_server;
use actix_service::{fn_factory, Service};
use bytes::Bytes;
use derive_more::{Display, Error, From};
use derive_more::derive::{Display, Error, From};
use futures_core::future::LocalBoxFuture;
use futures_util::{SinkExt as _, StreamExt as _};
@ -37,16 +37,16 @@ impl WsService {
#[derive(Debug, Display, Error, From)]
enum WsServiceError {
#[display(fmt = "HTTP error")]
#[display("HTTP error")]
Http(actix_http::Error),
#[display(fmt = "WS handshake error")]
#[display("WS handshake error")]
Ws(actix_http::ws::HandshakeError),
#[display(fmt = "I/O error")]
#[display("I/O error")]
Io(std::io::Error),
#[display(fmt = "dispatcher error")]
#[display("dispatcher error")]
Dispatcher,
}