diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index 6e57bf7a7..60d3ceb96 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -1,8 +1,12 @@ # Changes ## Unreleased - 2022-xx-xx + + +## 0.6.3 - 2023-01-21 - XHTML files now use `Content-Disposition: inline` instead of `attachment`. [#2903] - Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency. +- Update `tokio-uring` dependency to `0.4`. [#2903]: https://github.com/actix/actix-web/pull/2903 diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 018acdfb1..4c29c95b2 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.6.2" +version = "0.6.3" authors = [ "Nikolay Kim ", "Rob Ede ", @@ -29,7 +29,7 @@ actix-web = { version = "4", default-features = false } bitflags = "1" bytes = "1" derive_more = "0.99.5" -futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } +futures-core = { version = "0.3.17", default-features = false, features = ["alloc"] } http-range = "0.1.4" log = "0.4" mime = "0.3" @@ -40,8 +40,8 @@ v_htmlescape= "0.15" # experimental-io-uring [target.'cfg(target_os = "linux")'.dependencies] -tokio-uring = { version = "0.3", optional = true, features = ["bytes"] } -actix-server = { version = "2.1", optional = true } # ensure matching tokio-uring versions +tokio-uring = { version = "0.4", optional = true, features = ["bytes"] } +actix-server = { version = "2.2", optional = true } # ensure matching tokio-uring versions [dev-dependencies] actix-rt = "2.7" diff --git a/actix-files/README.md b/actix-files/README.md index a5078c8d5..9a7e6e722 100644 --- a/actix-files/README.md +++ b/actix-files/README.md @@ -3,11 +3,11 @@ > Static file serving for Actix Web [![crates.io](https://img.shields.io/crates/v/actix-files?label=latest)](https://crates.io/crates/actix-files) -[![Documentation](https://docs.rs/actix-files/badge.svg?version=0.6.2)](https://docs.rs/actix-files/0.6.2) +[![Documentation](https://docs.rs/actix-files/badge.svg?version=0.6.3)](https://docs.rs/actix-files/0.6.3) ![Version](https://img.shields.io/badge/rustc-1.59+-ab6000.svg) ![License](https://img.shields.io/crates/l/actix-files.svg)
-[![dependency status](https://deps.rs/crate/actix-files/0.6.2/status.svg)](https://deps.rs/crate/actix-files/0.6.2) +[![dependency status](https://deps.rs/crate/actix-files/0.6.3/status.svg)](https://deps.rs/crate/actix-files/0.6.3) [![Download](https://img.shields.io/crates/d/actix-files.svg)](https://crates.io/crates/actix-files) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) diff --git a/actix-files/src/files.rs b/actix-files/src/files.rs index a30ce6fd3..be2a450d2 100644 --- a/actix-files/src/files.rs +++ b/actix-files/src/files.rs @@ -142,7 +142,7 @@ impl Files { self } - /// Set custom directory renderer + /// Set custom directory renderer. pub fn files_listing_renderer(mut self, f: F) -> Self where for<'r, 's> F: @@ -152,7 +152,7 @@ impl Files { self } - /// Specifies mime override callback + /// Specifies MIME override callback. pub fn mime_override(mut self, f: F) -> Self where F: Fn(&mime::Name<'_>) -> DispositionType + 'static, @@ -390,3 +390,42 @@ impl ServiceFactory for Files { } } } + +#[cfg(test)] +mod tests { + use actix_web::{ + http::StatusCode, + test::{self, TestRequest}, + App, HttpResponse, + }; + + use super::*; + + #[actix_web::test] + async fn custom_files_listing_renderer() { + let srv = test::init_service( + App::new().service( + Files::new("/", "./tests") + .show_files_listing() + .files_listing_renderer(|dir, req| { + Ok(ServiceResponse::new( + req.clone(), + HttpResponse::Ok().body(dir.path.to_str().unwrap().to_owned()), + )) + }), + ), + ) + .await; + + let req = TestRequest::with_uri("/").to_request(); + let res = test::call_service(&srv, req).await; + + assert_eq!(res.status(), StatusCode::OK); + let body = test::read_body(res).await; + assert!( + body.ends_with(b"actix-files/tests/"), + "body {:?} does not end with `actix-files/tests/`", + body + ); + } +} diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 40327e5e8..0fbe39a8e 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -13,6 +13,7 @@ #![deny(rust_2018_idioms, nonstandard_style)] #![warn(future_incompatible, missing_docs, missing_debug_implementations)] +#![allow(clippy::uninlined_format_args)] use actix_service::boxed::{BoxService, BoxServiceFactory}; use actix_web::{ diff --git a/actix-files/src/path_buf.rs b/actix-files/src/path_buf.rs index 9ee1338c6..650f55247 100644 --- a/actix-files/src/path_buf.rs +++ b/actix-files/src/path_buf.rs @@ -30,7 +30,7 @@ impl PathBufWrap { let mut segment_count = path.matches('/').count() + 1; // we can decode the whole path here (instead of per-segment decoding) - // because we will reject `%2F` in paths using `segement_count`. + // because we will reject `%2F` in paths using `segment_count`. let path = percent_encoding::percent_decode_str(path) .decode_utf8() .map_err(|_| UriSegmentError::NotValidUtf8)?; diff --git a/actix-http-test/CHANGES.md b/actix-http-test/CHANGES.md index 028fe3ddc..0eeb80454 100644 --- a/actix-http-test/CHANGES.md +++ b/actix-http-test/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2022-xx-xx + + +## 3.1.0 - 2023-01-21 - Minimum supported Rust version (MSRV) is now 1.59. diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index 0a9ddf947..23fffdc17 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http-test" -version = "3.0.0" +version = "3.1.0" authors = ["Nikolay Kim "] description = "Various helpers for Actix applications to use during testing" keywords = ["http", "web", "framework", "async", "futures"] @@ -37,9 +37,8 @@ actix-rt = "2.2" actix-server = "2" awc = { version = "3", default-features = false } -base64 = "0.13" bytes = "1" -futures-core = { version = "0.3.7", default-features = false } +futures-core = { version = "0.3.17", default-features = false } http = "0.2.5" log = "0.4" socket2 = "0.4" @@ -48,7 +47,7 @@ serde_json = "1.0" slab = "0.4" serde_urlencoded = "0.7" tls-openssl = { version = "0.10.9", package = "openssl", optional = true } -tokio = { version = "1.8.4", features = ["sync"] } +tokio = { version = "1.18.5", features = ["sync"] } [dev-dependencies] actix-web = { version = "4", default-features = false, features = ["cookies"] } diff --git a/actix-http-test/README.md b/actix-http-test/README.md index 25e7c684e..910b5649d 100644 --- a/actix-http-test/README.md +++ b/actix-http-test/README.md @@ -3,11 +3,11 @@ > Various helpers for Actix applications to use during testing. [![crates.io](https://img.shields.io/crates/v/actix-http-test?label=latest)](https://crates.io/crates/actix-http-test) -[![Documentation](https://docs.rs/actix-http-test/badge.svg?version=3.0.0)](https://docs.rs/actix-http-test/3.0.0) +[![Documentation](https://docs.rs/actix-http-test/badge.svg?version=3.1.0)](https://docs.rs/actix-http-test/3.1.0) ![Version](https://img.shields.io/badge/rustc-1.59+-ab6000.svg) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http-test)
-[![Dependency Status](https://deps.rs/crate/actix-http-test/3.0.0/status.svg)](https://deps.rs/crate/actix-http-test/3.0.0) +[![Dependency Status](https://deps.rs/crate/actix-http-test/3.1.0/status.svg)](https://deps.rs/crate/actix-http-test/3.1.0) [![Download](https://img.shields.io/crates/d/actix-http-test.svg)](https://crates.io/crates/actix-http-test) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) diff --git a/actix-http-test/src/lib.rs b/actix-http-test/src/lib.rs index 8636ef9c4..a66f7b486 100644 --- a/actix-http-test/src/lib.rs +++ b/actix-http-test/src/lib.rs @@ -2,6 +2,7 @@ #![deny(rust_2018_idioms, nonstandard_style)] #![warn(future_incompatible)] +#![allow(clippy::uninlined_format_args)] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] @@ -87,6 +88,7 @@ pub async fn test_server_with_addr>( // notify TestServer that server and system have shut down // all thread managed resources should be dropped at this point + #[allow(clippy::let_underscore_future)] let _ = thread_stop_tx.send(()); }); @@ -294,6 +296,7 @@ impl Drop for TestServer { // without needing to await anything // signal server to stop + #[allow(clippy::let_underscore_future)] let _ = self.server.stop(true); // signal system to stop diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 045ae461f..6a45e79dd 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,15 +1,39 @@ # Changes ## Unreleased - 2022-xx-xx + + +## 3.3.0 - 2023-01-21 ### Added +- Implement `MessageBody` for `Cow<'static, str>` and `Cow<'static, [u8]>`. [#2959] - Implement `MessageBody` for `&mut B` where `B: MessageBody + Unpin`. [#2868] - Implement `MessageBody` for `Pin` where `B::Target: MessageBody`. [#2868] +- Automatic h2c detection via new service finalizer `HttpService::tcp_auto_h2c()`. [#2957] +- `HeaderMap::retain()`. [#2955] +- Header name constants in `header` module. [#2956] [#2968] + - `CACHE_STATUS` + - `CDN_CACHE_CONTROL` + - `CROSS_ORIGIN_EMBEDDER_POLICY` + - `CROSS_ORIGIN_OPENER_POLICY` + - `PERMISSIONS_POLICY` + - `X_FORWARDED_FOR` + - `X_FORWARDED_HOST` + - `X_FORWARDED_PROTO` + +### Fixed +- Fix non-empty body of HTTP/2 HEAD responses. [#2920] ### Performance - Improve overall performance of operations on `Extensions`. [#2890] +[#2959]: https://github.com/actix/actix-web/pull/2959 [#2868]: https://github.com/actix/actix-web/pull/2868 [#2890]: https://github.com/actix/actix-web/pull/2890 +[#2920]: https://github.com/actix/actix-web/pull/2920 +[#2957]: https://github.com/actix/actix-web/pull/2957 +[#2955]: https://github.com/actix/actix-web/pull/2955 +[#2956]: https://github.com/actix/actix-web/pull/2956 +[#2968]: https://github.com/actix/actix-web/pull/2968 ## 3.2.2 - 2022-09-11 @@ -657,7 +681,7 @@ - Reduce the level from `error` to `debug` for the log line that is emitted when a `500 Internal Server Error` is built using `HttpResponse::from_error`. [#2201] - `ResponseBuilder::message_body` now returns a `Result`. [#2201] - Remove `Unpin` bound on `ResponseBuilder::streaming`. [#2253] -- `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuation parameter. [#2226] +- `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuration parameter. [#2226] ### Removed - Stop re-exporting `http` crate's `HeaderMap` types in addition to ours. [#2171] diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index a8b888ef4..19302d002 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "3.2.2" +version = "3.3.0" authors = [ "Nikolay Kim ", "Rob Ede ", @@ -67,7 +67,7 @@ bytes = "1" bytestring = "1" derive_more = "0.99.5" encoding_rs = "0.8" -futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } +futures-core = { version = "0.3.17", default-features = false, features = ["alloc"] } http = "0.2.5" httparse = "1.5.1" httpdate = "1.0.1" @@ -77,7 +77,7 @@ mime = "0.3" percent-encoding = "2.1" pin-project-lite = "0.2" smallvec = "1.6.1" -tokio = { version = "1.13.1", features = [] } +tokio = { version = "1.18.5", features = [] } tokio-util = { version = "0.7", features = ["io", "codec"] } tracing = { version = "0.1.30", default-features = false, features = ["log"] } @@ -86,7 +86,7 @@ h2 = { version = "0.3.9", optional = true } # websockets local-channel = { version = "0.1", optional = true } -base64 = { version = "0.13", optional = true } +base64 = { version = "0.21", optional = true } rand = { version = "0.8", optional = true } sha1 = { version = "0.10", optional = true } @@ -96,7 +96,7 @@ actix-tls = { version = "3", default-features = false, optional = true } # compress-* brotli = { version = "3.3.3", optional = true } flate2 = { version = "1.0.13", optional = true } -zstd = { version = "0.11", optional = true } +zstd = { version = "0.12", optional = true } [dev-dependencies] actix-http-test = { version = "3", features = ["openssl"] } @@ -105,9 +105,9 @@ actix-tls = { version = "3", features = ["openssl"] } actix-web = "4" async-stream = "0.3" -criterion = { version = "0.3", features = ["html_reports"] } +criterion = { version = "0.4", features = ["html_reports"] } env_logger = "0.9" -futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] } +futures-util = { version = "0.3.17", default-features = false, features = ["alloc"] } memchr = "2.4" once_cell = "1.9" rcgen = "0.9" @@ -119,7 +119,7 @@ serde_json = "1.0" static_assertions = "1" tls-openssl = { package = "openssl", version = "0.10.9" } tls-rustls = { package = "rustls", version = "0.20.0" } -tokio = { version = "1.8.4", features = ["net", "rt", "macros"] } +tokio = { version = "1.18.5", features = ["net", "rt", "macros"] } [[example]] name = "ws" diff --git a/actix-http/README.md b/actix-http/README.md index 994cf97c6..2dd2d248f 100644 --- a/actix-http/README.md +++ b/actix-http/README.md @@ -3,11 +3,11 @@ > HTTP primitives for the Actix ecosystem. [![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.2.2)](https://docs.rs/actix-http/3.2.2) +[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.3.0)](https://docs.rs/actix-http/3.3.0) ![Version](https://img.shields.io/badge/rustc-1.59+-ab6000.svg) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http.svg)
-[![dependency status](https://deps.rs/crate/actix-http/3.2.2/status.svg)](https://deps.rs/crate/actix-http/3.2.2) +[![dependency status](https://deps.rs/crate/actix-http/3.3.0/status.svg)](https://deps.rs/crate/actix-http/3.3.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) diff --git a/actix-http/benches/quality-value.rs b/actix-http/benches/quality-value.rs index 33ba9c4c8..0ed274ded 100644 --- a/actix-http/benches/quality-value.rs +++ b/actix-http/benches/quality-value.rs @@ -1,3 +1,5 @@ +#![allow(clippy::uninlined_format_args)] + use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; const CODES: &[u16] = &[0, 1000, 201, 800, 550]; diff --git a/actix-http/examples/h2c-detect.rs b/actix-http/examples/h2c-detect.rs new file mode 100644 index 000000000..aa3dd5d31 --- /dev/null +++ b/actix-http/examples/h2c-detect.rs @@ -0,0 +1,29 @@ +//! An example that supports automatic selection of plaintext h1/h2c connections. +//! +//! Notably, both the following commands will work. +//! ```console +//! $ curl --http1.1 'http://localhost:8080/' +//! $ curl --http2-prior-knowledge 'http://localhost:8080/' +//! ``` + +use std::{convert::Infallible, io}; + +use actix_http::{HttpService, Request, Response, StatusCode}; +use actix_server::Server; + +#[tokio::main(flavor = "current_thread")] +async fn main() -> io::Result<()> { + env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); + + Server::build() + .bind("h2c-detect", ("127.0.0.1", 8080), || { + HttpService::build() + .finish(|_req: Request| async move { + Ok::<_, Infallible>(Response::build(StatusCode::OK).body("Hello!")) + }) + .tcp_auto_h2c() + })? + .workers(2) + .run() + .await +} diff --git a/actix-http/src/body/message_body.rs b/actix-http/src/body/message_body.rs index 0cfaa8653..e274cf8aa 100644 --- a/actix-http/src/body/message_body.rs +++ b/actix-http/src/body/message_body.rs @@ -120,7 +120,7 @@ pub trait MessageBody { } mod foreign_impls { - use std::ops::DerefMut; + use std::{borrow::Cow, ops::DerefMut}; use super::*; @@ -324,6 +324,39 @@ mod foreign_impls { } } + impl MessageBody for Cow<'static, [u8]> { + type Error = Infallible; + + #[inline] + fn size(&self) -> BodySize { + BodySize::Sized(self.len() as u64) + } + + #[inline] + fn poll_next( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll>> { + if self.is_empty() { + Poll::Ready(None) + } else { + let bytes = match mem::take(self.get_mut()) { + Cow::Borrowed(b) => Bytes::from_static(b), + Cow::Owned(b) => Bytes::from(b), + }; + Poll::Ready(Some(Ok(bytes))) + } + } + + #[inline] + fn try_into_bytes(self) -> Result { + match self { + Cow::Borrowed(b) => Ok(Bytes::from_static(b)), + Cow::Owned(b) => Ok(Bytes::from(b)), + } + } + } + impl MessageBody for &'static str { type Error = Infallible; @@ -379,6 +412,39 @@ mod foreign_impls { } } + impl MessageBody for Cow<'static, str> { + type Error = Infallible; + + #[inline] + fn size(&self) -> BodySize { + BodySize::Sized(self.len() as u64) + } + + #[inline] + fn poll_next( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll>> { + if self.is_empty() { + Poll::Ready(None) + } else { + let bytes = match mem::take(self.get_mut()) { + Cow::Borrowed(s) => Bytes::from_static(s.as_bytes()), + Cow::Owned(s) => Bytes::from(s.into_bytes()), + }; + Poll::Ready(Some(Ok(bytes))) + } + } + + #[inline] + fn try_into_bytes(self) -> Result { + match self { + Cow::Borrowed(s) => Ok(Bytes::from_static(s.as_bytes())), + Cow::Owned(s) => Ok(Bytes::from(s.into_bytes())), + } + } + } + impl MessageBody for bytestring::ByteString { type Error = Infallible; diff --git a/actix-http/src/body/sized_stream.rs b/actix-http/src/body/sized_stream.rs index e5e27b287..08cd81a0d 100644 --- a/actix-http/src/body/sized_stream.rs +++ b/actix-http/src/body/sized_stream.rs @@ -44,7 +44,7 @@ where #[inline] fn size(&self) -> BodySize { - BodySize::Sized(self.size as u64) + BodySize::Sized(self.size) } /// Attempts to pull out the next value of the underlying [`Stream`]. diff --git a/actix-http/src/builder.rs b/actix-http/src/builder.rs index 71b933835..e2693acaf 100644 --- a/actix-http/src/builder.rs +++ b/actix-http/src/builder.rs @@ -186,7 +186,7 @@ where self } - /// Finish service configuration and create a HTTP Service for HTTP/1 protocol. + /// Finish service configuration and create a service for the HTTP/1 protocol. pub fn h1(self, service: F) -> H1Service where B: MessageBody, @@ -209,7 +209,7 @@ where .on_connect_ext(self.on_connect_ext) } - /// Finish service configuration and create a HTTP service for HTTP/2 protocol. + /// Finish service configuration and create a service for the HTTP/2 protocol. #[cfg(feature = "http2")] #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] pub fn h2(self, service: F) -> crate::h2::H2Service diff --git a/actix-http/src/h1/chunked.rs b/actix-http/src/h1/chunked.rs index 4005ed892..fc9081b81 100644 --- a/actix-http/src/h1/chunked.rs +++ b/actix-http/src/h1/chunked.rs @@ -71,7 +71,7 @@ impl ChunkedState { match size.checked_mul(radix) { Some(n) => { - *size = n as u64; + *size = n; *size += rem as u64; Poll::Ready(Ok(ChunkedState::Size)) diff --git a/actix-http/src/h1/dispatcher_tests.rs b/actix-http/src/h1/dispatcher_tests.rs index 3eea859bf..d39c5bd69 100644 --- a/actix-http/src/h1/dispatcher_tests.rs +++ b/actix-http/src/h1/dispatcher_tests.rs @@ -64,7 +64,7 @@ fn drop_payload_service( fn echo_payload_service() -> impl Service, Error = Error> { fn_service(|mut req: Request| { Box::pin(async move { - use futures_util::stream::StreamExt as _; + use futures_util::StreamExt as _; let mut pl = req.take_payload(); let mut body = BytesMut::new(); diff --git a/actix-http/src/h1/encoder.rs b/actix-http/src/h1/encoder.rs index 21cfd75c4..abe396ce2 100644 --- a/actix-http/src/h1/encoder.rs +++ b/actix-http/src/h1/encoder.rs @@ -450,7 +450,7 @@ impl TransferEncoding { buf.extend_from_slice(&msg[..len as usize]); - *remaining -= len as u64; + *remaining -= len; Ok(*remaining == 0) } else { Ok(true) diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 680936f0f..3e618820e 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -29,7 +29,7 @@ use crate::{ HeaderName, HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING, UPGRADE, }, service::HttpFlow, - Extensions, OnConnectData, Payload, Request, Response, ResponseHead, + Extensions, Method, OnConnectData, Payload, Request, Response, ResponseHead, }; const CHUNK_SIZE: usize = 16_384; @@ -118,6 +118,7 @@ where let payload = crate::h2::Payload::new(body); let pl = Payload::H2 { payload }; let mut req = Request::with_payload(pl); + let head_req = parts.method == Method::HEAD; let head = req.head_mut(); head.uri = parts.uri; @@ -135,10 +136,10 @@ where actix_rt::spawn(async move { // resolve service call and send response. let res = match fut.await { - Ok(res) => handle_response(res.into(), tx, config).await, + Ok(res) => handle_response(res.into(), tx, config, head_req).await, Err(err) => { let res: Response = err.into(); - handle_response(res, tx, config).await + handle_response(res, tx, config, head_req).await } }; @@ -206,6 +207,7 @@ async fn handle_response( res: Response, mut tx: SendResponse, config: ServiceConfig, + head_req: bool, ) -> Result<(), DispatchError> where B: MessageBody, @@ -215,14 +217,14 @@ where // prepare response. let mut size = body.size(); let res = prepare_response(config, res.head(), &mut size); - let eof = size.is_eof(); + let eof_or_head = size.is_eof() || head_req; // send response head and return on eof. let mut stream = tx - .send_response(res, eof) + .send_response(res, eof_or_head) .map_err(DispatchError::SendResponse)?; - if eof { + if eof_or_head { return Ok(()); } diff --git a/actix-http/src/header/common.rs b/actix-http/src/header/common.rs new file mode 100644 index 000000000..67b0a9069 --- /dev/null +++ b/actix-http/src/header/common.rs @@ -0,0 +1,51 @@ +//! Common header names not defined in [`http`]. +//! +//! Any headers added to this file will need to be re-exported from the list at `crate::headers`. + +use http::header::HeaderName; + +/// Response header field that indicates how caches have handled that response and its corresponding +/// request. +/// +/// See [RFC 9211](https://www.rfc-editor.org/rfc/rfc9211) for full semantics. +pub const CACHE_STATUS: HeaderName = HeaderName::from_static("cache-status"); + +/// Response header field that allows origin servers to control the behavior of CDN caches +/// interposed between them and clients separately from other caches that might handle the response. +/// +/// See [RFC 9213](https://www.rfc-editor.org/rfc/rfc9213) for full semantics. +pub const CDN_CACHE_CONTROL: HeaderName = HeaderName::from_static("cdn-cache-control"); + +/// Response header that prevents a document from loading any cross-origin resources that don't +/// explicitly grant the document permission (using [CORP] or [CORS]). +/// +/// [CORP]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy_(CORP) +/// [CORS]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS +pub const CROSS_ORIGIN_EMBEDDER_POLICY: HeaderName = + HeaderName::from_static("cross-origin-embedder-policy"); + +/// Response header that allows you to ensure a top-level document does not share a browsing context +/// group with cross-origin documents. +pub const CROSS_ORIGIN_OPENER_POLICY: HeaderName = + HeaderName::from_static("cross-origin-opener-policy"); + +/// Response header that conveys a desire that the browser blocks no-cors cross-origin/cross-site +/// requests to the given resource. +pub const CROSS_ORIGIN_RESOURCE_POLICY: HeaderName = + HeaderName::from_static("cross-origin-resource-policy"); + +/// Response header that provides a mechanism to allow and deny the use of browser features in a +/// document or within any `