Merge branch 'master' into scope_work

This commit is contained in:
Rob Ede 2024-06-07 19:04:43 +01:00 committed by GitHub
commit 47e4950522
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 156 additions and 25 deletions

View File

@ -49,7 +49,7 @@ jobs:
toolchain: ${{ matrix.version.version }} toolchain: ${{ matrix.version.version }}
- name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean - name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean
uses: taiki-e/install-action@v2.33.34 uses: taiki-e/install-action@v2.34.0
with: with:
tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean
@ -80,7 +80,7 @@ jobs:
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0 uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
- name: Install cargo-hack - name: Install cargo-hack
uses: taiki-e/install-action@v2.33.34 uses: taiki-e/install-action@v2.34.0
with: with:
tool: cargo-hack tool: cargo-hack

View File

@ -18,7 +18,7 @@ concurrency:
jobs: jobs:
read_msrv: read_msrv:
name: Read MSRV name: Read MSRV
uses: actions-rust-lang/msrv/.github/workflows/msrv.yml@main uses: actions-rust-lang/msrv/.github/workflows/msrv.yml@v0.1.0
build_and_test: build_and_test:
needs: read_msrv needs: read_msrv
@ -64,7 +64,7 @@ jobs:
toolchain: ${{ matrix.version.version }} toolchain: ${{ matrix.version.version }}
- name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean - name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean
uses: taiki-e/install-action@v2.33.34 uses: taiki-e/install-action@v2.34.0
with: with:
tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean
@ -113,7 +113,7 @@ jobs:
toolchain: nightly toolchain: nightly
- name: Install just - name: Install just
uses: taiki-e/install-action@v2.33.34 uses: taiki-e/install-action@v2.34.0
with: with:
tool: just tool: just

View File

@ -23,7 +23,7 @@ jobs:
components: llvm-tools-preview components: llvm-tools-preview
- name: Install just,cargo-llvm-cov - name: Install just,cargo-llvm-cov
uses: taiki-e/install-action@v2.33.34 uses: taiki-e/install-action@v2.34.0
with: with:
tool: just,cargo-llvm-cov tool: just,cargo-llvm-cov

View File

@ -79,10 +79,10 @@ jobs:
- name: Install Rust - name: Install Rust
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0 uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
with: with:
toolchain: nightly-2024-04-26 toolchain: nightly-2024-06-07
- name: Install cargo-public-api - name: Install cargo-public-api
uses: taiki-e/install-action@v2.33.34 uses: taiki-e/install-action@v2.34.0
with: with:
tool: cargo-public-api tool: cargo-public-api

View File

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

View File

@ -15,7 +15,6 @@
<!-- prettier-ignore-end --> <!-- prettier-ignore-end -->
## Example ## Example
Dependencies: Dependencies:
@ -65,6 +64,7 @@ async fn main() -> std::io::Result<()> {
``` ```
Curl request : Curl request :
```bash ```bash
curl -v --request POST \ curl -v --request POST \
--url http://localhost:8080/videos \ --url http://localhost:8080/videos \
@ -72,7 +72,6 @@ curl -v --request POST \
-F file=@./Cargo.lock -F file=@./Cargo.lock
``` ```
### Examples ### Examples
https://github.com/actix/examples/tree/master/forms/multipart https://github.com/actix/examples/tree/master/forms/multipart

View File

@ -3,8 +3,9 @@
## Unreleased ## Unreleased
- 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.
- Minimum supported Rust version (MSRV) is now 1.72. - 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.
- Minimum supported Rust version (MSRV) is now 1.72.
## 0.1.3 ## 0.1.3

View File

@ -149,6 +149,8 @@ where
StreamType::Rustls023(_) => true, StreamType::Rustls023(_) => true,
}; };
let client_cfg = cfg.clone();
// 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 {
@ -460,7 +462,13 @@ where
} }
}; };
Client::builder().connector(connector).finish() let mut client_builder = Client::builder().connector(connector);
if client_cfg.disable_redirects {
client_builder = client_builder.disable_redirects();
}
client_builder.finish()
}; };
TestServer { TestServer {
@ -480,6 +488,7 @@ enum HttpVer {
Both, Both,
} }
#[allow(clippy::large_enum_variant)]
#[derive(Clone)] #[derive(Clone)]
enum StreamType { enum StreamType {
Tcp, Tcp,
@ -507,6 +516,7 @@ pub struct TestServerConfig {
client_request_timeout: Duration, client_request_timeout: Duration,
port: u16, port: u16,
workers: usize, workers: usize,
disable_redirects: bool,
} }
impl Default for TestServerConfig { impl Default for TestServerConfig {
@ -524,6 +534,7 @@ impl TestServerConfig {
client_request_timeout: Duration::from_secs(5), client_request_timeout: Duration::from_secs(5),
port: 0, port: 0,
workers: 1, workers: 1,
disable_redirects: false,
} }
} }
@ -611,6 +622,15 @@ impl TestServerConfig {
self.workers = workers; self.workers = workers;
self self
} }
/// Instruct the client to not follow redirects.
///
/// By default, the client will follow up to 10 consecutive redirects
/// before giving up.
pub fn disable_redirects(mut self) -> Self {
self.disable_redirects = true;
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

@ -2,6 +2,7 @@
## Unreleased ## Unreleased
- 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.
- Add a scope macro that takes a path - Add a scope macro that takes a path

View File

@ -16,7 +16,7 @@ rust-version.workspace = true
proc-macro = true proc-macro = true
[dependencies] [dependencies]
actix-router = "0.5" actix-router = { version = "0.5", default-features = false }
proc-macro2 = "1" proc-macro2 = "1"
quote = "1" quote = "1"
syn = { version = "2", features = ["full", "extra-traits"] } syn = { version = "2", features = ["full", "extra-traits"] }

View File

@ -20,10 +20,7 @@ error: custom attribute panicked
13 | #[get("/{}")] 13 | #[get("/{}")]
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
| |
= help: message: Wrong path pattern: "/{}" regex parse error: = help: message: Wrong path pattern: "/{}" empty capture group names are not allowed
((?s-m)^/(?P<>[^/]+))$
^
error: empty capture group name
error: custom attribute panicked error: custom attribute panicked
--> $DIR/route-malformed-path-fail.rs:23:1 --> $DIR/route-malformed-path-fail.rs:23:1

View File

@ -2,6 +2,12 @@
## Unreleased ## Unreleased
### Added
- Add `CustomizeResponder::add_cookie()` method.
- Add `guard::GuardContext::app_data()` method.
- Implement `From<Box<dyn ResponseError>>` for `Error`.
## 4.6.0 ## 4.6.0
### Added ### Added

View File

@ -35,7 +35,6 @@ features = [
"secure-cookies", "secure-cookies",
] ]
[lib] [lib]
name = "actix_web" name = "actix_web"
path = "src/lib.rs" path = "src/lib.rs"
@ -130,6 +129,7 @@ awc = { version = "3", features = ["openssl"] }
brotli = "6" brotli = "6"
const-str = "0.5" const-str = "0.5"
core_affinity = "0.8"
criterion = { version = "0.5", features = ["html_reports"] } criterion = { version = "0.5", features = ["html_reports"] }
env_logger = "0.11" env_logger = "0.11"
flate2 = "1.0.13" flate2 = "1.0.13"

View File

@ -0,0 +1,41 @@
use std::{
io,
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
},
thread,
};
use actix_web::{middleware, web, App, HttpServer};
async fn hello() -> &'static str {
"Hello world!"
}
#[actix_web::main]
async fn main() -> io::Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
let core_ids = core_affinity::get_core_ids().unwrap();
let n_core_ids = core_ids.len();
let next_core_id = Arc::new(AtomicUsize::new(0));
HttpServer::new(move || {
let pin = Arc::clone(&next_core_id).fetch_add(1, Ordering::AcqRel);
log::info!(
"setting CPU affinity for worker {}: pinning to core {}",
thread::current().name().unwrap(),
pin,
);
core_affinity::set_for_current(core_ids[pin]);
App::new()
.wrap(middleware::Logger::default())
.service(web::resource("/").get(hello))
})
.bind(("127.0.0.1", 8080))?
.workers(n_core_ids)
.run()
.await
}

View File

@ -112,8 +112,8 @@ where
/// }) /// })
/// ``` /// ```
#[doc(alias = "manage")] #[doc(alias = "manage")]
pub fn app_data<U: 'static>(mut self, ext: U) -> Self { pub fn app_data<U: 'static>(mut self, data: U) -> Self {
self.extensions.insert(ext); self.extensions.insert(data);
self self
} }

View File

@ -60,6 +60,12 @@ impl<T: ResponseError + 'static> From<T> for Error {
} }
} }
impl From<Box<dyn ResponseError>> for Error {
fn from(value: Box<dyn ResponseError>) -> Self {
Error { cause: value }
}
}
impl From<Error> for Response<BoxBody> { impl From<Error> for Response<BoxBody> {
fn from(err: Error) -> Response<BoxBody> { fn from(err: Error) -> Response<BoxBody> {
err.error_response().into() err.error_response().into()

View File

@ -110,6 +110,12 @@ impl<'a> GuardContext<'a> {
pub fn header<H: Header>(&self) -> Option<H> { pub fn header<H: Header>(&self) -> Option<H> {
H::parse(self.req).ok() H::parse(self.req).ok()
} }
/// Counterpart to [HttpRequest::app_data](crate::HttpRequest::app_data).
#[inline]
pub fn app_data<T: 'static>(&self) -> Option<&T> {
self.req.app_data()
}
} }
/// Interface for routing guards. /// Interface for routing guards.
@ -512,4 +518,18 @@ mod tests {
.to_srv_request(); .to_srv_request();
assert!(guard.check(&req.guard_ctx())); assert!(guard.check(&req.guard_ctx()));
} }
#[test]
fn app_data() {
const TEST_VALUE: u32 = 42;
let guard = fn_guard(|ctx| dbg!(ctx.app_data::<u32>()) == Some(&TEST_VALUE));
let req = TestRequest::default().app_data(TEST_VALUE).to_srv_request();
assert!(guard.check(&req.guard_ctx()));
let req = TestRequest::default()
.app_data(TEST_VALUE * 2)
.to_srv_request();
assert!(!guard.check(&req.guard_ctx()));
}
} }

View File

@ -7,7 +7,7 @@ use actix_http::{
use crate::{HttpRequest, HttpResponse, Responder}; use crate::{HttpRequest, HttpResponse, Responder};
/// Allows overriding status code and headers for a [`Responder`]. /// Allows overriding status code and headers (including cookies) for a [`Responder`].
/// ///
/// Created by calling the [`customize`](Responder::customize) method on a [`Responder`] type. /// Created by calling the [`customize`](Responder::customize) method on a [`Responder`] type.
pub struct CustomizeResponder<R> { pub struct CustomizeResponder<R> {
@ -137,6 +137,29 @@ impl<R: Responder> CustomizeResponder<R> {
Some(&mut self.inner) Some(&mut self.inner)
} }
} }
/// Appends a `cookie` to the final response.
///
/// # Errors
///
/// Final response will be an error if `cookie` cannot be converted into a valid header value.
#[cfg(feature = "cookies")]
pub fn add_cookie(mut self, cookie: &crate::cookie::Cookie<'_>) -> Self {
use actix_http::header::{TryIntoHeaderValue as _, SET_COOKIE};
if let Some(inner) = self.inner() {
match cookie.to_string().try_into_value() {
Ok(val) => {
inner.append_headers.append(SET_COOKIE, val);
}
Err(err) => {
self.error = Some(err.into());
}
}
}
self
}
} }
impl<T> Responder for CustomizeResponder<T> impl<T> Responder for CustomizeResponder<T>
@ -175,6 +198,7 @@ mod tests {
use super::*; use super::*;
use crate::{ use crate::{
cookie::Cookie,
http::header::{HeaderValue, CONTENT_TYPE}, http::header::{HeaderValue, CONTENT_TYPE},
test::TestRequest, test::TestRequest,
}; };
@ -209,6 +233,22 @@ mod tests {
to_bytes(res.into_body()).await.unwrap(), to_bytes(res.into_body()).await.unwrap(),
Bytes::from_static(b"test"), Bytes::from_static(b"test"),
); );
let res = "test"
.to_string()
.customize()
.add_cookie(&Cookie::new("name", "value"))
.respond_to(&req);
assert!(res.status().is_success());
assert_eq!(
res.cookies().collect::<Vec<Cookie<'_>>>(),
vec![Cookie::new("name", "value")],
);
assert_eq!(
to_bytes(res.into_body()).await.unwrap(),
Bytes::from_static(b"test"),
);
} }
#[actix_rt::test] #[actix_rt::test]