mirror of
https://github.com/fafhrd91/actix-web
synced 2025-07-06 19:00:18 +02:00
Compare commits
18 Commits
from_fn
...
error-resp
Author | SHA1 | Date | |
---|---|---|---|
eb10b74751 | |||
a2b9823d9d | |||
da56de4556 | |||
758ae1dac1 | |||
37577dcb89 | |||
8b8eb4eae1 | |||
22593a1532 | |||
f7646bcc48 | |||
8018983a68 | |||
266834cf7c | |||
40e1034566 | |||
a5c78483f9 | |||
12a0521ef8 | |||
b4faf8820c | |||
d6f885127d | |||
ebc43dcf1b | |||
7c4c26d2df | |||
98c99f3bc2 |
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@ -89,5 +89,5 @@ jobs:
|
|||||||
- name: Generate API diff
|
- name: Generate API diff
|
||||||
run: |
|
run: |
|
||||||
for f in $(find -mindepth 2 -maxdepth 2 -name Cargo.toml); do
|
for f in $(find -mindepth 2 -maxdepth 2 -name Cargo.toml); do
|
||||||
cargo public-api --manifest-path "$f" diff ${{ github.event.pull_request.base.sha }}..${{ github.sha }}
|
cargo public-api --manifest-path "$f" --simplified diff ${{ github.event.pull_request.base.sha }}..${{ github.sha }}
|
||||||
done
|
done
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## 0.6.6
|
||||||
|
|
||||||
|
- Update `tokio-uring` dependency to `0.4`.
|
||||||
- Minimum supported Rust version (MSRV) is now 1.72.
|
- Minimum supported Rust version (MSRV) is now 1.72.
|
||||||
|
|
||||||
## 0.6.5
|
## 0.6.5
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-files"
|
name = "actix-files"
|
||||||
version = "0.6.5"
|
version = "0.6.6"
|
||||||
authors = [
|
authors = [
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
"Rob Ede <robjtede@icloud.com>",
|
||||||
@ -40,8 +40,8 @@ v_htmlescape = "0.15.5"
|
|||||||
|
|
||||||
# experimental-io-uring
|
# experimental-io-uring
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
tokio-uring = { version = "0.4", optional = true, features = ["bytes"] }
|
tokio-uring = { version = "0.5", optional = true, features = ["bytes"] }
|
||||||
actix-server = { version = "2.2", optional = true } # ensure matching tokio-uring versions
|
actix-server = { version = "2.4", optional = true } # ensure matching tokio-uring versions
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.7"
|
actix-rt = "2.7"
|
||||||
|
@ -3,11 +3,11 @@
|
|||||||
<!-- prettier-ignore-start -->
|
<!-- prettier-ignore-start -->
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-files)
|
[](https://crates.io/crates/actix-files)
|
||||||
[](https://docs.rs/actix-files/0.6.5)
|
[](https://docs.rs/actix-files/0.6.6)
|
||||||

|

|
||||||

|

|
||||||
<br />
|
<br />
|
||||||
[](https://deps.rs/crate/actix-files/0.6.5)
|
[](https://deps.rs/crate/actix-files/0.6.6)
|
||||||
[](https://crates.io/crates/actix-files)
|
[](https://crates.io/crates/actix-files)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add `error::InvalidStatusCode` re-export.
|
||||||
|
|
||||||
## 3.7.0
|
## 3.7.0
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -106,7 +106,7 @@ tokio-util = { version = "0.7", features = ["io", "codec"] }
|
|||||||
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
||||||
|
|
||||||
# http2
|
# http2
|
||||||
h2 = { version = "0.3.24", optional = true }
|
h2 = { version = "0.3.26", optional = true }
|
||||||
|
|
||||||
# websockets
|
# websockets
|
||||||
local-channel = { version = "0.1", optional = true }
|
local-channel = { version = "0.1", optional = true }
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use std::{error::Error as StdError, fmt, io, str::Utf8Error, string::FromUtf8Error};
|
use std::{error::Error as StdError, fmt, io, str::Utf8Error, string::FromUtf8Error};
|
||||||
|
|
||||||
use derive_more::{Display, Error, From};
|
use derive_more::{Display, Error, From};
|
||||||
pub use http::Error as HttpError;
|
pub use http::{status::InvalidStatusCode, Error as HttpError};
|
||||||
use http::{uri::InvalidUri, StatusCode};
|
use http::{uri::InvalidUri, StatusCode};
|
||||||
|
|
||||||
use crate::{body::BoxBody, Response};
|
use crate::{body::BoxBody, Response};
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## 0.6.2
|
||||||
|
|
||||||
- Add testing utilities under new module `test`.
|
- Add testing utilities under new module `test`.
|
||||||
- Minimum supported Rust version (MSRV) is now 1.72.
|
- Minimum supported Rust version (MSRV) is now 1.72.
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-multipart"
|
name = "actix-multipart"
|
||||||
version = "0.6.1"
|
version = "0.6.2"
|
||||||
authors = [
|
authors = [
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
"Jacob Halsey <jacob@jhalsey.com>",
|
"Jacob Halsey <jacob@jhalsey.com>",
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
<!-- prettier-ignore-start -->
|
<!-- prettier-ignore-start -->
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-multipart)
|
[](https://crates.io/crates/actix-multipart)
|
||||||
[](https://docs.rs/actix-multipart/0.6.1)
|
[](https://docs.rs/actix-multipart/0.6.2)
|
||||||

|

|
||||||

|

|
||||||
<br />
|
<br />
|
||||||
[](https://deps.rs/crate/actix-multipart/0.6.1)
|
[](https://deps.rs/crate/actix-multipart/0.6.2)
|
||||||
[](https://crates.io/crates/actix-multipart)
|
[](https://crates.io/crates/actix-multipart)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## 0.1.5
|
||||||
|
|
||||||
|
- Add `TestServerConfig::listen_address()` method.
|
||||||
|
|
||||||
|
## 0.1.4
|
||||||
|
|
||||||
- Add `TestServerConfig::rustls_0_23()` method for Rustls v0.23 support behind new `rustls-0_23` crate feature.
|
- Add `TestServerConfig::rustls_0_23()` method for Rustls v0.23 support behind new `rustls-0_23` crate feature.
|
||||||
- Add `TestServerConfig::disable_redirects()` method.
|
- Add `TestServerConfig::disable_redirects()` method.
|
||||||
- Various types from `awc`, such as `ClientRequest` and `ClientResponse`, are now re-exported.
|
- Various types from `awc`, such as `ClientRequest` and `ClientResponse`, are now re-exported.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-test"
|
name = "actix-test"
|
||||||
version = "0.1.3"
|
version = "0.1.5"
|
||||||
authors = [
|
authors = [
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
"Rob Ede <robjtede@icloud.com>",
|
||||||
|
@ -154,7 +154,7 @@ where
|
|||||||
// run server in separate orphaned thread
|
// run server in separate orphaned thread
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
rt::System::new().block_on(async move {
|
rt::System::new().block_on(async move {
|
||||||
let tcp = net::TcpListener::bind(("127.0.0.1", cfg.port)).unwrap();
|
let tcp = net::TcpListener::bind((cfg.listen_address.clone(), cfg.port)).unwrap();
|
||||||
let local_addr = tcp.local_addr().unwrap();
|
let local_addr = tcp.local_addr().unwrap();
|
||||||
let factory = factory.clone();
|
let factory = factory.clone();
|
||||||
let srv_cfg = cfg.clone();
|
let srv_cfg = cfg.clone();
|
||||||
@ -514,6 +514,7 @@ pub struct TestServerConfig {
|
|||||||
tp: HttpVer,
|
tp: HttpVer,
|
||||||
stream: StreamType,
|
stream: StreamType,
|
||||||
client_request_timeout: Duration,
|
client_request_timeout: Duration,
|
||||||
|
listen_address: String,
|
||||||
port: u16,
|
port: u16,
|
||||||
workers: usize,
|
workers: usize,
|
||||||
disable_redirects: bool,
|
disable_redirects: bool,
|
||||||
@ -532,6 +533,7 @@ impl TestServerConfig {
|
|||||||
tp: HttpVer::Both,
|
tp: HttpVer::Both,
|
||||||
stream: StreamType::Tcp,
|
stream: StreamType::Tcp,
|
||||||
client_request_timeout: Duration::from_secs(5),
|
client_request_timeout: Duration::from_secs(5),
|
||||||
|
listen_address: "127.0.0.1".to_string(),
|
||||||
port: 0,
|
port: 0,
|
||||||
workers: 1,
|
workers: 1,
|
||||||
disable_redirects: false,
|
disable_redirects: false,
|
||||||
@ -607,6 +609,14 @@ impl TestServerConfig {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the address the server will listen on.
|
||||||
|
///
|
||||||
|
/// By default, only listens on `127.0.0.1`.
|
||||||
|
pub fn listen_address(mut self, addr: impl Into<String>) -> Self {
|
||||||
|
self.listen_address = addr.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets test server port.
|
/// Sets test server port.
|
||||||
///
|
///
|
||||||
/// By default, a random free port is determined by the OS.
|
/// By default, a random free port is determined by the OS.
|
||||||
@ -657,9 +667,9 @@ impl TestServer {
|
|||||||
let scheme = if self.tls { "https" } else { "http" };
|
let scheme = if self.tls { "https" } else { "http" };
|
||||||
|
|
||||||
if uri.starts_with('/') {
|
if uri.starts_with('/') {
|
||||||
format!("{}://localhost:{}{}", scheme, self.addr.port(), uri)
|
format!("{}://{}{}", scheme, self.addr, uri)
|
||||||
} else {
|
} else {
|
||||||
format!("{}://localhost:{}/{}", scheme, self.addr.port(), uri)
|
format!("{}://{}/{}", scheme, self.addr, uri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
- Take the encoded buffer when yielding bytes in the response stream rather than splitting the buffer, reducing memory use
|
||||||
- Minimum supported Rust version (MSRV) is now 1.72.
|
- Minimum supported Rust version (MSRV) is now 1.72.
|
||||||
|
|
||||||
## 4.3.0
|
## 4.3.0
|
||||||
|
@ -710,7 +710,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !this.buf.is_empty() {
|
if !this.buf.is_empty() {
|
||||||
Poll::Ready(Some(Ok(this.buf.split().freeze())))
|
Poll::Ready(Some(Ok(std::mem::take(&mut this.buf).freeze())))
|
||||||
} else if this.fut.alive() && !this.closed {
|
} else if this.fut.alive() && !this.closed {
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
} else {
|
} else {
|
||||||
|
@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## 4.3.0
|
||||||
|
|
||||||
- Add `#[scope]` macro.
|
- Add `#[scope]` macro.
|
||||||
|
- Add `compat-routing-macros-force-pub` crate feature which, on-by-default, which when disabled causes handlers to inherit their attached function's visibility.
|
||||||
- Prevent inclusion of default `actix-router` features.
|
- Prevent inclusion of default `actix-router` features.
|
||||||
- Minimum supported Rust version (MSRV) is now 1.72.
|
- Minimum supported Rust version (MSRV) is now 1.72.
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-web-codegen"
|
name = "actix-web-codegen"
|
||||||
version = "4.2.2"
|
version = "4.3.0"
|
||||||
description = "Routing and runtime macros for Actix Web"
|
description = "Routing and runtime macros for Actix Web"
|
||||||
authors = [
|
authors = [
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
@ -15,6 +15,10 @@ rust-version.workspace = true
|
|||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["compat-routing-macros-force-pub"]
|
||||||
|
compat-routing-macros-force-pub = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-router = { version = "0.5", default-features = false }
|
actix-router = { version = "0.5", default-features = false }
|
||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
<!-- prettier-ignore-start -->
|
<!-- prettier-ignore-start -->
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-web-codegen)
|
[](https://crates.io/crates/actix-web-codegen)
|
||||||
[](https://docs.rs/actix-web-codegen/4.2.2)
|
[](https://docs.rs/actix-web-codegen/4.3.0)
|
||||||

|

|
||||||

|

|
||||||
<br />
|
<br />
|
||||||
[](https://deps.rs/crate/actix-web-codegen/4.2.2)
|
[](https://deps.rs/crate/actix-web-codegen/4.3.0)
|
||||||
[](https://crates.io/crates/actix-web-codegen)
|
[](https://crates.io/crates/actix-web-codegen)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
@ -413,6 +413,13 @@ impl ToTokens for Route {
|
|||||||
doc_attributes,
|
doc_attributes,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
|
#[allow(unused_variables)] // used when force-pub feature is disabled
|
||||||
|
let vis = &ast.vis;
|
||||||
|
|
||||||
|
// TODO(breaking): remove this force-pub forwards-compatibility feature
|
||||||
|
#[cfg(feature = "compat-routing-macros-force-pub")]
|
||||||
|
let vis = syn::Visibility::Public(<Token![pub]>::default());
|
||||||
|
|
||||||
let registrations: TokenStream2 = args
|
let registrations: TokenStream2 = args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|args| {
|
.map(|args| {
|
||||||
@ -460,7 +467,7 @@ impl ToTokens for Route {
|
|||||||
let stream = quote! {
|
let stream = quote! {
|
||||||
#(#doc_attributes)*
|
#(#doc_attributes)*
|
||||||
#[allow(non_camel_case_types, missing_docs)]
|
#[allow(non_camel_case_types, missing_docs)]
|
||||||
pub struct #name;
|
#vis struct #name;
|
||||||
|
|
||||||
impl ::actix_web::dev::HttpServiceFactory for #name {
|
impl ::actix_web::dev::HttpServiceFactory for #name {
|
||||||
fn register(self, __config: &mut actix_web::dev::AppService) {
|
fn register(self, __config: &mut actix_web::dev::AppService) {
|
||||||
|
@ -49,7 +49,7 @@ mod scope_module {
|
|||||||
#[connect("/test2")]
|
#[connect("/test2")]
|
||||||
#[options("/test3")]
|
#[options("/test3")]
|
||||||
#[trace("/test4")]
|
#[trace("/test4")]
|
||||||
async fn multiple_separate_paths() -> impl Responder {
|
pub async fn multiple_separate_paths() -> impl Responder {
|
||||||
HttpResponse::Ok().finish()
|
HttpResponse::Ok().finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,10 +2,20 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- `ConnectionInfo::realip_remote_addr()` now handles IPv6 addresses from `Forwarded` header correctly. Previously, it sometimes returned the forwarded port as well.
|
||||||
|
|
||||||
|
## 4.7.0
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Add `#[scope]` macro.
|
||||||
|
- Add `middleware::Identity` type.
|
||||||
- Add `CustomizeResponder::add_cookie()` method.
|
- Add `CustomizeResponder::add_cookie()` method.
|
||||||
- Add `guard::GuardContext::app_data()` method.
|
- Add `guard::GuardContext::app_data()` method.
|
||||||
|
- Add `compat-routing-macros-force-pub` crate feature which (on-by-default) which, when disabled, causes handlers to inherit their attached function's visibility.
|
||||||
|
- Add `compat` crate feature group (on-by-default) which, when disabled, helps with transitioning to some planned v5.0 breaking changes, starting only with `compat-routing-macros-force-pub`.
|
||||||
- Implement `From<Box<dyn ResponseError>>` for `Error`.
|
- Implement `From<Box<dyn ResponseError>>` for `Error`.
|
||||||
|
|
||||||
## 4.6.0
|
## 4.6.0
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-web"
|
name = "actix-web"
|
||||||
version = "4.6.0"
|
version = "4.7.0"
|
||||||
description = "Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust"
|
description = "Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust"
|
||||||
authors = [
|
authors = [
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
@ -40,7 +40,16 @@ name = "actix_web"
|
|||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["macros", "compress-brotli", "compress-gzip", "compress-zstd", "cookies", "http2", "unicode"]
|
default = [
|
||||||
|
"macros",
|
||||||
|
"compress-brotli",
|
||||||
|
"compress-gzip",
|
||||||
|
"compress-zstd",
|
||||||
|
"cookies",
|
||||||
|
"http2",
|
||||||
|
"unicode",
|
||||||
|
"compat",
|
||||||
|
]
|
||||||
|
|
||||||
# Brotli algorithm content-encoding support
|
# Brotli algorithm content-encoding support
|
||||||
compress-brotli = ["actix-http/compress-brotli", "__compress"]
|
compress-brotli = ["actix-http/compress-brotli", "__compress"]
|
||||||
@ -50,14 +59,15 @@ compress-gzip = ["actix-http/compress-gzip", "__compress"]
|
|||||||
compress-zstd = ["actix-http/compress-zstd", "__compress"]
|
compress-zstd = ["actix-http/compress-zstd", "__compress"]
|
||||||
|
|
||||||
# Routing and runtime proc macros
|
# Routing and runtime proc macros
|
||||||
macros = ["actix-macros", "actix-web-codegen"]
|
macros = ["dep:actix-macros", "dep:actix-web-codegen"]
|
||||||
|
|
||||||
# Cookies support
|
# Cookies support
|
||||||
cookies = ["cookie"]
|
cookies = ["dep:cookie"]
|
||||||
|
|
||||||
# Secure & signed cookies
|
# Secure & signed cookies
|
||||||
secure-cookies = ["cookies", "cookie/secure"]
|
secure-cookies = ["cookies", "cookie/secure"]
|
||||||
|
|
||||||
|
# HTTP/2 support (including h2c).
|
||||||
http2 = ["actix-http/http2"]
|
http2 = ["actix-http/http2"]
|
||||||
|
|
||||||
# TLS via OpenSSL
|
# TLS via OpenSSL
|
||||||
@ -84,6 +94,14 @@ __compress = []
|
|||||||
# io-uring feature only available for Linux OSes.
|
# io-uring feature only available for Linux OSes.
|
||||||
experimental-io-uring = ["actix-server/io-uring"]
|
experimental-io-uring = ["actix-server/io-uring"]
|
||||||
|
|
||||||
|
# Feature group which, when disabled, helps migrate code to v5.0.
|
||||||
|
compat = [
|
||||||
|
"compat-routing-macros-force-pub",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Opt-out forwards-compatibility for handler visibility inheritance fix.
|
||||||
|
compat-routing-macros-force-pub = ["actix-web-codegen?/compat-routing-macros-force-pub"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-codec = "0.5"
|
actix-codec = "0.5"
|
||||||
actix-macros = { version = "0.2.3", optional = true }
|
actix-macros = { version = "0.2.3", optional = true }
|
||||||
@ -95,7 +113,7 @@ actix-tls = { version = "3.4", default-features = false, optional = true }
|
|||||||
|
|
||||||
actix-http = { version = "3.7", features = ["ws"] }
|
actix-http = { version = "3.7", features = ["ws"] }
|
||||||
actix-router = { version = "0.5.3", default-features = false, features = ["http"] }
|
actix-router = { version = "0.5.3", default-features = false, features = ["http"] }
|
||||||
actix-web-codegen = { version = "4.2", optional = true }
|
actix-web-codegen = { version = "4.3", optional = true, default-features = false }
|
||||||
|
|
||||||
ahash = "0.8"
|
ahash = "0.8"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
|
@ -8,10 +8,10 @@
|
|||||||
<!-- prettier-ignore-start -->
|
<!-- prettier-ignore-start -->
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-web)
|
[](https://crates.io/crates/actix-web)
|
||||||
[](https://docs.rs/actix-web/4.6.0)
|
[](https://docs.rs/actix-web/4.7.0)
|
||||||

|

|
||||||

|

|
||||||
[](https://deps.rs/crate/actix-web/4.6.0)
|
[](https://deps.rs/crate/actix-web/4.7.0)
|
||||||
<br />
|
<br />
|
||||||
[](https://github.com/actix/actix-web/actions/workflows/ci.yml)
|
[](https://github.com/actix/actix-web/actions/workflows/ci.yml)
|
||||||
[](https://codecov.io/gh/actix/actix-web)
|
[](https://codecov.io/gh/actix/actix-web)
|
||||||
|
@ -6,7 +6,7 @@ use crate::{HttpResponse, ResponseError};
|
|||||||
|
|
||||||
/// General purpose Actix Web error.
|
/// General purpose Actix Web error.
|
||||||
///
|
///
|
||||||
/// An Actix Web error is used to carry errors from `std::error` through actix in a convenient way.
|
/// An Actix Web error is used to carry errors from `std::error` through Actix in a convenient way.
|
||||||
/// It can be created through converting errors with `into()`.
|
/// It can be created through converting errors with `into()`.
|
||||||
///
|
///
|
||||||
/// Whenever it is created from an external object a response error is created for it that can be
|
/// Whenever it is created from an external object a response error is created for it that can be
|
||||||
@ -14,6 +14,7 @@ use crate::{HttpResponse, ResponseError};
|
|||||||
/// you can always get a `ResponseError` reference from it.
|
/// you can always get a `ResponseError` reference from it.
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
cause: Box<dyn ResponseError>,
|
cause: Box<dyn ResponseError>,
|
||||||
|
response_mappers: Vec<Box<dyn Fn(HttpResponse) -> HttpResponse>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
@ -29,7 +30,20 @@ impl Error {
|
|||||||
|
|
||||||
/// Shortcut for creating an `HttpResponse`.
|
/// Shortcut for creating an `HttpResponse`.
|
||||||
pub fn error_response(&self) -> HttpResponse {
|
pub fn error_response(&self) -> HttpResponse {
|
||||||
self.cause.error_response()
|
let mut res = self.cause.error_response();
|
||||||
|
|
||||||
|
for mapper in &self.response_mappers {
|
||||||
|
res = (mapper)(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_mapper<F, B>(&mut self, mapper: F)
|
||||||
|
where
|
||||||
|
F: Fn(HttpResponse) -> HttpResponse + 'static,
|
||||||
|
{
|
||||||
|
self.response_mappers.push(Box::new(mapper))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,6 +70,7 @@ impl<T: ResponseError + 'static> From<T> for Error {
|
|||||||
fn from(err: T) -> Error {
|
fn from(err: T) -> Error {
|
||||||
Error {
|
Error {
|
||||||
cause: Box::new(err),
|
cause: Box::new(err),
|
||||||
|
response_mappers: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,19 @@ fn unquote(val: &str) -> &str {
|
|||||||
val.trim().trim_start_matches('"').trim_end_matches('"')
|
val.trim().trim_start_matches('"').trim_end_matches('"')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove port and IPv6 square brackets from a peer specification.
|
||||||
|
fn bare_address(val: &str) -> &str {
|
||||||
|
if val.starts_with('[') {
|
||||||
|
val.split("]:")
|
||||||
|
.next()
|
||||||
|
.map(|s| s.trim_start_matches('[').trim_end_matches(']'))
|
||||||
|
// This shouldn't *actually* ever happen
|
||||||
|
.unwrap_or(val)
|
||||||
|
} else {
|
||||||
|
val.split(':').next().unwrap_or(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Extracts and trims first value for given header name.
|
/// Extracts and trims first value for given header name.
|
||||||
fn first_header_value<'a>(req: &'a RequestHead, name: &'_ HeaderName) -> Option<&'a str> {
|
fn first_header_value<'a>(req: &'a RequestHead, name: &'_ HeaderName) -> Option<&'a str> {
|
||||||
let hdr = req.headers.get(name)?.to_str().ok()?;
|
let hdr = req.headers.get(name)?.to_str().ok()?;
|
||||||
@ -100,7 +113,7 @@ impl ConnectionInfo {
|
|||||||
// --- https://datatracker.ietf.org/doc/html/rfc7239#section-5.2
|
// --- https://datatracker.ietf.org/doc/html/rfc7239#section-5.2
|
||||||
|
|
||||||
match name.trim().to_lowercase().as_str() {
|
match name.trim().to_lowercase().as_str() {
|
||||||
"for" => realip_remote_addr.get_or_insert_with(|| unquote(val)),
|
"for" => realip_remote_addr.get_or_insert_with(|| bare_address(unquote(val))),
|
||||||
"proto" => scheme.get_or_insert_with(|| unquote(val)),
|
"proto" => scheme.get_or_insert_with(|| unquote(val)),
|
||||||
"host" => host.get_or_insert_with(|| unquote(val)),
|
"host" => host.get_or_insert_with(|| unquote(val)),
|
||||||
"by" => {
|
"by" => {
|
||||||
@ -368,16 +381,25 @@ mod tests {
|
|||||||
.insert_header((header::FORWARDED, r#"for="192.0.2.60:8080""#))
|
.insert_header((header::FORWARDED, r#"for="192.0.2.60:8080""#))
|
||||||
.to_http_request();
|
.to_http_request();
|
||||||
let info = req.connection_info();
|
let info = req.connection_info();
|
||||||
assert_eq!(info.realip_remote_addr(), Some("192.0.2.60:8080"));
|
assert_eq!(info.realip_remote_addr(), Some("192.0.2.60"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn forwarded_for_ipv6() {
|
fn forwarded_for_ipv6() {
|
||||||
|
let req = TestRequest::default()
|
||||||
|
.insert_header((header::FORWARDED, r#"for="[2001:db8:cafe::17]""#))
|
||||||
|
.to_http_request();
|
||||||
|
let info = req.connection_info();
|
||||||
|
assert_eq!(info.realip_remote_addr(), Some("2001:db8:cafe::17"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn forwarded_for_ipv6_with_port() {
|
||||||
let req = TestRequest::default()
|
let req = TestRequest::default()
|
||||||
.insert_header((header::FORWARDED, r#"for="[2001:db8:cafe::17]:4711""#))
|
.insert_header((header::FORWARDED, r#"for="[2001:db8:cafe::17]:4711""#))
|
||||||
.to_http_request();
|
.to_http_request();
|
||||||
let info = req.connection_info();
|
let info = req.connection_info();
|
||||||
assert_eq!(info.realip_remote_addr(), Some("[2001:db8:cafe::17]:4711"));
|
assert_eq!(info.realip_remote_addr(), Some("2001:db8:cafe::17"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -38,15 +38,6 @@ pub struct Compat<T> {
|
|||||||
transform: T,
|
transform: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl Compat<super::Noop> {
|
|
||||||
pub(crate) fn noop() -> Self {
|
|
||||||
Self {
|
|
||||||
transform: super::Noop,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Compat<T> {
|
impl<T> Compat<T> {
|
||||||
/// Wrap a middleware to give it broader compatibility.
|
/// Wrap a middleware to give it broader compatibility.
|
||||||
pub fn new(middleware: T) -> Self {
|
pub fn new(middleware: T) -> Self {
|
||||||
@ -152,7 +143,7 @@ mod tests {
|
|||||||
use crate::{
|
use crate::{
|
||||||
dev::ServiceRequest,
|
dev::ServiceRequest,
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
middleware::{self, Condition, Logger},
|
middleware::{self, Condition, Identity, Logger},
|
||||||
test::{self, call_service, init_service, TestRequest},
|
test::{self, call_service, init_service, TestRequest},
|
||||||
web, App, HttpResponse,
|
web, App, HttpResponse,
|
||||||
};
|
};
|
||||||
@ -225,7 +216,7 @@ mod tests {
|
|||||||
async fn compat_noop_is_noop() {
|
async fn compat_noop_is_noop() {
|
||||||
let srv = test::ok_service();
|
let srv = test::ok_service();
|
||||||
|
|
||||||
let mw = Compat::noop()
|
let mw = Compat::new(Identity)
|
||||||
.new_transform(srv.into_service())
|
.new_transform(srv.into_service())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -141,7 +141,7 @@ mod tests {
|
|||||||
header::{HeaderValue, CONTENT_TYPE},
|
header::{HeaderValue, CONTENT_TYPE},
|
||||||
StatusCode,
|
StatusCode,
|
||||||
},
|
},
|
||||||
middleware::{self, ErrorHandlerResponse, ErrorHandlers},
|
middleware::{self, ErrorHandlerResponse, ErrorHandlers, Identity},
|
||||||
test::{self, TestRequest},
|
test::{self, TestRequest},
|
||||||
web::Bytes,
|
web::Bytes,
|
||||||
HttpResponse,
|
HttpResponse,
|
||||||
@ -158,7 +158,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compat_with_builtin_middleware() {
|
fn compat_with_builtin_middleware() {
|
||||||
let _ = Condition::new(true, middleware::Compat::noop());
|
let _ = Condition::new(true, middleware::Compat::new(Identity));
|
||||||
let _ = Condition::new(true, middleware::Logger::default());
|
let _ = Condition::new(true, middleware::Logger::default());
|
||||||
let _ = Condition::new(true, middleware::Compress::default());
|
let _ = Condition::new(true, middleware::Compress::default());
|
||||||
let _ = Condition::new(true, middleware::NormalizePath::trim());
|
let _ = Condition::new(true, middleware::NormalizePath::trim());
|
||||||
|
@ -2,35 +2,39 @@
|
|||||||
|
|
||||||
use actix_utils::future::{ready, Ready};
|
use actix_utils::future::{ready, Ready};
|
||||||
|
|
||||||
use crate::dev::{Service, Transform};
|
use crate::dev::{forward_ready, Service, Transform};
|
||||||
|
|
||||||
/// A no-op middleware that passes through request and response untouched.
|
/// A no-op middleware that passes through request and response untouched.
|
||||||
pub(crate) struct Noop;
|
#[derive(Debug, Clone, Default)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct Identity;
|
||||||
|
|
||||||
impl<S: Service<Req>, Req> Transform<S, Req> for Noop {
|
impl<S: Service<Req>, Req> Transform<S, Req> for Identity {
|
||||||
type Response = S::Response;
|
type Response = S::Response;
|
||||||
type Error = S::Error;
|
type Error = S::Error;
|
||||||
type Transform = NoopService<S>;
|
type Transform = IdentityMiddleware<S>;
|
||||||
type InitError = ();
|
type InitError = ();
|
||||||
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn new_transform(&self, service: S) -> Self::Future {
|
fn new_transform(&self, service: S) -> Self::Future {
|
||||||
ready(Ok(NoopService { service }))
|
ready(Ok(IdentityMiddleware { service }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub(crate) struct NoopService<S> {
|
pub struct IdentityMiddleware<S> {
|
||||||
service: S,
|
service: S,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: Service<Req>, Req> Service<Req> for NoopService<S> {
|
impl<S: Service<Req>, Req> Service<Req> for IdentityMiddleware<S> {
|
||||||
type Response = S::Response;
|
type Response = S::Response;
|
||||||
type Error = S::Error;
|
type Error = S::Error;
|
||||||
type Future = S::Future;
|
type Future = S::Future;
|
||||||
|
|
||||||
crate::dev::forward_ready!(service);
|
forward_ready!(service);
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn call(&self, req: Req) -> Self::Future {
|
fn call(&self, req: Req) -> Self::Future {
|
||||||
self.service.call(req)
|
self.service.call(req)
|
||||||
}
|
}
|
@ -218,31 +218,27 @@
|
|||||||
//! [lab_from_fn]: https://docs.rs/actix-web-lab/latest/actix_web_lab/middleware/fn.from_fn.html
|
//! [lab_from_fn]: https://docs.rs/actix-web-lab/latest/actix_web_lab/middleware/fn.from_fn.html
|
||||||
|
|
||||||
mod compat;
|
mod compat;
|
||||||
|
#[cfg(feature = "__compress")]
|
||||||
|
mod compress;
|
||||||
mod condition;
|
mod condition;
|
||||||
mod default_headers;
|
mod default_headers;
|
||||||
mod err_handlers;
|
mod err_handlers;
|
||||||
|
mod identity;
|
||||||
mod logger;
|
mod logger;
|
||||||
#[cfg(test)]
|
|
||||||
mod noop;
|
|
||||||
mod normalize;
|
mod normalize;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(feature = "__compress")]
|
||||||
pub(crate) use self::noop::Noop;
|
pub use self::compress::Compress;
|
||||||
pub use self::{
|
pub use self::{
|
||||||
compat::Compat,
|
compat::Compat,
|
||||||
condition::Condition,
|
condition::Condition,
|
||||||
default_headers::DefaultHeaders,
|
default_headers::DefaultHeaders,
|
||||||
err_handlers::{ErrorHandlerResponse, ErrorHandlers},
|
err_handlers::{ErrorHandlerResponse, ErrorHandlers},
|
||||||
|
identity::Identity,
|
||||||
logger::Logger,
|
logger::Logger,
|
||||||
normalize::{NormalizePath, TrailingSlash},
|
normalize::{NormalizePath, TrailingSlash},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "__compress")]
|
|
||||||
mod compress;
|
|
||||||
|
|
||||||
#[cfg(feature = "__compress")]
|
|
||||||
pub use self::compress::Compress;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -64,7 +64,7 @@ compress-gzip = ["actix-http/compress-gzip", "__compress"]
|
|||||||
compress-zstd = ["actix-http/compress-zstd", "__compress"]
|
compress-zstd = ["actix-http/compress-zstd", "__compress"]
|
||||||
|
|
||||||
# Cookie parsing and cookie jar
|
# Cookie parsing and cookie jar
|
||||||
cookies = ["cookie"]
|
cookies = ["dep:cookie"]
|
||||||
|
|
||||||
# Use `trust-dns-resolver` crate as DNS resolver
|
# Use `trust-dns-resolver` crate as DNS resolver
|
||||||
trust-dns = ["trust-dns-resolver"]
|
trust-dns = ["trust-dns-resolver"]
|
||||||
@ -92,7 +92,7 @@ cfg-if = "1"
|
|||||||
derive_more = "0.99.5"
|
derive_more = "0.99.5"
|
||||||
futures-core = { version = "0.3.17", default-features = false, features = ["alloc"] }
|
futures-core = { version = "0.3.17", default-features = false, features = ["alloc"] }
|
||||||
futures-util = { version = "0.3.17", default-features = false, features = ["alloc", "sink"] }
|
futures-util = { version = "0.3.17", default-features = false, features = ["alloc", "sink"] }
|
||||||
h2 = "0.3.24"
|
h2 = "0.3.26"
|
||||||
http = "0.2.7"
|
http = "0.2.7"
|
||||||
itoa = "1"
|
itoa = "1"
|
||||||
log =" 0.4"
|
log =" 0.4"
|
||||||
|
Reference in New Issue
Block a user