1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-07-21 16:46:15 +02:00

Compare commits

...

7 Commits

Author SHA1 Message Date
Rob Ede
129b78f9c7 prepare actix-test release 0.1.1 2023-02-26 14:20:48 +00:00
Rob Ede
ad27150c5f fix doc tests 2023-02-26 14:14:04 +00:00
Rob Ede
8d5d6a2598 tweak err handlers docs 2023-02-26 13:28:19 +00:00
Rob Ede
e97329eb2a bump socket2 dep to 0.5 2023-02-26 13:28:19 +00:00
Kristian Gaylord
fbfff3e751 actix-test: allow dynamic port setting (#2960)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2023-02-26 05:25:36 +00:00
Rob Ede
fdfb3d45db remove direct dep on ahash for client pool 2023-02-26 03:50:36 +00:00
Rob Ede
4e05629368 specify safe tokio version range 2023-02-26 03:47:25 +00:00
11 changed files with 93 additions and 55 deletions

View File

@@ -47,7 +47,7 @@ serde_json = "1.0"
slab = "0.4" slab = "0.4"
serde_urlencoded = "0.7" serde_urlencoded = "0.7"
tls-openssl = { version = "0.10.9", package = "openssl", optional = true } tls-openssl = { version = "0.10.9", package = "openssl", optional = true }
tokio = { version = "1.18.5", features = ["sync"] } tokio = { version = "1.24.2", features = ["sync"] }
[dev-dependencies] [dev-dependencies]
actix-web = { version = "4", default-features = false, features = ["cookies"] } actix-web = { version = "4", default-features = false, features = ["cookies"] }

View File

@@ -61,7 +61,7 @@ actix-codec = "0.5"
actix-utils = "3" actix-utils = "3"
actix-rt = { version = "2.2", default-features = false } actix-rt = { version = "2.2", default-features = false }
ahash = "0.7" ahash = "0.8"
bitflags = "1.2" bitflags = "1.2"
bytes = "1" bytes = "1"
bytestring = "1" bytestring = "1"
@@ -77,7 +77,7 @@ mime = "0.3"
percent-encoding = "2.1" percent-encoding = "2.1"
pin-project-lite = "0.2" pin-project-lite = "0.2"
smallvec = "1.6.1" smallvec = "1.6.1"
tokio = { version = "1.18.5", features = [] } tokio = { version = "1.24.2", features = [] }
tokio-util = { version = "0.7", features = ["io", "codec"] } 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"] }
@@ -119,7 +119,7 @@ serde_json = "1.0"
static_assertions = "1" static_assertions = "1"
tls-openssl = { package = "openssl", version = "0.10.9" } tls-openssl = { package = "openssl", version = "0.10.9" }
tls-rustls = { package = "rustls", version = "0.20.0" } tls-rustls = { package = "rustls", version = "0.20.0" }
tokio = { version = "1.18.5", features = ["net", "rt", "macros"] } tokio = { version = "1.24.2", features = ["net", "rt", "macros"] }
[[example]] [[example]]
name = "ws" name = "ws"

View File

@@ -44,7 +44,7 @@ serde_json = "1"
serde_plain = "1" serde_plain = "1"
# TODO(MSRV 1.60): replace with dep: prefix # TODO(MSRV 1.60): replace with dep: prefix
tempfile-dep = { package = "tempfile", version = "3.4", optional = true } tempfile-dep = { package = "tempfile", version = "3.4", optional = true }
tokio = { version = "1.18.5", features = ["sync"] } tokio = { version = "1.24.2", features = ["sync"] }
[dev-dependencies] [dev-dependencies]
actix-http = "3" actix-http = "3"
@@ -53,5 +53,5 @@ actix-rt = "2.2"
actix-test = "0.1" actix-test = "0.1"
awc = "3" awc = "3"
futures-util = { version = "0.3.17", default-features = false, features = ["alloc"] } futures-util = { version = "0.3.17", default-features = false, features = ["alloc"] }
tokio = { version = "1.18.5", features = ["sync"] } tokio = { version = "1.24.2", features = ["sync"] }
tokio-stream = "0.1" tokio-stream = "0.1"

View File

@@ -2,6 +2,9 @@
## Unreleased - 2022-xx-xx ## Unreleased - 2022-xx-xx
## 0.1.1 - 2023-02-26
- Add `TestServerConfig::port()` setter method.
- Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency. - Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency.
## 0.1.0 - 2022-07-24 ## 0.1.0 - 2022-07-24

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "actix-test" name = "actix-test"
version = "0.1.0" version = "0.1.1"
authors = [ authors = [
"Nikolay Kim <fafhrd91@gmail.com>", "Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>", "Rob Ede <robjtede@icloud.com>",
@@ -45,4 +45,4 @@ serde_json = "1"
serde_urlencoded = "0.7" serde_urlencoded = "0.7"
tls-openssl = { package = "openssl", version = "0.10.9", optional = true } tls-openssl = { package = "openssl", version = "0.10.9", optional = true }
tls-rustls = { package = "rustls", version = "0.20.0", optional = true } tls-rustls = { package = "rustls", version = "0.20.0", optional = true }
tokio = { version = "1.18.5", features = ["sync"] } tokio = { version = "1.24.2", features = ["sync"] }

View File

@@ -145,7 +145,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:0").unwrap(); let tcp = net::TcpListener::bind(("127.0.0.1", 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();
@@ -390,6 +390,7 @@ pub struct TestServerConfig {
tp: HttpVer, tp: HttpVer,
stream: StreamType, stream: StreamType,
client_request_timeout: Duration, client_request_timeout: Duration,
port: u16,
} }
impl Default for TestServerConfig { impl Default for TestServerConfig {
@@ -405,6 +406,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),
port: 0,
} }
} }
@@ -439,6 +441,14 @@ impl TestServerConfig {
self.client_request_timeout = dur; self.client_request_timeout = dur;
self self
} }
/// Sets test server port.
///
/// By default, a random free port is determined by the OS.
pub fn port(mut self, port: u16) -> Self {
self.port = port;
self
}
} }
/// A basic HTTP server controller that simplifies the process of writing integration tests for /// A basic HTTP server controller that simplifies the process of writing integration tests for

View File

@@ -23,7 +23,7 @@ bytes = "1"
bytestring = "1" bytestring = "1"
futures-core = { version = "0.3.17", default-features = false } futures-core = { version = "0.3.17", default-features = false }
pin-project-lite = "0.2" pin-project-lite = "0.2"
tokio = { version = "1.18.5", features = ["sync"] } tokio = { version = "1.24.2", features = ["sync"] }
tokio-util = { version = "0.7", features = ["codec"] } tokio-util = { version = "0.7", features = ["codec"] }
[dev-dependencies] [dev-dependencies]

View File

@@ -72,7 +72,7 @@ actix-http = { version = "3.3", features = ["http2", "ws"] }
actix-router = "0.5" actix-router = "0.5"
actix-web-codegen = { version = "4.2", optional = true } actix-web-codegen = { version = "4.2", optional = true }
ahash = "0.7" ahash = "0.8"
bytes = "1" bytes = "1"
bytestring = "1" bytestring = "1"
cfg-if = "1" cfg-if = "1"
@@ -93,7 +93,7 @@ serde = "1.0"
serde_json = "1.0" serde_json = "1.0"
serde_urlencoded = "0.7" serde_urlencoded = "0.7"
smallvec = "1.6.1" smallvec = "1.6.1"
socket2 = "0.4.0" socket2 = "0.4"
time = { version = "0.3", default-features = false, features = ["formatting"] } time = { version = "0.3", default-features = false, features = ["formatting"] }
url = "2.1" url = "2.1"
@@ -115,7 +115,7 @@ serde = { version = "1.0", features = ["derive"] }
static_assertions = "1" static_assertions = "1"
tls-openssl = { package = "openssl", version = "0.10.9" } tls-openssl = { package = "openssl", version = "0.10.9" }
tls-rustls = { package = "rustls", version = "0.20.0" } tls-rustls = { package = "rustls", version = "0.20.0" }
tokio = { version = "1.18.5", features = ["rt-multi-thread", "macros"] } tokio = { version = "1.24.2", features = ["rt-multi-thread", "macros"] }
zstd = "0.12" zstd = "0.12"
[[test]] [[test]]

View File

@@ -50,18 +50,24 @@ type DefaultHandler<B> = Option<Rc<ErrorHandler<B>>>;
/// will pass by unchanged by this middleware. /// will pass by unchanged by this middleware.
/// ///
/// # Examples /// # Examples
/// ## Handler Response
/// Header
/// ```
/// use actix_web::http::{header, StatusCode};
/// use actix_web::middleware::{ErrorHandlerResponse, ErrorHandlers};
/// use actix_web::{dev, web, App, HttpResponse, Result};
/// ///
/// fn add_error_header<B>(mut res: dev::ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> { /// Adding a header:
///
/// ```
/// use actix_web::{
/// dev::ServiceResponse,
/// http::{header, StatusCode},
/// middleware::{ErrorHandlerResponse, ErrorHandlers},
/// web, App, HttpResponse, Result,
/// };
///
/// fn add_error_header<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
/// res.response_mut().headers_mut().insert( /// res.response_mut().headers_mut().insert(
/// header::CONTENT_TYPE, /// header::CONTENT_TYPE,
/// header::HeaderValue::from_static("Error"), /// header::HeaderValue::from_static("Error"),
/// ); /// );
///
/// // body is unchanged, map to "left" slot
/// Ok(ErrorHandlerResponse::Response(res.map_into_left_body())) /// Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))
/// } /// }
/// ///
@@ -70,45 +76,62 @@ type DefaultHandler<B> = Option<Rc<ErrorHandler<B>>>;
/// .service(web::resource("/").route(web::get().to(HttpResponse::InternalServerError))); /// .service(web::resource("/").route(web::get().to(HttpResponse::InternalServerError)));
/// ``` /// ```
/// ///
/// Body Content /// Modifying response body:
///
/// ``` /// ```
/// use actix_web::http::{header, StatusCode}; /// use actix_web::{
/// use actix_web::middleware::{ErrorHandlerResponse, ErrorHandlers}; /// dev::ServiceResponse,
/// use actix_web::{dev, web, App, HttpResponse, Result}; /// http::{header, StatusCode},
/// fn add_error_body<B>(res: dev::ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> { /// middleware::{ErrorHandlerResponse, ErrorHandlers},
/// // Get the error message and status code /// web, App, HttpResponse, Result,
/// let error_message = "An error occurred"; /// };
/// // Destructures ServiceResponse into request and response components ///
/// let (req, res) = res.into_parts(); /// fn add_error_body<B>(res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
/// // Create a new response with the modified body /// // split service response into request and response components
/// let res = res.set_body(error_message).map_into_boxed_body(); /// let (req, res) = res.into_parts();
/// // Create a new ServiceResponse with the modified response ///
/// let res = dev::ServiceResponse::new(req, res).map_into_right_body(); /// // set body of response to modified body
/// Ok(ErrorHandlerResponse::Response(res)) /// let res = res.set_body("An error occurred.");
///} ///
/// // modified bodies need to be boxed and placed in the "right" slot
/// let res = ServiceResponse::new(req, res)
/// .map_into_boxed_body()
/// .map_into_right_body();
///
/// Ok(ErrorHandlerResponse::Response(res))
/// }
/// ///
/// let app = App::new() /// let app = App::new()
/// .wrap(ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, add_error_body)) /// .wrap(ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, add_error_body))
/// .service(web::resource("/").route(web::get().to(HttpResponse::InternalServerError))); /// .service(web::resource("/").route(web::get().to(HttpResponse::InternalServerError)));
/// ``` /// ```
/// ## Registering default handler ///
/// Registering default handler:
///
/// ``` /// ```
/// # use actix_web::http::{header, StatusCode}; /// # use actix_web::{
/// # use actix_web::middleware::{ErrorHandlerResponse, ErrorHandlers}; /// # dev::ServiceResponse,
/// # use actix_web::{dev, web, App, HttpResponse, Result}; /// # http::{header, StatusCode},
/// fn add_error_header<B>(mut res: dev::ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> { /// # middleware::{ErrorHandlerResponse, ErrorHandlers},
/// # web, App, HttpResponse, Result,
/// # };
/// fn add_error_header<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
/// res.response_mut().headers_mut().insert( /// res.response_mut().headers_mut().insert(
/// header::CONTENT_TYPE, /// header::CONTENT_TYPE,
/// header::HeaderValue::from_static("Error"), /// header::HeaderValue::from_static("Error"),
/// ); /// );
///
/// // body is unchanged, map to "left" slot
/// Ok(ErrorHandlerResponse::Response(res.map_into_left_body())) /// Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))
/// } /// }
/// ///
/// fn handle_bad_request<B>(mut res: dev::ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> { /// fn handle_bad_request<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
/// res.response_mut().headers_mut().insert( /// res.response_mut().headers_mut().insert(
/// header::CONTENT_TYPE, /// header::CONTENT_TYPE,
/// header::HeaderValue::from_static("Bad Request Error"), /// header::HeaderValue::from_static("Bad Request Error"),
/// ); /// );
///
/// // body is unchanged, map to "left" slot
/// Ok(ErrorHandlerResponse::Response(res.map_into_left_body())) /// Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))
/// } /// }
/// ///
@@ -122,20 +145,24 @@ type DefaultHandler<B> = Option<Rc<ErrorHandler<B>>>;
/// ) /// )
/// .service(web::resource("/").route(web::get().to(HttpResponse::InternalServerError))); /// .service(web::resource("/").route(web::get().to(HttpResponse::InternalServerError)));
/// ``` /// ```
/// Alternatively, you can set default handlers for only client or only server errors:
/// ///
/// ```rust /// You can set default handlers for all client (4xx) or all server (5xx) errors:
/// # use actix_web::http::{header, StatusCode}; ///
/// # use actix_web::middleware::{ErrorHandlerResponse, ErrorHandlers}; /// ```
/// # use actix_web::{dev, web, App, HttpResponse, Result}; /// # use actix_web::{
/// # fn add_error_header<B>(mut res: dev::ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> { /// # dev::ServiceResponse,
/// # http::{header, StatusCode},
/// # middleware::{ErrorHandlerResponse, ErrorHandlers},
/// # web, App, HttpResponse, Result,
/// # };
/// # fn add_error_header<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
/// # res.response_mut().headers_mut().insert( /// # res.response_mut().headers_mut().insert(
/// # header::CONTENT_TYPE, /// # header::CONTENT_TYPE,
/// # header::HeaderValue::from_static("Error"), /// # header::HeaderValue::from_static("Error"),
/// # ); /// # );
/// # Ok(ErrorHandlerResponse::Response(res.map_into_left_body())) /// # Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))
/// # } /// # }
/// # fn handle_bad_request<B>(mut res: dev::ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> { /// # fn handle_bad_request<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
/// # res.response_mut().headers_mut().insert( /// # res.response_mut().headers_mut().insert(
/// # header::CONTENT_TYPE, /// # header::CONTENT_TYPE,
/// # header::HeaderValue::from_static("Bad Request Error"), /// # header::HeaderValue::from_static("Bad Request Error"),

View File

@@ -62,7 +62,6 @@ actix-rt = { version = "2.1", default-features = false }
actix-tls = { version = "3", features = ["connect", "uri"] } actix-tls = { version = "3", features = ["connect", "uri"] }
actix-utils = "3" actix-utils = "3"
ahash = "0.7"
base64 = "0.21" base64 = "0.21"
bytes = "1" bytes = "1"
cfg-if = "1" cfg-if = "1"
@@ -80,7 +79,7 @@ rand = "0.8"
serde = "1.0" serde = "1.0"
serde_json = "1.0" serde_json = "1.0"
serde_urlencoded = "0.7" serde_urlencoded = "0.7"
tokio = { version = "1.18.5", features = ["sync"] } tokio = { version = "1.24.2", features = ["sync"] }
cookie = { version = "0.16", features = ["percent-encode"], optional = true } cookie = { version = "0.16", features = ["percent-encode"], optional = true }
@@ -106,7 +105,7 @@ futures-util = { version = "0.3.17", default-features = false }
static_assertions = "1.1" static_assertions = "1.1"
rcgen = "0.9" rcgen = "0.9"
rustls-pemfile = "1" rustls-pemfile = "1"
tokio = { version = "1.18.5", features = ["rt-multi-thread", "macros"] } tokio = { version = "1.24.2", features = ["rt-multi-thread", "macros"] }
zstd = "0.12" zstd = "0.12"
[[example]] [[example]]

View File

@@ -2,7 +2,7 @@
use std::{ use std::{
cell::RefCell, cell::RefCell,
collections::VecDeque, collections::{HashMap, VecDeque},
future::Future, future::Future,
io, io,
ops::Deref, ops::Deref,
@@ -17,7 +17,6 @@ use actix_codec::{AsyncRead, AsyncWrite, ReadBuf};
use actix_http::Protocol; use actix_http::Protocol;
use actix_rt::time::{sleep, Sleep}; use actix_rt::time::{sleep, Sleep};
use actix_service::Service; use actix_service::Service;
use ahash::AHashMap;
use futures_core::future::LocalBoxFuture; use futures_core::future::LocalBoxFuture;
use futures_util::FutureExt as _; use futures_util::FutureExt as _;
use http::uri::Authority; use http::uri::Authority;
@@ -62,7 +61,7 @@ where
{ {
fn new(config: ConnectorConfig) -> Self { fn new(config: ConnectorConfig) -> Self {
let permits = Arc::new(Semaphore::new(config.limit)); let permits = Arc::new(Semaphore::new(config.limit));
let available = RefCell::new(AHashMap::default()); let available = RefCell::new(HashMap::default());
Self(Rc::new(ConnectionPoolInnerPriv { Self(Rc::new(ConnectionPoolInnerPriv {
config, config,
@@ -124,7 +123,7 @@ where
Io: AsyncWrite + Unpin + 'static, Io: AsyncWrite + Unpin + 'static,
{ {
config: ConnectorConfig, config: ConnectorConfig,
available: RefCell<AHashMap<Key, VecDeque<PooledConnection<Io>>>>, available: RefCell<HashMap<Key, VecDeque<PooledConnection<Io>>>>,
permits: Arc<Semaphore>, permits: Arc<Semaphore>,
} }