From 3851a377dffd6ef37e6d3ddac8ea58b19cb4ef74 Mon Sep 17 00:00:00 2001 From: Levi Notik Date: Thu, 6 Feb 2020 13:00:22 -0500 Subject: [PATCH 001/157] Fix minor grammatical errors (#1341) --- src/web.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/web.rs b/src/web.rs index 50d99479a..962c1157b 100644 --- a/src/web.rs +++ b/src/web.rs @@ -96,7 +96,7 @@ pub fn route() -> Route { /// ); /// ``` /// -/// In the above example, one `GET` route get added: +/// In the above example, one `GET` route gets added: /// * /{project_id} /// pub fn get() -> Route { @@ -114,7 +114,7 @@ pub fn get() -> Route { /// ); /// ``` /// -/// In the above example, one `POST` route get added: +/// In the above example, one `POST` route gets added: /// * /{project_id} /// pub fn post() -> Route { @@ -132,7 +132,7 @@ pub fn post() -> Route { /// ); /// ``` /// -/// In the above example, one `PUT` route get added: +/// In the above example, one `PUT` route gets added: /// * /{project_id} /// pub fn put() -> Route { @@ -150,7 +150,7 @@ pub fn put() -> Route { /// ); /// ``` /// -/// In the above example, one `PATCH` route get added: +/// In the above example, one `PATCH` route gets added: /// * /{project_id} /// pub fn patch() -> Route { @@ -168,7 +168,7 @@ pub fn patch() -> Route { /// ); /// ``` /// -/// In the above example, one `DELETE` route get added: +/// In the above example, one `DELETE` route gets added: /// * /{project_id} /// pub fn delete() -> Route { @@ -186,7 +186,7 @@ pub fn delete() -> Route { /// ); /// ``` /// -/// In the above example, one `HEAD` route get added: +/// In the above example, one `HEAD` route gets added: /// * /{project_id} /// pub fn head() -> Route { @@ -204,7 +204,7 @@ pub fn head() -> Route { /// ); /// ``` /// -/// In the above example, one `GET` route get added: +/// In the above example, one `GET` route gets added: /// * /{project_id} /// pub fn method(method: Method) -> Route { From 728b944360e38706de8100c9d2294e46374c2f39 Mon Sep 17 00:00:00 2001 From: zero-systems <60021149+zero-systems@users.noreply.github.com> Date: Fri, 7 Feb 2020 17:08:25 +1000 Subject: [PATCH 002/157] Extensions module improvement and tests. (#1297) * replace get.is_some to contains_key * Add tests * remove unnecessary box cast * fix missing uints * asserts fix Co-authored-by: Yuki Okushi --- actix-http/src/extensions.rs | 101 +++++++++++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 9 deletions(-) diff --git a/actix-http/src/extensions.rs b/actix-http/src/extensions.rs index d85ca184d..5114ce140 100644 --- a/actix-http/src/extensions.rs +++ b/actix-http/src/extensions.rs @@ -28,33 +28,30 @@ impl Extensions { /// Check if container contains entry pub fn contains(&self) -> bool { - self.map.get(&TypeId::of::()).is_some() + self.map.contains_key(&TypeId::of::()) } /// Get a reference to a type previously inserted on this `Extensions`. pub fn get(&self) -> Option<&T> { self.map .get(&TypeId::of::()) - .and_then(|boxed| (&**boxed as &(dyn Any + 'static)).downcast_ref()) + .and_then(|boxed| boxed.downcast_ref()) } /// Get a mutable reference to a type previously inserted on this `Extensions`. pub fn get_mut(&mut self) -> Option<&mut T> { self.map .get_mut(&TypeId::of::()) - .and_then(|boxed| (&mut **boxed as &mut (dyn Any + 'static)).downcast_mut()) + .and_then(|boxed| boxed.downcast_mut()) } /// Remove a type from this `Extensions`. /// /// If a extension of this type existed, it will be returned. pub fn remove(&mut self) -> Option { - self.map.remove(&TypeId::of::()).and_then(|boxed| { - (boxed as Box) - .downcast() - .ok() - .map(|boxed| *boxed) - }) + self.map + .remove(&TypeId::of::()) + .and_then(|boxed| boxed.downcast().ok().map(|boxed| *boxed)) } /// Clear the `Extensions` of all inserted extensions. @@ -70,6 +67,92 @@ impl fmt::Debug for Extensions { } } +#[test] +fn test_remove() { + let mut map = Extensions::new(); + + map.insert::(123); + assert!(map.get::().is_some()); + + map.remove::(); + assert!(map.get::().is_none()); +} + +#[test] +fn test_clear() { + let mut map = Extensions::new(); + + map.insert::(8); + map.insert::(16); + map.insert::(32); + + assert!(map.contains::()); + assert!(map.contains::()); + assert!(map.contains::()); + + map.clear(); + + assert!(!map.contains::()); + assert!(!map.contains::()); + assert!(!map.contains::()); + + map.insert::(10); + assert_eq!(*map.get::().unwrap(), 10); +} + +#[test] +fn test_integers() { + let mut map = Extensions::new(); + + map.insert::(8); + map.insert::(16); + map.insert::(32); + map.insert::(64); + map.insert::(128); + map.insert::(8); + map.insert::(16); + map.insert::(32); + map.insert::(64); + map.insert::(128); + assert!(map.get::().is_some()); + assert!(map.get::().is_some()); + assert!(map.get::().is_some()); + assert!(map.get::().is_some()); + assert!(map.get::().is_some()); + assert!(map.get::().is_some()); + assert!(map.get::().is_some()); + assert!(map.get::().is_some()); + assert!(map.get::().is_some()); + assert!(map.get::().is_some()); +} + +#[test] +fn test_composition() { + struct Magi(pub T); + + struct Madoka { + pub god: bool, + } + + struct Homura { + pub attempts: usize, + } + + struct Mami { + pub guns: usize, + } + + let mut map = Extensions::new(); + + map.insert(Magi(Madoka { god: false })); + map.insert(Magi(Homura { attempts: 0 })); + map.insert(Magi(Mami { guns: 999 })); + + assert!(!map.get::>().unwrap().0.god); + assert_eq!(0, map.get::>().unwrap().0.attempts); + assert_eq!(999, map.get::>().unwrap().0.guns); +} + #[test] fn test_extensions() { #[derive(Debug, PartialEq)] From 6406f56ca212bf462745e09ffc7f33a157b2c1e8 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 7 Feb 2020 23:16:32 +0900 Subject: [PATCH 003/157] Fix/suppress warnings --- actix-files/src/error.rs | 1 + awc/tests/test_rustls_client.rs | 1 + examples/uds.rs | 7 ++++--- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/actix-files/src/error.rs b/actix-files/src/error.rs index 49a46e58d..9b30cbaa2 100644 --- a/actix-files/src/error.rs +++ b/actix-files/src/error.rs @@ -5,6 +5,7 @@ use derive_more::Display; #[derive(Display, Debug, PartialEq)] pub enum FilesError { /// Path is not a directory + #[allow(dead_code)] #[display(fmt = "Path is not a directory. Unable to serve static files")] IsNotDirectory, diff --git a/awc/tests/test_rustls_client.rs b/awc/tests/test_rustls_client.rs index 1d7eb7bc5..8863dfcbe 100644 --- a/awc/tests/test_rustls_client.rs +++ b/awc/tests/test_rustls_client.rs @@ -11,6 +11,7 @@ use futures::future::ok; use open_ssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslVerifyMode}; use rust_tls::ClientConfig; +#[allow(unused)] fn ssl_acceptor() -> SslAcceptor { // load ssl keys let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); diff --git a/examples/uds.rs b/examples/uds.rs index 77f245d99..06aa87f4d 100644 --- a/examples/uds.rs +++ b/examples/uds.rs @@ -1,6 +1,6 @@ -use actix_web::{ - get, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer, -}; +use actix_web::{get, web, HttpRequest}; +#[cfg(unix)] +use actix_web::{middleware, App, Error, HttpResponse, HttpServer}; #[get("/resource1/{name}/index.html")] async fn index(req: HttpRequest, name: web::Path) -> String { @@ -8,6 +8,7 @@ async fn index(req: HttpRequest, name: web::Path) -> String { format!("Hello: {}!\r\n", name) } +#[cfg(unix)] async fn index_async(req: HttpRequest) -> Result<&'static str, Error> { println!("REQ: {:?}", req); Ok("Hello world!\r\n") From 53ff3ad0998ccbe9fdbf2398ca32b795149254fa Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 7 Feb 2020 23:17:03 +0900 Subject: [PATCH 004/157] More ignore test causes timeout --- .github/workflows/windows.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 36b224ba6..5fd785fad 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -56,3 +56,4 @@ jobs: --skip=test_slow_request --skip=test_connection_force_close --skip=test_connection_server_close + --skip=test_connection_wait_queue_force_close From 7d40b66300a94c6560ac75a3cbe417adae21f08e Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sat, 8 Feb 2020 04:28:34 +0900 Subject: [PATCH 005/157] Add some Actions workflows --- .github/workflows/linux.yml | 90 ++++++++++++++++++++++++++++++++ .github/workflows/upload-doc.yml | 35 +++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 .github/workflows/linux.yml create mode 100644 .github/workflows/upload-doc.yml diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 000000000..875ce10b4 --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,90 @@ +name: CI (Linux) + +on: [push, pull_request] + +jobs: + build_and_test: + strategy: + fail-fast: false + matrix: + version: + - 1.39.0 # MSRV + - stable + - nightly + + name: ${{ matrix.version }} - x86_64-unknown-linux-gnu + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@master + + - name: Install ${{ matrix.version }} + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.version }}-x86_64-unknown-linux-gnu + profile: minimal + override: true + + - name: Generate Cargo.lock + uses: actions-rs/cargo@v1 + with: + command: generate-lockfile + - name: Cache cargo registry + uses: actions/cache@v1 + with: + path: ~/.cargo/registry + key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo index + uses: actions/cache@v1 + with: + path: ~/.cargo/git + key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo build + uses: actions/cache@v1 + with: + path: target + key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-build-trimmed-${{ hashFiles('**/Cargo.lock') }} + + - name: check build + uses: actions-rs/cargo@v1 + with: + command: check + args: --all --bins --examples --tests + + - name: tests + uses: actions-rs/cargo@v1 + timeout-minutes: 40 + with: + command: test + args: --all --all-features --no-fail-fast -- --nocapture + + - name: tests (actix-http) + uses: actions-rs/cargo@v1 + timeout-minutes: 40 + with: + command: test + args: --package=actix-http --no-default-features --features=rustls -- --nocapture + + - name: tests (awc) + uses: actions-rs/cargo@v1 + timeout-minutes: 40 + with: + command: test + args: --package=awc --no-default-features --features=rustls -- --nocapture + + - name: Generate coverage file + if: matrix.version == 'stable' && (github.ref == 'master' || github.event_name == 'pull_request') + run: | + cargo install cargo-tarpaulin + cargo tarpaulin --out Xml + - name: Upload to Codecov + if: matrix.version == 'stable' && (github.ref == 'master' || github.event_name == 'pull_request') + uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: cobertura.xml + + - name: Clear the cargo caches + run: | + cargo install cargo-cache --no-default-features --features ci-autoclean + cargo-cache diff --git a/.github/workflows/upload-doc.yml b/.github/workflows/upload-doc.yml new file mode 100644 index 000000000..388ae3704 --- /dev/null +++ b/.github/workflows/upload-doc.yml @@ -0,0 +1,35 @@ +name: Upload documentation + +on: + push: + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + if: github.repository == 'actix/actix-web' + + steps: + - uses: actions/checkout@master + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable-x86_64-unknown-linux-gnu + profile: minimal + override: true + + - name: check build + uses: actions-rs/cargo@v1 + with: + command: doc + args: --no-deps --all-features + + - name: Tweak HTML + run: echo "" > target/doc/index.html + + - name: Upload documentation + run: | + git clone https://github.com/davisp/ghp-import.git + ./ghp-import/ghp_import.py -n -p -f -m "Documentation upload" -r https://${{ secrets.GITHUB_TOKEN }}@github.com/"${{ github.repository }}.git" target/doc \ No newline at end of file From cde3ae5f61e2805b6b41926beaeaf3bfb9d9e2fc Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sat, 8 Feb 2020 05:36:11 +0900 Subject: [PATCH 006/157] Remove Travis config --- .travis.yml | 61 ----------------------------------------------------- 1 file changed, 61 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f10f82a48..000000000 --- a/.travis.yml +++ /dev/null @@ -1,61 +0,0 @@ -language: rust -sudo: required -dist: trusty - -cache: - # cargo: true - apt: true - -matrix: - include: - - rust: stable - - rust: beta - - rust: nightly-2019-11-20 - allow_failures: - - rust: nightly-2019-11-20 - -env: - global: - # - RUSTFLAGS="-C link-dead-code" - - OPENSSL_VERSION=openssl-1.0.2 - -before_install: - - sudo add-apt-repository -y ppa:0k53d-karl-f830m/openssl - - sudo apt-get update -qq - - sudo apt-get install -y openssl libssl-dev libelf-dev libdw-dev cmake gcc binutils-dev libiberty-dev - -before_cache: | - if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-11-20" ]]; then - RUSTFLAGS="--cfg procmacro2_semver_exempt" cargo install --version 0.6.11 cargo-tarpaulin - fi - -# Add clippy -before_script: - - export PATH=$PATH:~/.cargo/bin - -script: - - cargo update - - cargo check --all --no-default-features - - | - if [[ "$TRAVIS_RUST_VERSION" == "stable" || "$TRAVIS_RUST_VERSION" == "beta" ]]; then - cargo test --all-features --all -- --nocapture - cd actix-http; cargo test --no-default-features --features="rustls" -- --nocapture; cd .. - cd awc; cargo test --no-default-features --features="rustls" -- --nocapture; cd .. - fi - -# Upload docs -after_success: - - | - if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PULL_REQUEST" = "false" && "$TRAVIS_BRANCH" == "master" && "$TRAVIS_RUST_VERSION" == "stable" ]]; then - cargo doc --no-deps --all-features && - echo "" > target/doc/index.html && - git clone https://github.com/davisp/ghp-import.git && - ./ghp-import/ghp_import.py -n -p -f -m "Documentation upload" -r https://"$GH_TOKEN"@github.com/"$TRAVIS_REPO_SLUG.git" target/doc && - echo "Uploaded documentation" - fi - - | - if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-11-20" ]]; then - taskset -c 0 cargo tarpaulin --out Xml --all --all-features - bash <(curl -s https://codecov.io/bash) - echo "Uploaded code coverage" - fi From a1835d651074dc600a7e874c52543f7f5588ee51 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 14 Feb 2020 07:31:29 +0900 Subject: [PATCH 007/157] Disable coverage for PRs --- .github/workflows/linux.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 875ce10b4..7cabb8020 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -73,12 +73,12 @@ jobs: args: --package=awc --no-default-features --features=rustls -- --nocapture - name: Generate coverage file - if: matrix.version == 'stable' && (github.ref == 'master' || github.event_name == 'pull_request') + if: matrix.version == 'stable' && github.ref == 'refs/heads/master' run: | cargo install cargo-tarpaulin cargo tarpaulin --out Xml - name: Upload to Codecov - if: matrix.version == 'stable' && (github.ref == 'master' || github.event_name == 'pull_request') + if: matrix.version == 'stable' && github.ref == 'refs/heads/master' uses: codecov/codecov-action@v1 with: token: ${{ secrets.CODECOV_TOKEN }} From 6ab7cfa2bef32eef7dca2c75223dc4199aca0aec Mon Sep 17 00:00:00 2001 From: Masayuki Nagamachi Date: Sun, 16 Feb 2020 04:18:31 +0900 Subject: [PATCH 008/157] Remove descriptions about undefined `uds` feature from docs (#1356) --- src/server.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/server.rs b/src/server.rs index 11cfbb6bc..97dd9f7f7 100644 --- a/src/server.rs +++ b/src/server.rs @@ -443,8 +443,6 @@ where #[cfg(unix)] /// Start listening for unix domain connections on existing listener. - /// - /// This method is available with `uds` feature. pub fn listen_uds( mut self, lst: std::os::unix::net::UnixListener, @@ -483,8 +481,6 @@ where #[cfg(unix)] /// Start listening for incoming unix domain connections. - /// - /// This method is available with `uds` feature. pub fn bind_uds(mut self, addr: A) -> io::Result where A: AsRef, From 82b2786d6bb521af4cff634031f8409f3e330ef0 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 6 Feb 2020 23:51:02 +0000 Subject: [PATCH 009/157] replace unsafe content length implementation --- actix-http/src/helpers.rs | 95 +++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 48 deletions(-) diff --git a/actix-http/src/helpers.rs b/actix-http/src/helpers.rs index 58ebff61f..9868e3f5b 100644 --- a/actix-http/src/helpers.rs +++ b/actix-http/src/helpers.rs @@ -1,4 +1,4 @@ -use std::{io, mem, ptr, slice}; +use std::{io, ptr, slice}; use bytes::{BufMut, BytesMut}; use http::Version; @@ -14,9 +14,7 @@ const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\ pub(crate) const STATUS_LINE_BUF_SIZE: usize = 13; pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesMut) { - let mut buf: [u8; STATUS_LINE_BUF_SIZE] = [ - b'H', b'T', b'T', b'P', b'/', b'1', b'.', b'1', b' ', b' ', b' ', b' ', b' ', - ]; + let mut buf: [u8; STATUS_LINE_BUF_SIZE] = *b"HTTP/1.1 "; match version { Version::HTTP_2 => buf[5] = b'2', Version::HTTP_10 => buf[7] = b'0', @@ -64,60 +62,55 @@ pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesM } } +const DIGITS_START: u8 = b'0'; + /// NOTE: bytes object has to contain enough space -pub fn write_content_length(mut n: usize, bytes: &mut BytesMut) { +pub fn write_content_length(n: usize, bytes: &mut BytesMut) { + bytes.put_slice(b"\r\ncontent-length: "); + if n < 10 { - let mut buf: [u8; 21] = [ - b'\r', b'\n', b'c', b'o', b'n', b't', b'e', b'n', b't', b'-', b'l', b'e', - b'n', b'g', b't', b'h', b':', b' ', b'0', b'\r', b'\n', - ]; - buf[18] = (n as u8) + b'0'; - bytes.put_slice(&buf); + bytes.put_u8(DIGITS_START + (n as u8)); } else if n < 100 { - let mut buf: [u8; 22] = [ - b'\r', b'\n', b'c', b'o', b'n', b't', b'e', b'n', b't', b'-', b'l', b'e', - b'n', b'g', b't', b'h', b':', b' ', b'0', b'0', b'\r', b'\n', - ]; - let d1 = n << 1; - unsafe { - ptr::copy_nonoverlapping( - DEC_DIGITS_LUT.as_ptr().add(d1), - buf.as_mut_ptr().offset(18), - 2, - ); - } - bytes.put_slice(&buf); + let n = n as u8; + + let d10 = n / 10; + let d1 = n % 10; + + bytes.put_u8(DIGITS_START + d10); + bytes.put_u8(DIGITS_START + d1); } else if n < 1000 { - let mut buf: [u8; 23] = [ - b'\r', b'\n', b'c', b'o', b'n', b't', b'e', b'n', b't', b'-', b'l', b'e', - b'n', b'g', b't', b'h', b':', b' ', b'0', b'0', b'0', b'\r', b'\n', - ]; - // decode 2 more chars, if > 2 chars - let d1 = (n % 100) << 1; - n /= 100; - unsafe { - ptr::copy_nonoverlapping( - DEC_DIGITS_LUT.as_ptr().add(d1), - buf.as_mut_ptr().offset(19), - 2, - ) - }; + let n = n as u16; - // decode last 1 - buf[18] = (n as u8) + b'0'; + let d100 = (n / 100) as u8; + let d10 = ((n / 10) % 10) as u8; + let d1 = (n % 10) as u8; - bytes.put_slice(&buf); + bytes.put_u8(DIGITS_START + d100); + bytes.put_u8(DIGITS_START + d10); + bytes.put_u8(DIGITS_START + d1); + } else if n < 10000 { + let n = n as u16; + + let d1000 = (n / 1000) as u8; + let d100 = ((n / 100) % 10) as u8; + let d10 = ((n / 10) % 10) as u8; + let d1 = (n % 10) as u8; + + bytes.put_u8(DIGITS_START + d1000); + bytes.put_u8(DIGITS_START + d100); + bytes.put_u8(DIGITS_START + d10); + bytes.put_u8(DIGITS_START + d1); } else { - bytes.put_slice(b"\r\ncontent-length: "); - convert_usize(n, bytes); + write_usize(n, bytes); } + + bytes.put_slice(b"\r\n"); } -pub(crate) fn convert_usize(mut n: usize, bytes: &mut BytesMut) { +pub(crate) fn write_usize(mut n: usize, bytes: &mut BytesMut) { let mut curr: isize = 39; - let mut buf: [u8; 41] = unsafe { mem::MaybeUninit::uninit().assume_init() }; - buf[39] = b'\r'; - buf[40] = b'\n'; + let mut buf = [0u8; 39]; + let buf_ptr = buf.as_mut_ptr(); let lut_ptr = DEC_DIGITS_LUT.as_ptr(); @@ -165,7 +158,7 @@ pub(crate) fn convert_usize(mut n: usize, bytes: &mut BytesMut) { unsafe { bytes.extend_from_slice(slice::from_raw_parts( buf_ptr.offset(curr), - 41 - curr as usize, + 39 - curr as usize, )); } } @@ -231,5 +224,11 @@ mod tests { bytes.reserve(50); write_content_length(5909, &mut bytes); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 5909\r\n"[..]); + bytes.reserve(50); + write_content_length(10001, &mut bytes); + assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 10001\r\n"[..]); + bytes.reserve(50); + write_content_length(59094, &mut bytes); + assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 59094\r\n"[..]); } } From 31a3515e904ac53b5df99fb6aa133923a937cb84 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 7 Feb 2020 00:42:16 +0000 Subject: [PATCH 010/157] add safe vs unsafe benchmarks --- actix-http/Cargo.toml | 5 + actix-http/benches/content-length.rs | 279 +++++++++++++++++++++++++++ 2 files changed, 284 insertions(+) create mode 100644 actix-http/benches/content-length.rs diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index cd813e49f..d27bca7e7 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -93,8 +93,13 @@ actix-server = "1.0.0" actix-connect = { version = "1.0.0", features=["openssl"] } actix-http-test = { version = "1.0.0", features=["openssl"] } actix-tls = { version = "1.0.0", features=["openssl"] } +criterion = "0.3" futures = "0.3.1" env_logger = "0.6" serde_derive = "1.0" open-ssl = { version="0.10", package = "openssl" } rust-tls = { version="0.16", package = "rustls" } + +[[bench]] +name = "content-length" +harness = false diff --git a/actix-http/benches/content-length.rs b/actix-http/benches/content-length.rs new file mode 100644 index 000000000..a80fca2e7 --- /dev/null +++ b/actix-http/benches/content-length.rs @@ -0,0 +1,279 @@ +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; + +use bytes::BytesMut; + +// benchmark sending all requests at the same time +fn bench_write_content_length(c: &mut Criterion) { + let mut group = c.benchmark_group("write_content_length"); + + let sizes = [ + 0, 1, 11, 83, 101, 653, 1001, 6323, 10001, 56329, 100001, 123456, 98724245, + 4294967202, + ]; + + for i in sizes.iter() { + group.bench_with_input(BenchmarkId::new("Original (unsafe)", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _original::write_content_length(i, &mut b) + }) + }); + + group.bench_with_input(BenchmarkId::new("New (safe)", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _new::write_content_length(i, &mut b) + }) + }); + } + + group.finish(); +} + +criterion_group!(benches, bench_write_content_length); +criterion_main!(benches); + +mod _new { + use std::{ptr, slice}; + + use bytes::{BufMut, BytesMut}; + + const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\ + 2021222324252627282930313233343536373839\ + 4041424344454647484950515253545556575859\ + 6061626364656667686970717273747576777879\ + 8081828384858687888990919293949596979899"; + + const DIGITS_START: u8 = b'0'; + + /// NOTE: bytes object has to contain enough space + pub fn write_content_length(n: usize, bytes: &mut BytesMut) { + if n == 0 { + bytes.put_slice(b"\r\ncontent-length: 0\r\n"); + return; + } + + bytes.put_slice(b"\r\ncontent-length: "); + + if n < 10 { + bytes.put_u8(DIGITS_START + (n as u8)); + } else if n < 100 { + let n = n as u8; + + let d10 = n / 10; + let d1 = n % 10; + + bytes.put_u8(DIGITS_START + d10); + bytes.put_u8(DIGITS_START + d1); + } else if n < 1000 { + let n = n as u16; + + let d100 = (n / 100) as u8; + let d10 = ((n / 10) % 10) as u8; + let d1 = (n % 10) as u8; + + bytes.put_u8(DIGITS_START + d100); + bytes.put_u8(DIGITS_START + d10); + bytes.put_u8(DIGITS_START + d1); + } else if n < 10000 { + let n = n as u16; + + let d1000 = (n / 1000) as u8; + let d100 = ((n / 100) % 10) as u8; + let d10 = ((n / 10) % 10) as u8; + let d1 = (n % 10) as u8; + + bytes.put_u8(DIGITS_START + d1000); + bytes.put_u8(DIGITS_START + d100); + bytes.put_u8(DIGITS_START + d10); + bytes.put_u8(DIGITS_START + d1); + } else { + write_usize(n, bytes); + } + + bytes.put_slice(b"\r\n"); + } + + pub(crate) fn write_usize(mut n: usize, bytes: &mut BytesMut) { + let mut curr: isize = 39; + let mut buf = [0u8; 39]; + + let buf_ptr = buf.as_mut_ptr(); + let lut_ptr = DEC_DIGITS_LUT.as_ptr(); + + // eagerly decode 4 characters at a time + while n >= 10_000 { + let rem = (n % 10_000) as isize; + n /= 10_000; + + let d1 = (rem / 100) << 1; + let d2 = (rem % 100) << 1; + curr -= 4; + unsafe { + ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); + ptr::copy_nonoverlapping( + lut_ptr.offset(d2), + buf_ptr.offset(curr + 2), + 2, + ); + } + } + + // if we reach here numbers are <= 9999, so at most 4 chars long + let mut n = n as isize; // possibly reduce 64bit math + + // decode 2 more chars, if > 2 chars + if n >= 100 { + let d1 = (n % 100) << 1; + n /= 100; + curr -= 2; + unsafe { + ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); + } + } + + // decode last 1 or 2 chars + if n < 10 { + curr -= 1; + unsafe { + *buf_ptr.offset(curr) = (n as u8) + b'0'; + } + } else { + let d1 = n << 1; + curr -= 2; + unsafe { + ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); + } + } + + unsafe { + bytes.extend_from_slice(slice::from_raw_parts( + buf_ptr.offset(curr), + 39 - curr as usize, + )); + } + } +} + +mod _original { + use std::{mem, ptr, slice}; + + use bytes::{BufMut, BytesMut}; + + const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\ + 2021222324252627282930313233343536373839\ + 4041424344454647484950515253545556575859\ + 6061626364656667686970717273747576777879\ + 8081828384858687888990919293949596979899"; + + /// NOTE: bytes object has to contain enough space + pub fn write_content_length(mut n: usize, bytes: &mut BytesMut) { + if n < 10 { + let mut buf: [u8; 21] = [ + b'\r', b'\n', b'c', b'o', b'n', b't', b'e', b'n', b't', b'-', b'l', + b'e', b'n', b'g', b't', b'h', b':', b' ', b'0', b'\r', b'\n', + ]; + buf[18] = (n as u8) + b'0'; + bytes.put_slice(&buf); + } else if n < 100 { + let mut buf: [u8; 22] = [ + b'\r', b'\n', b'c', b'o', b'n', b't', b'e', b'n', b't', b'-', b'l', + b'e', b'n', b'g', b't', b'h', b':', b' ', b'0', b'0', b'\r', b'\n', + ]; + let d1 = n << 1; + unsafe { + ptr::copy_nonoverlapping( + DEC_DIGITS_LUT.as_ptr().add(d1), + buf.as_mut_ptr().offset(18), + 2, + ); + } + bytes.put_slice(&buf); + } else if n < 1000 { + let mut buf: [u8; 23] = [ + b'\r', b'\n', b'c', b'o', b'n', b't', b'e', b'n', b't', b'-', b'l', + b'e', b'n', b'g', b't', b'h', b':', b' ', b'0', b'0', b'0', b'\r', + b'\n', + ]; + // decode 2 more chars, if > 2 chars + let d1 = (n % 100) << 1; + n /= 100; + unsafe { + ptr::copy_nonoverlapping( + DEC_DIGITS_LUT.as_ptr().add(d1), + buf.as_mut_ptr().offset(19), + 2, + ) + }; + + // decode last 1 + buf[18] = (n as u8) + b'0'; + + bytes.put_slice(&buf); + } else { + bytes.put_slice(b"\r\ncontent-length: "); + convert_usize(n, bytes); + } + } + + pub(crate) fn convert_usize(mut n: usize, bytes: &mut BytesMut) { + let mut curr: isize = 39; + let mut buf: [u8; 41] = unsafe { mem::MaybeUninit::uninit().assume_init() }; + buf[39] = b'\r'; + buf[40] = b'\n'; + let buf_ptr = buf.as_mut_ptr(); + let lut_ptr = DEC_DIGITS_LUT.as_ptr(); + + // eagerly decode 4 characters at a time + while n >= 10_000 { + let rem = (n % 10_000) as isize; + n /= 10_000; + + let d1 = (rem / 100) << 1; + let d2 = (rem % 100) << 1; + curr -= 4; + unsafe { + ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); + ptr::copy_nonoverlapping( + lut_ptr.offset(d2), + buf_ptr.offset(curr + 2), + 2, + ); + } + } + + // if we reach here numbers are <= 9999, so at most 4 chars long + let mut n = n as isize; // possibly reduce 64bit math + + // decode 2 more chars, if > 2 chars + if n >= 100 { + let d1 = (n % 100) << 1; + n /= 100; + curr -= 2; + unsafe { + ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); + } + } + + // decode last 1 or 2 chars + if n < 10 { + curr -= 1; + unsafe { + *buf_ptr.offset(curr) = (n as u8) + b'0'; + } + } else { + let d1 = n << 1; + curr -= 2; + unsafe { + ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); + } + } + + unsafe { + bytes.extend_from_slice(slice::from_raw_parts( + buf_ptr.offset(curr), + 41 - curr as usize, + )); + } + } +} From f266b44cb0a08c5f7586ea79cce7fcc11c2f9299 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 16 Feb 2020 15:20:25 +0000 Subject: [PATCH 011/157] replace unsafe blocks in write_usize helper --- actix-http/benches/content-length.rs | 76 ++++++------------------ actix-http/src/helpers.rs | 87 ++++++++++++---------------- 2 files changed, 55 insertions(+), 108 deletions(-) diff --git a/actix-http/benches/content-length.rs b/actix-http/benches/content-length.rs index a80fca2e7..150f48a2c 100644 --- a/actix-http/benches/content-length.rs +++ b/actix-http/benches/content-length.rs @@ -34,16 +34,8 @@ criterion_group!(benches, bench_write_content_length); criterion_main!(benches); mod _new { - use std::{ptr, slice}; - use bytes::{BufMut, BytesMut}; - const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\ - 2021222324252627282930313233343536373839\ - 4041424344454647484950515253545556575859\ - 6061626364656667686970717273747576777879\ - 8081828384858687888990919293949596979899"; - const DIGITS_START: u8 = b'0'; /// NOTE: bytes object has to contain enough space @@ -94,63 +86,29 @@ mod _new { bytes.put_slice(b"\r\n"); } - pub(crate) fn write_usize(mut n: usize, bytes: &mut BytesMut) { - let mut curr: isize = 39; - let mut buf = [0u8; 39]; + fn write_usize(n: usize, bytes: &mut BytesMut) { + let mut n = n; - let buf_ptr = buf.as_mut_ptr(); - let lut_ptr = DEC_DIGITS_LUT.as_ptr(); + // 20 chars is max length of a usize (2^64) + // digits will be added to the buffer from lsd to msd + let mut buf = BytesMut::with_capacity(20); - // eagerly decode 4 characters at a time - while n >= 10_000 { - let rem = (n % 10_000) as isize; - n /= 10_000; + while n > 9 { + // "pop" the least-significant digit + let lsd = (n % 10) as u8; - let d1 = (rem / 100) << 1; - let d2 = (rem % 100) << 1; - curr -= 4; - unsafe { - ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); - ptr::copy_nonoverlapping( - lut_ptr.offset(d2), - buf_ptr.offset(curr + 2), - 2, - ); - } + // remove the lsd from n + n = n / 10; + + buf.put_u8(DIGITS_START + lsd); } - // if we reach here numbers are <= 9999, so at most 4 chars long - let mut n = n as isize; // possibly reduce 64bit math + // put msd to result buffer + bytes.put_u8(DIGITS_START + (n as u8)); - // decode 2 more chars, if > 2 chars - if n >= 100 { - let d1 = (n % 100) << 1; - n /= 100; - curr -= 2; - unsafe { - ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); - } - } - - // decode last 1 or 2 chars - if n < 10 { - curr -= 1; - unsafe { - *buf_ptr.offset(curr) = (n as u8) + b'0'; - } - } else { - let d1 = n << 1; - curr -= 2; - unsafe { - ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); - } - } - - unsafe { - bytes.extend_from_slice(slice::from_raw_parts( - buf_ptr.offset(curr), - 39 - curr as usize, - )); + // put, in reverse (msd to lsd), remaining digits to buffer + for i in (0..buf.len()).rev() { + bytes.put_u8(buf[i]); } } } diff --git a/actix-http/src/helpers.rs b/actix-http/src/helpers.rs index 9868e3f5b..cba21ed0c 100644 --- a/actix-http/src/helpers.rs +++ b/actix-http/src/helpers.rs @@ -1,4 +1,4 @@ -use std::{io, ptr, slice}; +use std::{io, ptr}; use bytes::{BufMut, BytesMut}; use http::Version; @@ -107,59 +107,29 @@ pub fn write_content_length(n: usize, bytes: &mut BytesMut) { bytes.put_slice(b"\r\n"); } -pub(crate) fn write_usize(mut n: usize, bytes: &mut BytesMut) { - let mut curr: isize = 39; - let mut buf = [0u8; 39]; +pub(crate) fn write_usize(n: usize, bytes: &mut BytesMut) { + let mut n = n; + + // 20 chars is max length of a usize (2^64) + // digits will be added to the buffer from lsd to msd + let mut buf = BytesMut::with_capacity(20); - let buf_ptr = buf.as_mut_ptr(); - let lut_ptr = DEC_DIGITS_LUT.as_ptr(); + while n > 9 { + // "pop" the least-significant digit + let lsd = (n % 10) as u8; - // eagerly decode 4 characters at a time - while n >= 10_000 { - let rem = (n % 10_000) as isize; - n /= 10_000; + // remove the lsd from n + n = n / 10; - let d1 = (rem / 100) << 1; - let d2 = (rem % 100) << 1; - curr -= 4; - unsafe { - ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); - ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2); - } + buf.put_u8(DIGITS_START + lsd); } - // if we reach here numbers are <= 9999, so at most 4 chars long - let mut n = n as isize; // possibly reduce 64bit math - - // decode 2 more chars, if > 2 chars - if n >= 100 { - let d1 = (n % 100) << 1; - n /= 100; - curr -= 2; - unsafe { - ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); - } - } - - // decode last 1 or 2 chars - if n < 10 { - curr -= 1; - unsafe { - *buf_ptr.offset(curr) = (n as u8) + b'0'; - } - } else { - let d1 = n << 1; - curr -= 2; - unsafe { - ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); - } - } - - unsafe { - bytes.extend_from_slice(slice::from_raw_parts( - buf_ptr.offset(curr), - 39 - curr as usize, - )); + // put msd to result buffer + bytes.put_u8(DIGITS_START + (n as u8)); + + // put, in reverse (msd to lsd), remaining digits to buffer + for i in (0..buf.len()).rev() { + bytes.put_u8(buf[i]); } } @@ -230,5 +200,24 @@ mod tests { bytes.reserve(50); write_content_length(59094, &mut bytes); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 59094\r\n"[..]); + + bytes.reserve(50); + write_content_length(590947, &mut bytes); + assert_eq!( + bytes.split().freeze(), + b"\r\ncontent-length: 590947\r\n"[..] + ); + bytes.reserve(50); + write_content_length(5909471, &mut bytes); + assert_eq!( + bytes.split().freeze(), + b"\r\ncontent-length: 5909471\r\n"[..] + ); + bytes.reserve(50); + write_content_length(59094718, &mut bytes); + assert_eq!( + bytes.split().freeze(), + b"\r\ncontent-length: 59094718\r\n"[..] + ); } } From 809930d36e3d414662205ad02eb514083843463b Mon Sep 17 00:00:00 2001 From: Elliot Jackson Date: Wed, 19 Feb 2020 21:13:10 +0100 Subject: [PATCH 012/157] Add dependencies to docs example (#1343) * Add dependencies to docs example * Change codeblock type to toml * Clarify the need for actix-rt Co-authored-by: Yuki Okushi --- src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index d51005cfe..a898bd704 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,12 @@ //! Actix web is a small, pragmatic, and extremely fast web framework //! for Rust. //! +//! ## Example +//! +//! The `#[actix_rt::main]` macro in the example below is provided by the Actix runtime +//! crate, [`actix-rt`](https://crates.io/crates/actix-rt). You will need to include +//! `actix-rt` in your dependencies for it to run. +//! //! ```rust,no_run //! use actix_web::{web, App, Responder, HttpServer}; //! From e6811e8818b015c31f88d22bd037d3377deac1c3 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Wed, 19 Feb 2020 21:03:53 -0500 Subject: [PATCH 013/157] Use #[pin_project] with `ConnectorPoolSupport` This removes a use of `Pin::get_unchecked_mut` --- actix-http/src/client/pool.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index 8c94423ac..139cf9f66 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -17,6 +17,7 @@ use h2::client::{handshake, Connection, SendRequest}; use http::uri::Authority; use indexmap::IndexSet; use slab::Slab; +use pin_project::pin_project; use super::connection::{ConnectionType, IoConnection}; use super::error::ConnectError; @@ -422,6 +423,7 @@ where } } +#[pin_project] struct ConnectorPoolSupport where Io: AsyncRead + AsyncWrite + Unpin + 'static, @@ -439,7 +441,7 @@ where type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = unsafe { self.get_unchecked_mut() }; + let this = self.project(); let mut inner = this.inner.as_ref().borrow_mut(); inner.waker.register(cx.waker()); From 245f96868aaf6b86bbefc36e79ef39fd70878730 Mon Sep 17 00:00:00 2001 From: Daniel YU Date: Fri, 21 Feb 2020 12:31:51 +0800 Subject: [PATCH 014/157] impl downcast_ref for MessageBody (#1287) Co-authored-by: Yuki Okushi --- actix-http/src/body.rs | 23 +++++++++- actix-http/src/error.rs | 44 +------------------ actix-http/src/lib.rs | 3 ++ actix-http/src/macros.rs | 95 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 43 deletions(-) create mode 100644 actix-http/src/macros.rs diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index e2bcce359..1a2428e14 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -37,8 +37,12 @@ pub trait MessageBody { fn size(&self) -> BodySize; fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>>; + + downcast_get_type_id!(); } +downcast!(MessageBody); + impl MessageBody for () { fn size(&self) -> BodySize { BodySize::Empty @@ -416,7 +420,10 @@ where S: Stream>, { pub fn new(size: u64, stream: S) -> Self { - SizedStream { size, stream: Box::pin(stream) } + SizedStream { + size, + stream: Box::pin(stream), + } } } @@ -643,4 +650,18 @@ mod tests { ); } } + + #[actix_rt::test] + async fn test_body_casting() { + let mut body = String::from("hello cast"); + let resp_body: &mut dyn MessageBody = &mut body; + let body = resp_body.downcast_ref::().unwrap(); + assert_eq!(body, "hello cast"); + let body = &mut resp_body.downcast_mut::().unwrap(); + body.push_str("!"); + let body = resp_body.downcast_ref::().unwrap(); + assert_eq!(body, "hello cast!"); + let not_body = resp_body.downcast_ref::<()>(); + assert!(not_body.is_none()); + } } diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index b6637075c..c19aef2aa 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -1,5 +1,4 @@ //! Error and Result module -use std::any::TypeId; use std::cell::RefCell; use std::io::Write; use std::str::Utf8Error; @@ -60,12 +59,6 @@ impl Error { } } -/// A struct with a private constructor, for use with -/// `__private_get_type_id__`. Its single field is private, -/// ensuring that it can only be constructed from this module -#[doc(hidden)] -pub struct PrivateHelper(()); - /// Error that can be converted to `Response` pub trait ResponseError: fmt::Debug + fmt::Display { /// Response's status code @@ -89,43 +82,10 @@ pub trait ResponseError: fmt::Debug + fmt::Display { resp.set_body(Body::from(buf)) } - /// A helper method to get the type ID of the type - /// this trait is implemented on. - /// This method is unsafe to *implement*, since `downcast_ref` relies - /// on the returned `TypeId` to perform a cast. - /// - /// Unfortunately, Rust has no notion of a trait method that is - /// unsafe to implement (marking it as `unsafe` makes it unsafe - /// to *call*). As a workaround, we require this method - /// to return a private type along with the `TypeId`. This - /// private type (`PrivateHelper`) has a private constructor, - /// making it impossible for safe code to construct outside of - /// this module. This ensures that safe code cannot violate - /// type-safety by implementing this method. - #[doc(hidden)] - fn __private_get_type_id__(&self) -> (TypeId, PrivateHelper) - where - Self: 'static, - { - (TypeId::of::(), PrivateHelper(())) - } + downcast_get_type_id!(); } -impl dyn ResponseError + 'static { - /// Downcasts a response error to a specific type. - pub fn downcast_ref(&self) -> Option<&T> { - if self.__private_get_type_id__().0 == TypeId::of::() { - // Safety: external crates cannot override the default - // implementation of `__private_get_type_id__`, since - // it requires returning a private type. We can therefore - // rely on the returned `TypeId`, which ensures that this - // case is correct. - unsafe { Some(&*(self as *const dyn ResponseError as *const T)) } - } else { - None - } - } -} +downcast!(ResponseError); impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index a5ae4b447..1cda9437f 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -10,6 +10,9 @@ #[macro_use] extern crate log; +#[macro_use] +mod macros; + pub mod body; mod builder; pub mod client; diff --git a/actix-http/src/macros.rs b/actix-http/src/macros.rs new file mode 100644 index 000000000..0aaf1abec --- /dev/null +++ b/actix-http/src/macros.rs @@ -0,0 +1,95 @@ +#[macro_export] +macro_rules! downcast_get_type_id { + () => { + /// A helper method to get the type ID of the type + /// this trait is implemented on. + /// This method is unsafe to *implement*, since `downcast_ref` relies + /// on the returned `TypeId` to perform a cast. + /// + /// Unfortunately, Rust has no notion of a trait method that is + /// unsafe to implement (marking it as `unsafe` makes it unsafe + /// to *call*). As a workaround, we require this method + /// to return a private type along with the `TypeId`. This + /// private type (`PrivateHelper`) has a private constructor, + /// making it impossible for safe code to construct outside of + /// this module. This ensures that safe code cannot violate + /// type-safety by implementing this method. + #[doc(hidden)] + fn __private_get_type_id__(&self) -> (std::any::TypeId, PrivateHelper) + where + Self: 'static, + { + (std::any::TypeId::of::(), PrivateHelper(())) + } + } +} + +//Generate implementation for dyn $name +#[macro_export] +macro_rules! downcast { + ($name:ident) => { + /// A struct with a private constructor, for use with + /// `__private_get_type_id__`. Its single field is private, + /// ensuring that it can only be constructed from this module + #[doc(hidden)] + pub struct PrivateHelper(()); + + impl dyn $name + 'static { + /// Downcasts generic body to a specific type. + pub fn downcast_ref(&self) -> Option<&T> { + if self.__private_get_type_id__().0 == std::any::TypeId::of::() { + // Safety: external crates cannot override the default + // implementation of `__private_get_type_id__`, since + // it requires returning a private type. We can therefore + // rely on the returned `TypeId`, which ensures that this + // case is correct. + unsafe { Some(&*(self as *const dyn $name as *const T)) } + } else { + None + } + } + /// Downcasts a generic body to a mutable specific type. + pub fn downcast_mut(&mut self) -> Option<&mut T> { + if self.__private_get_type_id__().0 == std::any::TypeId::of::() { + // Safety: external crates cannot override the default + // implementation of `__private_get_type_id__`, since + // it requires returning a private type. We can therefore + // rely on the returned `TypeId`, which ensures that this + // case is correct. + unsafe { + Some(&mut *(self as *const dyn $name as *const T as *mut T)) + } + } else { + None + } + } + } + }; +} + +#[cfg(test)] +mod tests { + + trait MB { + downcast_get_type_id!(); + } + + downcast!(MB); + + impl MB for String {} + impl MB for () {} + + #[actix_rt::test] + async fn test_any_casting() { + let mut body = String::from("hello cast"); + let resp_body: &mut dyn MB = &mut body; + let body = resp_body.downcast_ref::().unwrap(); + assert_eq!(body, "hello cast"); + let body = &mut resp_body.downcast_mut::().unwrap(); + body.push_str("!"); + let body = resp_body.downcast_ref::().unwrap(); + assert_eq!(body, "hello cast!"); + let not_body = resp_body.downcast_ref::<()>(); + assert!(not_body.is_none()); + } +} From 060c392c676c55accfc569ea8c05e570efe6df8e Mon Sep 17 00:00:00 2001 From: Matt Gathu Date: Sat, 22 Feb 2020 10:32:12 +0100 Subject: [PATCH 015/157] Add missing_docs attribute to generated structs --- actix-web-codegen/src/route.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-web-codegen/src/route.rs b/actix-web-codegen/src/route.rs index d48198484..60b829595 100644 --- a/actix-web-codegen/src/route.rs +++ b/actix-web-codegen/src/route.rs @@ -191,7 +191,7 @@ impl Route { let extra_guards = &self.args.guards; let resource_type = &self.resource_type; let stream = quote! { - #[allow(non_camel_case_types)] + #[allow(non_camel_case_types, missing_docs)] pub struct #name; impl actix_web::dev::HttpServiceFactory for #name { From 036ffd43f964b24c1aa975efb9de48073f5b80f5 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 23 Feb 2020 06:40:02 +0900 Subject: [PATCH 016/157] Prepare for new release --- actix-web-codegen/CHANGES.md | 8 ++++++-- actix-web-codegen/Cargo.toml | 8 ++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/actix-web-codegen/CHANGES.md b/actix-web-codegen/CHANGES.md index 95696abd3..13ed5d43d 100644 --- a/actix-web-codegen/CHANGES.md +++ b/actix-web-codegen/CHANGES.md @@ -1,8 +1,12 @@ # Changes -## [0.2.NEXT] - 2020-xx-xx +## [0.2.1] - 2020-02-23 -* Allow the handler function to be named as `config` #1290 +* Add `#[allow(missing_docs)]` attribute to generated structs [#1368] +* Allow the handler function to be named as `config` [#1290] + +[#1368]: https://github.com/actix/actix-web/issues/1368 +[#1290]: https://github.com/actix/actix-web/issues/1290 ## [0.2.0] - 2019-12-13 diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 3fe561deb..0b926b807 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web-codegen" -version = "0.2.0" +version = "0.2.1" description = "Actix web proc macros" readme = "README.md" authors = ["Nikolay Kim "] @@ -17,6 +17,6 @@ syn = { version = "^1", features = ["full", "parsing"] } proc-macro2 = "^1" [dev-dependencies] -actix-rt = { version = "1.0.0" } -actix-web = { version = "2.0.0-rc" } -futures = { version = "0.3.1" } +actix-rt = "1.0.0" +actix-web = "2.0.0" +futures = "0.3.1" From 1b77963aacb38df135cef31b2b41838d71c3b188 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 23 Feb 2020 07:08:22 +0900 Subject: [PATCH 017/157] actix-web: update `time` to 0.2.7 --- CHANGES.md | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b42635b86..5da4e19ec 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,7 +9,7 @@ * Skip empty chunks when returning response from a `Stream` #1308 -* Update the `time` dependency to 0.2.5 +* Update the `time` dependency to 0.2.7 ## [2.0.0] - 2019-12-25 diff --git a/Cargo.toml b/Cargo.toml index a6783a6db..d06e47ef8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,7 +87,7 @@ regex = "1.3" serde = { version = "1.0", features=["derive"] } serde_json = "1.0" serde_urlencoded = "0.6.1" -time = { version = "0.2.5", default-features = false, features = ["std"] } +time = { version = "0.2.7", default-features = false, features = ["std"] } url = "2.1" open-ssl = { version="0.10", package = "openssl", optional = true } rust-tls = { version = "0.16.0", package = "rustls", optional = true } From f9f9fb4c840c9c6c31837d18e4984a87d662c81c Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 23 Feb 2020 07:08:50 +0900 Subject: [PATCH 018/157] actix-http-test: update `time` to 0.2.7 --- test-server/CHANGES.md | 2 +- test-server/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test-server/CHANGES.md b/test-server/CHANGES.md index 617b8092f..f8b29f39d 100644 --- a/test-server/CHANGES.md +++ b/test-server/CHANGES.md @@ -2,7 +2,7 @@ ## [Unreleased] - 2020-xx-xx -* Update the `time` dependency to 0.2.5 +* Update the `time` dependency to 0.2.7 ## [1.0.0] - 2019-12-13 diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index b22414e29..774c8f0b2 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -51,7 +51,7 @@ serde_json = "1.0" sha1 = "0.6" slab = "0.4" serde_urlencoded = "0.6.1" -time = { version = "0.2.5", default-features = false, features = ["std"] } +time = { version = "0.2.7", default-features = false, features = ["std"] } open-ssl = { version="0.10", package="openssl", optional = true } [dev-dependencies] From c8ccc69b93e016e42c47978fb8cf64b87119db3e Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 23 Feb 2020 07:09:00 +0900 Subject: [PATCH 019/157] actix-http: update `time` to 0.2.7 --- actix-http/CHANGES.md | 2 +- actix-http/Cargo.toml | 2 +- actix-http/src/cookie/mod.rs | 4 ++-- actix-http/src/cookie/parse.rs | 10 +++++----- actix-http/src/header/shared/httpdate.rs | 8 ++++---- actix-http/src/time_parser.rs | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 511ef4f1c..4e8d7fd42 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -4,7 +4,7 @@ ### Changed -* Update the `time` dependency to 0.2.5 +* Update the `time` dependency to 0.2.7 ### Fixed diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index cd813e49f..10aa79d18 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -76,7 +76,7 @@ serde_json = "1.0" sha-1 = "0.8" slab = "0.4" serde_urlencoded = "0.6.1" -time = { version = "0.2.5", default-features = false, features = ["std"] } +time = { version = "0.2.7", default-features = false, features = ["std"] } # for secure cookie ring = { version = "0.16.9", optional = true } diff --git a/actix-http/src/cookie/mod.rs b/actix-http/src/cookie/mod.rs index 09120e19f..8dccd0b6d 100644 --- a/actix-http/src/cookie/mod.rs +++ b/actix-http/src/cookie/mod.rs @@ -990,7 +990,7 @@ impl<'a, 'b> PartialEq> for Cookie<'a> { #[cfg(test)] mod tests { use super::{Cookie, SameSite}; - use time::{offset, PrimitiveDateTime}; + use time::PrimitiveDateTime; #[test] fn format() { @@ -1015,7 +1015,7 @@ mod tests { assert_eq!(&cookie.to_string(), "foo=bar; Domain=www.rust-lang.org"); let time_str = "Wed, 21 Oct 2015 07:28:00 GMT"; - let expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%M:%S").unwrap().using_offset(offset!(UTC)); + let expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%M:%S").unwrap().assume_utc(); let cookie = Cookie::build("foo", "bar").expires(expires).finish(); assert_eq!( &cookie.to_string(), diff --git a/actix-http/src/cookie/parse.rs b/actix-http/src/cookie/parse.rs index 28eb4f8b6..537069de3 100644 --- a/actix-http/src/cookie/parse.rs +++ b/actix-http/src/cookie/parse.rs @@ -6,7 +6,7 @@ use std::fmt; use std::str::Utf8Error; use percent_encoding::percent_decode; -use time::{Duration, offset}; +use time::Duration; use super::{Cookie, CookieStr, SameSite}; @@ -188,7 +188,7 @@ fn parse_inner<'c>(s: &str, decode: bool) -> Result, ParseError> { .or_else(|| time::parse(v, "%a, %d-%b-%Y %H:%M:%S").ok()); if let Some(time) = tm { - cookie.expires = Some(time.using_offset(offset!(UTC))) + cookie.expires = Some(time.assume_utc()) } } _ => { @@ -216,7 +216,7 @@ where #[cfg(test)] mod tests { use super::{Cookie, SameSite}; - use time::{offset, Duration, PrimitiveDateTime}; + use time::{Duration, PrimitiveDateTime}; macro_rules! assert_eq_parse { ($string:expr, $expected:expr) => { @@ -376,7 +376,7 @@ mod tests { ); let time_str = "Wed, 21 Oct 2015 07:28:00 GMT"; - let expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%M:%S").unwrap().using_offset(offset!(UTC)); + let expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%M:%S").unwrap().assume_utc(); expected.set_expires(expires); assert_eq_parse!( " foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \ @@ -385,7 +385,7 @@ mod tests { ); unexpected.set_domain("foo.com"); - let bad_expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%S:%M").unwrap().using_offset(offset!(UTC)); + let bad_expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%S:%M").unwrap().assume_utc(); expected.set_expires(bad_expires); assert_ne_parse!( " foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \ diff --git a/actix-http/src/header/shared/httpdate.rs b/actix-http/src/header/shared/httpdate.rs index 1b52f0de4..5227118fa 100644 --- a/actix-http/src/header/shared/httpdate.rs +++ b/actix-http/src/header/shared/httpdate.rs @@ -20,7 +20,7 @@ impl FromStr for HttpDate { fn from_str(s: &str) -> Result { match time_parser::parse_http_date(s) { - Some(t) => Ok(HttpDate(t.using_offset(offset!(UTC)))), + Some(t) => Ok(HttpDate(t.assume_utc())), None => Err(ParseError::Header) } } @@ -40,7 +40,7 @@ impl From for HttpDate { impl From for HttpDate { fn from(sys: SystemTime) -> HttpDate { - HttpDate(PrimitiveDateTime::from(sys).using_offset(offset!(UTC))) + HttpDate(PrimitiveDateTime::from(sys).assume_utc()) } } @@ -66,14 +66,14 @@ impl From for SystemTime { #[cfg(test)] mod tests { use super::HttpDate; - use time::{PrimitiveDateTime, date, time, offset}; + use time::{PrimitiveDateTime, date, time}; #[test] fn test_date() { let nov_07 = HttpDate(PrimitiveDateTime::new( date!(1994-11-07), time!(8:48:37) - ).using_offset(offset!(UTC))); + ).assume_utc()); assert_eq!( "Sun, 07 Nov 1994 08:48:37 GMT".parse::().unwrap(), diff --git a/actix-http/src/time_parser.rs b/actix-http/src/time_parser.rs index f6623d24e..34fac139e 100644 --- a/actix-http/src/time_parser.rs +++ b/actix-http/src/time_parser.rs @@ -1,4 +1,4 @@ -use time::{PrimitiveDateTime, Date}; +use time::{OffsetDateTime, PrimitiveDateTime, Date}; /// Attempt to parse a `time` string as one of either RFC 1123, RFC 850, or asctime. pub fn parse_http_date(time: &str) -> Option { @@ -19,7 +19,7 @@ fn try_parse_rfc_850(time: &str) -> Option { // If the `time` string contains a two-digit year, then as per RFC 2616 ยง 19.3, // we consider the year as part of this century if it's within the next 50 years, // otherwise we consider as part of the previous century. - let now = PrimitiveDateTime::now(); + let now = OffsetDateTime::now(); let century_start_year = (now.year() / 100) * 100; let mut expanded_year = century_start_year + dt.year(); From 8ec8ccf4fb38e5ceb0c6853f8ad1a0b7110b0fa5 Mon Sep 17 00:00:00 2001 From: Matt Gathu Date: Sat, 22 Feb 2020 16:19:29 +0100 Subject: [PATCH 020/157] Create helper function for HTTP Trace Method Create *route* with `TRACE` method guard. --- src/web.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/web.rs b/src/web.rs index 962c1157b..f47cf865e 100644 --- a/src/web.rs +++ b/src/web.rs @@ -193,6 +193,24 @@ pub fn head() -> Route { method(Method::HEAD) } +/// Create *route* with `TRACE` method guard. +/// +/// ```rust +/// use actix_web::{web, App, HttpResponse}; +/// +/// let app = App::new().service( +/// web::resource("/{project_id}") +/// .route(web::trace().to(|| HttpResponse::Ok())) +/// ); +/// ``` +/// +/// In the above example, one `HEAD` route gets added: +/// * /{project_id} +/// +pub fn trace() -> Route { + method(Method::TRACE) +} + /// Create *route* and add method guard. /// /// ```rust From d143c44130213e43fecc82068c28f33303d9ed98 Mon Sep 17 00:00:00 2001 From: Matt Gathu Date: Sun, 23 Feb 2020 09:33:28 +0100 Subject: [PATCH 021/157] Update the ChangeLog --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index b42635b86..f9cfdf5fa 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,10 @@ ## [2.0.NEXT] - 2020-01-xx +### Added + +* Add helper function for creating routes with `TRACE` method guard `web::trace()` + ### Changed * Use `sha-1` crate instead of unmaintained `sha1` crate From 94da08f506c822064e37d7848f0b0f31b7df9a0e Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 24 Feb 2020 20:58:41 +0000 Subject: [PATCH 022/157] increase content-length fast path to responses up to 1MB --- actix-http/benches/content-length.rs | 32 ++++++++++++++++- actix-http/src/helpers.rs | 54 ++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 4 deletions(-) diff --git a/actix-http/benches/content-length.rs b/actix-http/benches/content-length.rs index 150f48a2c..b001b3931 100644 --- a/actix-http/benches/content-length.rs +++ b/actix-http/benches/content-length.rs @@ -67,7 +67,7 @@ mod _new { bytes.put_u8(DIGITS_START + d100); bytes.put_u8(DIGITS_START + d10); bytes.put_u8(DIGITS_START + d1); - } else if n < 10000 { + } else if n < 10_000 { let n = n as u16; let d1000 = (n / 1000) as u8; @@ -75,6 +75,36 @@ mod _new { let d10 = ((n / 10) % 10) as u8; let d1 = (n % 10) as u8; + bytes.put_u8(DIGITS_START + d1000); + bytes.put_u8(DIGITS_START + d100); + bytes.put_u8(DIGITS_START + d10); + bytes.put_u8(DIGITS_START + d1); + } else if n < 100_000 { + let n = n as u32; + + let d10000 = (n / 10000) as u8; + let d1000 = ((n / 1000) % 10) as u8; + let d100 = ((n / 100) % 10) as u8; + let d10 = ((n / 10) % 10) as u8; + let d1 = (n % 10) as u8; + + bytes.put_u8(DIGITS_START + d10000); + bytes.put_u8(DIGITS_START + d1000); + bytes.put_u8(DIGITS_START + d100); + bytes.put_u8(DIGITS_START + d10); + bytes.put_u8(DIGITS_START + d1); + } else if n < 1_000_000 { + let n = n as u32; + + let d100000 = (n / 100000) as u8; + let d10000 = ((n / 10000) % 10) as u8; + let d1000 = ((n / 1000) % 10) as u8; + let d100 = ((n / 100) % 10) as u8; + let d10 = ((n / 10) % 10) as u8; + let d1 = (n % 10) as u8; + + bytes.put_u8(DIGITS_START + d100000); + bytes.put_u8(DIGITS_START + d10000); bytes.put_u8(DIGITS_START + d1000); bytes.put_u8(DIGITS_START + d100); bytes.put_u8(DIGITS_START + d10); diff --git a/actix-http/src/helpers.rs b/actix-http/src/helpers.rs index cba21ed0c..6599f6a32 100644 --- a/actix-http/src/helpers.rs +++ b/actix-http/src/helpers.rs @@ -88,7 +88,7 @@ pub fn write_content_length(n: usize, bytes: &mut BytesMut) { bytes.put_u8(DIGITS_START + d100); bytes.put_u8(DIGITS_START + d10); bytes.put_u8(DIGITS_START + d1); - } else if n < 10000 { + } else if n < 10_000 { let n = n as u16; let d1000 = (n / 1000) as u8; @@ -96,6 +96,36 @@ pub fn write_content_length(n: usize, bytes: &mut BytesMut) { let d10 = ((n / 10) % 10) as u8; let d1 = (n % 10) as u8; + bytes.put_u8(DIGITS_START + d1000); + bytes.put_u8(DIGITS_START + d100); + bytes.put_u8(DIGITS_START + d10); + bytes.put_u8(DIGITS_START + d1); + } else if n < 100_000 { + let n = n as u32; + + let d10000 = (n / 10000) as u8; + let d1000 = ((n / 1000) % 10) as u8; + let d100 = ((n / 100) % 10) as u8; + let d10 = ((n / 10) % 10) as u8; + let d1 = (n % 10) as u8; + + bytes.put_u8(DIGITS_START + d10000); + bytes.put_u8(DIGITS_START + d1000); + bytes.put_u8(DIGITS_START + d100); + bytes.put_u8(DIGITS_START + d10); + bytes.put_u8(DIGITS_START + d1); + } else if n < 1_000_000 { + let n = n as u32; + + let d100000 = (n / 100000) as u8; + let d10000 = ((n / 10000) % 10) as u8; + let d1000 = ((n / 1000) % 10) as u8; + let d100 = ((n / 100) % 10) as u8; + let d10 = ((n / 10) % 10) as u8; + let d1 = (n % 10) as u8; + + bytes.put_u8(DIGITS_START + d100000); + bytes.put_u8(DIGITS_START + d10000); bytes.put_u8(DIGITS_START + d1000); bytes.put_u8(DIGITS_START + d100); bytes.put_u8(DIGITS_START + d10); @@ -109,7 +139,7 @@ pub fn write_content_length(n: usize, bytes: &mut BytesMut) { pub(crate) fn write_usize(n: usize, bytes: &mut BytesMut) { let mut n = n; - + // 20 chars is max length of a usize (2^64) // digits will be added to the buffer from lsd to msd let mut buf = BytesMut::with_capacity(20); @@ -126,7 +156,7 @@ pub(crate) fn write_usize(n: usize, bytes: &mut BytesMut) { // put msd to result buffer bytes.put_u8(DIGITS_START + (n as u8)); - + // put, in reverse (msd to lsd), remaining digits to buffer for i in (0..buf.len()).rev() { bytes.put_u8(buf[i]); @@ -195,11 +225,17 @@ mod tests { write_content_length(5909, &mut bytes); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 5909\r\n"[..]); bytes.reserve(50); + write_content_length(9999, &mut bytes); + assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 9999\r\n"[..]); + bytes.reserve(50); write_content_length(10001, &mut bytes); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 10001\r\n"[..]); bytes.reserve(50); write_content_length(59094, &mut bytes); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 59094\r\n"[..]); + bytes.reserve(50); + write_content_length(99999, &mut bytes); + assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 99999\r\n"[..]); bytes.reserve(50); write_content_length(590947, &mut bytes); @@ -208,6 +244,12 @@ mod tests { b"\r\ncontent-length: 590947\r\n"[..] ); bytes.reserve(50); + write_content_length(999999, &mut bytes); + assert_eq!( + bytes.split().freeze(), + b"\r\ncontent-length: 999999\r\n"[..] + ); + bytes.reserve(50); write_content_length(5909471, &mut bytes); assert_eq!( bytes.split().freeze(), @@ -219,5 +261,11 @@ mod tests { bytes.split().freeze(), b"\r\ncontent-length: 59094718\r\n"[..] ); + bytes.reserve(50); + write_content_length(4294973728, &mut bytes); + assert_eq!( + bytes.split().freeze(), + b"\r\ncontent-length: 4294973728\r\n"[..] + ); } } From 845ce3cf343487ea5fc48a0d8484d75f0e8d148f Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Tue, 25 Feb 2020 07:46:03 +0900 Subject: [PATCH 023/157] Fix doc comment --- actix-http/src/header/common/accept_charset.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-http/src/header/common/accept_charset.rs b/actix-http/src/header/common/accept_charset.rs index 117e2015d..291ca53b6 100644 --- a/actix-http/src/header/common/accept_charset.rs +++ b/actix-http/src/header/common/accept_charset.rs @@ -63,7 +63,7 @@ header! { (AcceptCharset, ACCEPT_CHARSET) => (QualityItem)+ test_accept_charset { - /// Test case from RFC + // Test case from RFC test_header!(test1, vec![b"iso-8859-5, unicode-1-1;q=0.8"]); } } From a4f87a53da7813e03fa5872e4ed702b21b9afd7e Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Tue, 25 Feb 2020 08:42:39 +0900 Subject: [PATCH 024/157] Update CHANGES.md --- actix-web-codegen/CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-web-codegen/CHANGES.md b/actix-web-codegen/CHANGES.md index 13ed5d43d..941cd36de 100644 --- a/actix-web-codegen/CHANGES.md +++ b/actix-web-codegen/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [0.2.1] - 2020-02-23 +## [0.2.1] - 2020-02-25 * Add `#[allow(missing_docs)]` attribute to generated structs [#1368] * Allow the handler function to be named as `config` [#1290] From 71c4bd1b30d8bf20356d2fcff68f5e6bc98fff17 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Tue, 25 Feb 2020 18:21:05 -0500 Subject: [PATCH 025/157] Remove uses of Pin::new_unchecked in h1 Dispatcher (#1374) This removes the last uses of unsafe `Pin` functions in actix-web. This PR adds a `Pin>` wrapper to `DispatcherState::Upgrade`, `State::ExpectCall`, and `State::ServiceCall`. The previous uses of the futures `State::ExpectCall` and `State::ServiceCall` were Undefined Behavior - a future was obtained from `self.expect.call` or `self.service.call`, pinned on the stack, and then immediately returned from `handle_request`. The only alternative to using `Box::pin` would be to refactor `handle_request` to write the futures directly into their final location, or avoid polling them before they are returned. The previous use of `DispatcherState::Upgrade` doesn't seem to be unsound. However, having data pinned inside an enum that we `std::mem::replace` would require some careful `unsafe` code to ensure that we never call `std::mem::replace` when the active variant contains pinned data. By using `Box::pin`, we any possibility of future refactoring accidentally introducing undefined behavior. Co-authored-by: Yuki Okushi --- actix-http/src/h1/dispatcher.rs | 40 +++++++++++---------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 6f4c09915..a496fd993 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -66,7 +66,7 @@ where U::Error: fmt::Display, { Normal(InnerDispatcher), - Upgrade(U::Future), + Upgrade(Pin>), None, } @@ -114,8 +114,8 @@ where B: MessageBody, { None, - ExpectCall(X::Future), - ServiceCall(S::Future), + ExpectCall(Pin>), + ServiceCall(Pin>), SendPayload(ResponseBody), } @@ -298,7 +298,7 @@ where let len = self.write_buf.len(); let mut written = 0; while written < len { - match unsafe { Pin::new_unchecked(&mut self.io) } + match Pin::new(&mut self.io) .poll_write(cx, &self.write_buf[written..]) { Poll::Ready(Ok(0)) => { @@ -372,10 +372,10 @@ where None => None, }, State::ExpectCall(ref mut fut) => { - match unsafe { Pin::new_unchecked(fut) }.poll(cx) { + match fut.as_mut().poll(cx) { Poll::Ready(Ok(req)) => { self.send_continue(); - self.state = State::ServiceCall(self.service.call(req)); + self.state = State::ServiceCall(Box::pin(self.service.call(req))); continue; } Poll::Ready(Err(e)) => { @@ -387,7 +387,7 @@ where } } State::ServiceCall(ref mut fut) => { - match unsafe { Pin::new_unchecked(fut) }.poll(cx) { + match fut.as_mut().poll(cx) { Poll::Ready(Ok(res)) => { let (res, body) = res.into().replace_body(()); self.state = self.send_response(res, body)?; @@ -463,8 +463,8 @@ where ) -> Result, DispatchError> { // Handle `EXPECT: 100-Continue` header let req = if req.head().expect() { - let mut task = self.expect.call(req); - match unsafe { Pin::new_unchecked(&mut task) }.poll(cx) { + let mut task = Box::pin(self.expect.call(req)); + match task.as_mut().poll(cx) { Poll::Ready(Ok(req)) => { self.send_continue(); req @@ -482,8 +482,8 @@ where }; // Call service - let mut task = self.service.call(req); - match unsafe { Pin::new_unchecked(&mut task) }.poll(cx) { + let mut task = Box::pin(self.service.call(req)); + match task.as_mut().poll(cx) { Poll::Ready(Ok(res)) => { let (res, body) = res.into().replace_body(()); self.send_response(res, body) @@ -681,20 +681,6 @@ where } } -impl Unpin for Dispatcher -where - T: AsyncRead + AsyncWrite + Unpin, - S: Service, - S::Error: Into, - S::Response: Into>, - B: MessageBody, - X: Service, - X::Error: Into, - U: Service), Response = ()>, - U::Error: fmt::Display, -{ -} - impl Future for Dispatcher where T: AsyncRead + AsyncWrite + Unpin, @@ -771,7 +757,7 @@ where parts.write_buf = inner.write_buf; let framed = Framed::from_parts(parts); self.inner = DispatcherState::Upgrade( - inner.upgrade.unwrap().call((req, framed)), + Box::pin(inner.upgrade.unwrap().call((req, framed))), ); return self.poll(cx); } else { @@ -823,7 +809,7 @@ where } } DispatcherState::Upgrade(ref mut fut) => { - unsafe { Pin::new_unchecked(fut) }.poll(cx).map_err(|e| { + fut.as_mut().poll(cx).map_err(|e| { error!("Upgrade handler error: {}", e); DispatchError::Upgrade }) From 48ef4d7a2686bd0500e091c1e041542d32280a7f Mon Sep 17 00:00:00 2001 From: Maxim Vorobjov Date: Thu, 27 Feb 2020 02:34:49 +0200 Subject: [PATCH 026/157] Add actix-http support for actix error messages (#1379) * Moved actix-http for actix from actix crate * remove resolver feature * renamed actix feature to actor * fixed doc attr for actors, add documentation --- MIGRATION.md | 2 ++ actix-http/CHANGES.md | 2 ++ actix-http/Cargo.toml | 6 +++++- actix-http/src/error.rs | 10 ++++++++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/MIGRATION.md b/MIGRATION.md index aef382a21..86721e0eb 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -3,6 +3,8 @@ * Setting a cookie's SameSite property, explicitly, to `SameSite::None` will now result in `SameSite=None` being sent with the response Set-Cookie header. To create a cookie without a SameSite attribute, remove any calls setting same_site. +* actix-http support for Actors messages was moved to actix-http crate and is enabled + with feature `actors` ## 2.0.0 diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 4e8d7fd42..f2ef217c1 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -6,6 +6,8 @@ * Update the `time` dependency to 0.2.7 +* Moved actors messages support from actix crate, enabled with feature `actors`. + ### Fixed * Allow `SameSite=None` cookies to be sent in a response. diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 81203a2f4..afdb548f5 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -15,7 +15,7 @@ license = "MIT/Apache-2.0" edition = "2018" [package.metadata.docs.rs] -features = ["openssl", "rustls", "failure", "compress", "secure-cookies"] +features = ["openssl", "rustls", "failure", "compress", "secure-cookies","actors"] [lib] name = "actix_http" @@ -39,6 +39,9 @@ failure = ["fail-ure"] # support for secure cookies secure-cookies = ["ring"] +# support for actix Actor messages +actors = ["actix"] + [dependencies] actix-service = "1.0.1" actix-codec = "0.2.0" @@ -47,6 +50,7 @@ actix-utils = "1.0.3" actix-rt = "1.0.0" actix-threadpool = "0.3.1" actix-tls = { version = "1.0.0", optional = true } +actix = { version = "0.10.0-alpha.1", optional = true } base64 = "0.11" bitflags = "1.2" diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index c19aef2aa..4b8f13cf0 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -951,6 +951,16 @@ where /// Compatibility for `failure::Error` impl ResponseError for fail_ure::Error {} +#[cfg(feature = "actors")] +/// `InternalServerError` for `actix::MailboxError` +/// This is supported on feature=`actors` only +impl ResponseError for actix::MailboxError {} + +#[cfg(feature = "actors")] +/// `InternalServerError` for `actix::ResolverError` +/// This is supported on feature=`actors` only +impl ResponseError for actix::actors::resolver::ResolverError {} + #[cfg(test)] mod tests { use super::*; From a4148de226bcbee70188cf47b41ac0b725775118 Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Tue, 28 Jan 2020 19:08:03 +0300 Subject: [PATCH 027/157] add test crashing with segfault according to #1321 --- actix-http/src/body.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index 1a2428e14..1c34b06a0 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -612,6 +612,8 @@ mod tests { mod body_stream { use super::*; + use futures::task::noop_waker; + use futures::stream::once; #[actix_rt::test] async fn skips_empty_chunks() { @@ -629,6 +631,25 @@ mod tests { Some(Bytes::from("2")), ); } + + #[actix_rt::test] + async fn move_pinned_pointer() { + let (sender, receiver) = futures::channel::oneshot::channel(); + let mut body_stream = Ok(BodyStream::new(once(async { + let x = Box::new(0i32); + let y = &x; + receiver.await.unwrap(); + let _z = **y; + Ok::<_, ()>(Bytes::new()) + }))); + + let waker = noop_waker(); + let mut context = Context::from_waker(&waker); + + let _ = body_stream.as_mut().unwrap().poll_next(&mut context); + sender.send(()).unwrap(); + let _ = std::mem::replace(&mut body_stream, Err([0; 32])).unwrap().poll_next(&mut context); + } } mod sized_stream { From 9d04b250f908f3304571162583772e4dc1bddd0e Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Wed, 29 Jan 2020 11:15:13 +0300 Subject: [PATCH 028/157] This is a squashed commit: - Convert MessageBody to accept Pin in poll_next - add CHANGES and increase versions aligned to semver - update crates to accomodate MessageBody Pin change - fix tests and dependencies --- CHANGES.md | 4 +- Cargo.toml | 8 +- actix-http/CHANGES.md | 4 + actix-http/Cargo.toml | 14 +-- actix-http/src/body.rs | 185 ++++++++++++++++------------- actix-http/src/client/h1proto.rs | 7 +- actix-http/src/client/h2proto.rs | 6 +- actix-http/src/encoding/encoder.rs | 77 +++++++----- actix-http/src/error.rs | 6 + actix-http/src/h1/dispatcher.rs | 9 +- actix-http/src/h1/service.rs | 8 +- actix-http/src/h1/utils.rs | 20 ++-- actix-http/src/h2/dispatcher.rs | 4 +- actix-http/src/response.rs | 2 +- actix-identity/Cargo.toml | 8 +- actix-multipart/Cargo.toml | 4 +- actix-session/Cargo.toml | 6 +- actix-web-actors/Cargo.toml | 2 +- awc/CHANGES.md | 1 + awc/Cargo.toml | 10 +- src/middleware/logger.rs | 20 ++-- src/test.rs | 6 +- test-server/CHANGES.md | 2 + test-server/Cargo.toml | 8 +- 24 files changed, 247 insertions(+), 174 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ab9caa7bd..ce8ffd619 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,7 @@ # Changes -## [2.0.NEXT] - 2020-01-xx +## [2.1.NEXT] - 2020-01-xx ### Added @@ -15,6 +15,8 @@ * Update the `time` dependency to 0.2.7 +* Accomodate breaking change in actix-http: trait actix_http::MessageBody requires Unpin and accepting Pin<&mut Self> instead of &mut self in the poll_next() + ## [2.0.0] - 2019-12-25 ### Changed diff --git a/Cargo.toml b/Cargo.toml index d06e47ef8..11d6292a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "2.0.0" +version = "3.0.0" authors = ["Nikolay Kim "] description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." readme = "README.md" @@ -71,8 +71,8 @@ actix-threadpool = "0.3.1" actix-tls = "1.0.0" actix-web-codegen = "0.2.0" -actix-http = "1.0.1" -awc = { version = "1.0.1", default-features = false } +actix-http = { version = "2.0.0", path = "actix-http" } +awc = { version = "2.0.0", path = "awc", default-features = false } bytes = "0.5.3" derive_more = "0.99.2" @@ -107,7 +107,7 @@ opt-level = 3 codegen-units = 1 [patch.crates-io] -actix-web = { path = "." } +actix-web = { path = "." } actix-http = { path = "actix-http" } actix-http-test = { path = "test-server" } actix-web-codegen = { path = "actix-web-codegen" } diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index f2ef217c1..8a25efea1 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -8,6 +8,10 @@ * Moved actors messages support from actix crate, enabled with feature `actors`. +* Breaking change: trait MessageBody requires Unpin and accepting Pin<&mut Self> instead of &mut self in the poll_next(). + +* MessageBody is not implemented for &'static [u8] anymore. + ### Fixed * Allow `SameSite=None` cookies to be sent in a response. diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index afdb548f5..3b586521c 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "1.0.1" +version = "2.0.0" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" @@ -43,10 +43,10 @@ secure-cookies = ["ring"] actors = ["actix"] [dependencies] -actix-service = "1.0.1" +actix-service = "1.0.5" actix-codec = "0.2.0" -actix-connect = "1.0.1" -actix-utils = "1.0.3" +actix-connect = "1.0.2" +actix-utils = "1.0.6" actix-rt = "1.0.0" actix-threadpool = "0.3.1" actix-tls = { version = "1.0.0", optional = true } @@ -93,9 +93,9 @@ flate2 = { version = "1.0.13", optional = true } fail-ure = { version = "0.1.5", package="failure", optional = true } [dev-dependencies] -actix-server = "1.0.0" -actix-connect = { version = "1.0.0", features=["openssl"] } -actix-http-test = { version = "1.0.0", features=["openssl"] } +actix-server = "1.0.1" +actix-connect = { version = "1.0.2", features=["openssl"] } +actix-http-test = { version = "2.0.0", path = "../test-server", features=["openssl"] } actix-tls = { version = "1.0.0", features=["openssl"] } criterion = "0.3" futures = "0.3.1" diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index 1c34b06a0..ea742af5f 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -33,10 +33,10 @@ impl BodySize { } /// Type that provides this trait can be streamed to a peer. -pub trait MessageBody { +pub trait MessageBody: Unpin { fn size(&self) -> BodySize; - fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>>; + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>>; downcast_get_type_id!(); } @@ -48,7 +48,7 @@ impl MessageBody for () { BodySize::Empty } - fn poll_next(&mut self, _: &mut Context<'_>) -> Poll>> { + fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll>> { Poll::Ready(None) } } @@ -58,15 +58,28 @@ impl MessageBody for Box { self.as_ref().size() } - fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>> { - self.as_mut().poll_next(cx) + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + let a: Pin<&mut T> = Pin::new(self.get_mut().as_mut()); + a.poll_next(cx) } } +impl MessageBody for Box { + fn size(&self) -> BodySize { + self.as_ref().size() + } + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + let a: Pin<&mut dyn MessageBody> = Pin::new(self.get_mut().as_mut()); + a.poll_next(cx) + } +} + + #[pin_project] pub enum ResponseBody { - Body(B), - Other(Body), + Body(#[pin] B), + Other(#[pin] Body), } impl ResponseBody { @@ -102,10 +115,12 @@ impl MessageBody for ResponseBody { } } - fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>> { - match self { - ResponseBody::Body(ref mut body) => body.poll_next(cx), - ResponseBody::Other(ref mut body) => body.poll_next(cx), + #[project] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + #[project] + match self.project() { + ResponseBody::Body(body) => body.poll_next(cx), + ResponseBody::Other(body) => body.poll_next(cx), } } } @@ -120,12 +135,13 @@ impl Stream for ResponseBody { ) -> Poll> { #[project] match self.project() { - ResponseBody::Body(ref mut body) => body.poll_next(cx), - ResponseBody::Other(ref mut body) => body.poll_next(cx), + ResponseBody::Body(body) => body.poll_next(cx), + ResponseBody::Other(body) => body.poll_next(cx), } } } +#[pin_project] /// Represents various types of http message body. pub enum Body { /// Empty response. `Content-Length` header is not set. @@ -135,7 +151,7 @@ pub enum Body { /// Specific response body. Bytes(Bytes), /// Generic message body. - Message(Box), + Message(#[pin] Box), } impl Body { @@ -160,8 +176,10 @@ impl MessageBody for Body { } } - fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>> { - match self { + #[project] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + #[project] + match self.project() { Body::None => Poll::Ready(None), Body::Empty => Poll::Ready(None), Body::Bytes(ref mut bin) => { @@ -172,7 +190,7 @@ impl MessageBody for Body { Poll::Ready(Some(Ok(mem::replace(bin, Bytes::new())))) } } - Body::Message(ref mut body) => body.poll_next(cx), + Body::Message(body) => body.poll_next(cx), } } } @@ -258,7 +276,7 @@ impl From for Body { impl From> for Body where - S: Stream> + 'static, + S: Stream> + Unpin + 'static, { fn from(s: SizedStream) -> Body { Body::from_message(s) @@ -267,7 +285,7 @@ where impl From> for Body where - S: Stream> + 'static, + S: Stream> + Unpin + 'static, E: Into + 'static, { fn from(s: BodyStream) -> Body { @@ -280,11 +298,11 @@ impl MessageBody for Bytes { BodySize::Sized(self.len()) } - fn poll_next(&mut self, _: &mut Context<'_>) -> Poll>> { + fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { - Poll::Ready(Some(Ok(mem::replace(self, Bytes::new())))) + Poll::Ready(Some(Ok(mem::replace(self.get_mut(), Bytes::new())))) } } } @@ -294,11 +312,11 @@ impl MessageBody for BytesMut { BodySize::Sized(self.len()) } - fn poll_next(&mut self, _: &mut Context<'_>) -> Poll>> { + fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { - Poll::Ready(Some(Ok(mem::replace(self, BytesMut::new()).freeze()))) + Poll::Ready(Some(Ok(mem::replace(self.get_mut(), BytesMut::new()).freeze()))) } } } @@ -308,41 +326,27 @@ impl MessageBody for &'static str { BodySize::Sized(self.len()) } - fn poll_next(&mut self, _: &mut Context<'_>) -> Poll>> { + fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { Poll::Ready(Some(Ok(Bytes::from_static( - mem::replace(self, "").as_ref(), + mem::replace(self.get_mut(), "").as_ref(), )))) } } } -impl MessageBody for &'static [u8] { - fn size(&self) -> BodySize { - BodySize::Sized(self.len()) - } - - fn poll_next(&mut self, _: &mut Context<'_>) -> Poll>> { - if self.is_empty() { - Poll::Ready(None) - } else { - Poll::Ready(Some(Ok(Bytes::from_static(mem::replace(self, b""))))) - } - } -} - impl MessageBody for Vec { fn size(&self) -> BodySize { BodySize::Sized(self.len()) } - fn poll_next(&mut self, _: &mut Context<'_>) -> Poll>> { + fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { - Poll::Ready(Some(Ok(Bytes::from(mem::replace(self, Vec::new()))))) + Poll::Ready(Some(Ok(Bytes::from(mem::replace(self.get_mut(), Vec::new()))))) } } } @@ -352,12 +356,12 @@ impl MessageBody for String { BodySize::Sized(self.len()) } - fn poll_next(&mut self, _: &mut Context<'_>) -> Poll>> { + fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { Poll::Ready(Some(Ok(Bytes::from( - mem::replace(self, String::new()).into_bytes(), + mem::replace(self.get_mut(), String::new()).into_bytes(), )))) } } @@ -365,14 +369,16 @@ impl MessageBody for String { /// Type represent streaming body. /// Response does not contain `content-length` header and appropriate transfer encoding is used. -pub struct BodyStream { - stream: Pin>, +#[pin_project] +pub struct BodyStream { + #[pin] + stream: S, _t: PhantomData, } impl BodyStream where - S: Stream>, + S: Stream> + Unpin, E: Into, { pub fn new(stream: S) -> Self { @@ -385,7 +391,7 @@ where impl MessageBody for BodyStream where - S: Stream>, + S: Stream> + Unpin, E: Into, { fn size(&self) -> BodySize { @@ -397,10 +403,11 @@ where /// Empty values are skipped to prevent [`BodyStream`]'s transmission being /// ended on a zero-length chunk, but rather proceed until the underlying /// [`Stream`] ends. - fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>> { - let mut stream = self.stream.as_mut(); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + let mut stream = self.project().stream; loop { - return Poll::Ready(match ready!(stream.as_mut().poll_next(cx)) { + let stream = stream.as_mut(); + return Poll::Ready(match ready!(stream.poll_next(cx)) { Some(Ok(ref bytes)) if bytes.is_empty() => continue, opt => opt.map(|res| res.map_err(Into::into)), }); @@ -410,14 +417,15 @@ where /// Type represent streaming body. This body implementation should be used /// if total size of stream is known. Data get sent as is without using transfer encoding. -pub struct SizedStream { +#[pin_project] +pub struct SizedStream { size: u64, stream: Pin>, } impl SizedStream where - S: Stream>, + S: Stream> + Unpin, { pub fn new(size: u64, stream: S) -> Self { SizedStream { @@ -429,7 +437,7 @@ where impl MessageBody for SizedStream where - S: Stream>, + S: Stream> + Unpin, { fn size(&self) -> BodySize { BodySize::Sized64(self.size) @@ -440,10 +448,11 @@ where /// Empty values are skipped to prevent [`SizedStream`]'s transmission being /// ended on a zero-length chunk, but rather proceed until the underlying /// [`Stream`] ends. - fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>> { - let mut stream = self.stream.as_mut(); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + let mut stream: Pin<&mut S> = self.project().stream; loop { - return Poll::Ready(match ready!(stream.as_mut().poll_next(cx)) { + let stream = stream.as_mut(); + return Poll::Ready(match ready!(stream.poll_next(cx)) { Some(Ok(ref bytes)) if bytes.is_empty() => continue, val => val, }); @@ -456,6 +465,7 @@ mod tests { use super::*; use futures::stream; use futures_util::future::poll_fn; + use futures_util::pin_mut; impl Body { pub(crate) fn get_ref(&self) -> &[u8] { @@ -483,7 +493,7 @@ mod tests { assert_eq!("test".size(), BodySize::Sized(4)); assert_eq!( - poll_fn(|cx| "test".poll_next(cx)).await.unwrap().ok(), + poll_fn(|cx| Pin::new(&mut "test").poll_next(cx)).await.unwrap().ok(), Some(Bytes::from("test")) ); } @@ -497,10 +507,12 @@ mod tests { BodySize::Sized(4) ); assert_eq!(Body::from_slice(b"test".as_ref()).get_ref(), b"test"); + let sb = Bytes::from(&b"test"[..]); + pin_mut!(sb); - assert_eq!((&b"test"[..]).size(), BodySize::Sized(4)); + assert_eq!(sb.size(), BodySize::Sized(4)); assert_eq!( - poll_fn(|cx| (&b"test"[..]).poll_next(cx)) + poll_fn(|cx| sb.as_mut().poll_next(cx)) .await .unwrap() .ok(), @@ -512,10 +524,12 @@ mod tests { async fn test_vec() { assert_eq!(Body::from(Vec::from("test")).size(), BodySize::Sized(4)); assert_eq!(Body::from(Vec::from("test")).get_ref(), b"test"); + let test_vec = Vec::from("test"); + pin_mut!(test_vec); - assert_eq!(Vec::from("test").size(), BodySize::Sized(4)); + assert_eq!(test_vec.size(), BodySize::Sized(4)); assert_eq!( - poll_fn(|cx| Vec::from("test").poll_next(cx)) + poll_fn(|cx| test_vec.as_mut().poll_next(cx)) .await .unwrap() .ok(), @@ -525,41 +539,44 @@ mod tests { #[actix_rt::test] async fn test_bytes() { - let mut b = Bytes::from("test"); + let b = Bytes::from("test"); assert_eq!(Body::from(b.clone()).size(), BodySize::Sized(4)); assert_eq!(Body::from(b.clone()).get_ref(), b"test"); + pin_mut!(b); assert_eq!(b.size(), BodySize::Sized(4)); assert_eq!( - poll_fn(|cx| b.poll_next(cx)).await.unwrap().ok(), + poll_fn(|cx| b.as_mut().poll_next(cx)).await.unwrap().ok(), Some(Bytes::from("test")) ); } - + #[actix_rt::test] async fn test_bytes_mut() { - let mut b = BytesMut::from("test"); + let b = BytesMut::from("test"); assert_eq!(Body::from(b.clone()).size(), BodySize::Sized(4)); assert_eq!(Body::from(b.clone()).get_ref(), b"test"); + pin_mut!(b); assert_eq!(b.size(), BodySize::Sized(4)); assert_eq!( - poll_fn(|cx| b.poll_next(cx)).await.unwrap().ok(), + poll_fn(|cx| b.as_mut().poll_next(cx)).await.unwrap().ok(), Some(Bytes::from("test")) ); } #[actix_rt::test] async fn test_string() { - let mut b = "test".to_owned(); + let b = "test".to_owned(); assert_eq!(Body::from(b.clone()).size(), BodySize::Sized(4)); assert_eq!(Body::from(b.clone()).get_ref(), b"test"); assert_eq!(Body::from(&b).size(), BodySize::Sized(4)); assert_eq!(Body::from(&b).get_ref(), b"test"); + pin_mut!(b); assert_eq!(b.size(), BodySize::Sized(4)); assert_eq!( - poll_fn(|cx| b.poll_next(cx)).await.unwrap().ok(), + poll_fn(|cx| b.as_mut().poll_next(cx)).await.unwrap().ok(), Some(Bytes::from("test")) ); } @@ -567,14 +584,15 @@ mod tests { #[actix_rt::test] async fn test_unit() { assert_eq!(().size(), BodySize::Empty); - assert!(poll_fn(|cx| ().poll_next(cx)).await.is_none()); + assert!(poll_fn(|cx| Pin::new(&mut ()).poll_next(cx)).await.is_none()); } #[actix_rt::test] async fn test_box() { - let mut val = Box::new(()); + let val = Box::new(()); + pin_mut!(val); assert_eq!(val.size(), BodySize::Empty); - assert!(poll_fn(|cx| val.poll_next(cx)).await.is_none()); + assert!(poll_fn(|cx| val.as_mut().poll_next(cx)).await.is_none()); } #[actix_rt::test] @@ -612,26 +630,29 @@ mod tests { mod body_stream { use super::*; - use futures::task::noop_waker; - use futures::stream::once; + //use futures::task::noop_waker; + //use futures::stream::once; #[actix_rt::test] async fn skips_empty_chunks() { - let mut body = BodyStream::new(stream::iter( + let body = BodyStream::new(stream::iter( ["1", "", "2"] .iter() .map(|&v| Ok(Bytes::from(v)) as Result), )); + pin_mut!(body); + assert_eq!( - poll_fn(|cx| body.poll_next(cx)).await.unwrap().ok(), + poll_fn(|cx| body.as_mut().poll_next(cx)).await.unwrap().ok(), Some(Bytes::from("1")), ); assert_eq!( - poll_fn(|cx| body.poll_next(cx)).await.unwrap().ok(), + poll_fn(|cx| body.as_mut().poll_next(cx)).await.unwrap().ok(), Some(Bytes::from("2")), ); } + /* Now it does not compile as it should #[actix_rt::test] async fn move_pinned_pointer() { let (sender, receiver) = futures::channel::oneshot::channel(); @@ -645,11 +666,12 @@ mod tests { let waker = noop_waker(); let mut context = Context::from_waker(&waker); - + pin_mut!(body_stream); + let _ = body_stream.as_mut().unwrap().poll_next(&mut context); sender.send(()).unwrap(); let _ = std::mem::replace(&mut body_stream, Err([0; 32])).unwrap().poll_next(&mut context); - } + }*/ } mod sized_stream { @@ -657,16 +679,17 @@ mod tests { #[actix_rt::test] async fn skips_empty_chunks() { - let mut body = SizedStream::new( + let body = SizedStream::new( 2, stream::iter(["1", "", "2"].iter().map(|&v| Ok(Bytes::from(v)))), ); + pin_mut!(body); assert_eq!( - poll_fn(|cx| body.poll_next(cx)).await.unwrap().ok(), + poll_fn(|cx| body.as_mut().poll_next(cx)).await.unwrap().ok(), Some(Bytes::from("1")), ); assert_eq!( - poll_fn(|cx| body.poll_next(cx)).await.unwrap().ok(), + poll_fn(|cx| body.as_mut().poll_next(cx)).await.unwrap().ok(), Some(Bytes::from("2")), ); } diff --git a/actix-http/src/client/h1proto.rs b/actix-http/src/client/h1proto.rs index a0a20edf6..c1863b920 100644 --- a/actix-http/src/client/h1proto.rs +++ b/actix-http/src/client/h1proto.rs @@ -8,7 +8,7 @@ use bytes::buf::BufMutExt; use bytes::{Bytes, BytesMut}; use futures_core::Stream; use futures_util::future::poll_fn; -use futures_util::{SinkExt, StreamExt}; +use futures_util::{SinkExt, StreamExt, pin_mut}; use crate::error::PayloadError; use crate::h1; @@ -120,7 +120,7 @@ where /// send request body to the peer pub(crate) async fn send_body( - mut body: B, + body: B, framed: &mut Framed, ) -> Result<(), SendRequestError> where @@ -128,9 +128,10 @@ where B: MessageBody, { let mut eof = false; + pin_mut!(body); while !eof { while !eof && !framed.is_write_buf_full() { - match poll_fn(|cx| body.poll_next(cx)).await { + match poll_fn(|cx| body.as_mut().poll_next(cx)).await { Some(result) => { framed.write(h1::Message::Chunk(Some(result?)))?; } diff --git a/actix-http/src/client/h2proto.rs b/actix-http/src/client/h2proto.rs index eabf54e97..69d20752a 100644 --- a/actix-http/src/client/h2proto.rs +++ b/actix-http/src/client/h2proto.rs @@ -4,6 +4,7 @@ use std::time; use actix_codec::{AsyncRead, AsyncWrite}; use bytes::Bytes; use futures_util::future::poll_fn; +use futures_util::pin_mut; use h2::{client::SendRequest, SendStream}; use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, TRANSFER_ENCODING}; use http::{request::Request, Method, Version}; @@ -123,13 +124,14 @@ where } async fn send_body( - mut body: B, + body: B, mut send: SendStream, ) -> Result<(), SendRequestError> { let mut buf = None; + pin_mut!(body); loop { if buf.is_none() { - match poll_fn(|cx| body.poll_next(cx)).await { + match poll_fn(|cx| body.as_mut().poll_next(cx)).await { Some(Ok(b)) => { send.reserve_capacity(b.len()); buf = Some(b); diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index ca04845ab..6530609e1 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -9,6 +9,7 @@ use brotli2::write::BrotliEncoder; use bytes::Bytes; use flate2::write::{GzEncoder, ZlibEncoder}; use futures_core::ready; +use pin_project::{pin_project, project}; use crate::body::{Body, BodySize, MessageBody, ResponseBody}; use crate::http::header::{ContentEncoding, CONTENT_ENCODING}; @@ -19,8 +20,10 @@ use super::Writer; const INPLACE: usize = 1024; +#[pin_project] pub struct Encoder { eof: bool, + #[pin] body: EncoderBody, encoder: Option, fut: Option>, @@ -76,67 +79,83 @@ impl Encoder { } } +#[pin_project] enum EncoderBody { Bytes(Bytes), - Stream(B), - BoxedStream(Box), + Stream(#[pin] B), + BoxedStream(#[pin] Box), } +impl MessageBody for EncoderBody { + fn size(&self) -> BodySize { + match self { + EncoderBody::Bytes(ref b) => b.size(), + EncoderBody::Stream(ref b) => b.size(), + EncoderBody::BoxedStream(ref b) => b.size(), + } + } + + #[project] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + #[project] + match self.project() { + EncoderBody::Bytes(b) => { + if b.is_empty() { + Poll::Ready(None) + } else { + Poll::Ready(Some(Ok(std::mem::replace(b, Bytes::new())))) + } + } + EncoderBody::Stream(b) => b.poll_next(cx), + EncoderBody::BoxedStream(b) => b.poll_next(cx), + } + } +} + + impl MessageBody for Encoder { fn size(&self) -> BodySize { if self.encoder.is_none() { - match self.body { - EncoderBody::Bytes(ref b) => b.size(), - EncoderBody::Stream(ref b) => b.size(), - EncoderBody::BoxedStream(ref b) => b.size(), - } + self.body.size() } else { BodySize::Stream } } - - fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>> { + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + let mut this = self.project(); loop { - if self.eof { + if *this.eof { return Poll::Ready(None); } - if let Some(ref mut fut) = self.fut { + if let Some(ref mut fut) = this.fut { let mut encoder = match ready!(Pin::new(fut).poll(cx)) { Ok(item) => item, Err(e) => return Poll::Ready(Some(Err(e.into()))), }; let chunk = encoder.take(); - self.encoder = Some(encoder); - self.fut.take(); + *this.encoder = Some(encoder); + this.fut.take(); if !chunk.is_empty() { return Poll::Ready(Some(Ok(chunk))); } } - let result = match self.body { - EncoderBody::Bytes(ref mut b) => { - if b.is_empty() { - Poll::Ready(None) - } else { - Poll::Ready(Some(Ok(std::mem::replace(b, Bytes::new())))) - } - } - EncoderBody::Stream(ref mut b) => b.poll_next(cx), - EncoderBody::BoxedStream(ref mut b) => b.poll_next(cx), - }; + let result = this.body.as_mut().poll_next(cx); + match result { Poll::Ready(Some(Ok(chunk))) => { - if let Some(mut encoder) = self.encoder.take() { + if let Some(mut encoder) = this.encoder.take() { if chunk.len() < INPLACE { encoder.write(&chunk)?; let chunk = encoder.take(); - self.encoder = Some(encoder); + *this.encoder = Some(encoder); if !chunk.is_empty() { return Poll::Ready(Some(Ok(chunk))); } } else { - self.fut = Some(run(move || { + *this.fut = Some(run(move || { encoder.write(&chunk)?; Ok(encoder) })); @@ -146,12 +165,12 @@ impl MessageBody for Encoder { } } Poll::Ready(None) => { - if let Some(encoder) = self.encoder.take() { + if let Some(encoder) = this.encoder.take() { let chunk = encoder.finish()?; if chunk.is_empty() { return Poll::Ready(None); } else { - self.eof = true; + *this.eof = true; return Poll::Ready(Some(Ok(chunk))); } } else { diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 4b8f13cf0..8a1c1b5dc 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -59,6 +59,12 @@ impl Error { } } +/// A struct with a private constructor, for use with +/// `__private_get_type_id__`. Its single field is private, +/// ensuring that it can only be constructed from this module +#[doc(hidden)] +pub struct PrivateHelper(()); + /// Error that can be converted to `Response` pub trait ResponseError: fmt::Debug + fmt::Display { /// Response's status code diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index a496fd993..eb60893ff 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -170,7 +170,7 @@ where S: Service, S::Error: Into, S::Response: Into>, - B: MessageBody, + B: MessageBody+Unpin, X: Service, X::Error: Into, U: Service), Response = ()>, @@ -258,7 +258,7 @@ where S: Service, S::Error: Into, S::Response: Into>, - B: MessageBody, + B: MessageBody+Unpin, X: Service, X::Error: Into, U: Service), Response = ()>, @@ -402,9 +402,10 @@ where } } State::SendPayload(ref mut stream) => { + let mut stream = Pin::new(stream); loop { if self.write_buf.len() < HW_BUFFER_SIZE { - match stream.poll_next(cx) { + match stream.as_mut().poll_next(cx) { Poll::Ready(Some(Ok(item))) => { self.codec.encode( Message::Chunk(Some(item)), @@ -687,7 +688,7 @@ where S: Service, S::Error: Into, S::Response: Into>, - B: MessageBody, + B: MessageBody+Unpin, X: Service, X::Error: Into, U: Service), Response = ()>, diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index 4d1a1dc1b..84e1112e9 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -63,7 +63,7 @@ where S::Error: Into, S::InitError: fmt::Debug, S::Response: Into>, - B: MessageBody, + B: MessageBody+Unpin, X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, @@ -106,7 +106,7 @@ mod openssl { S::Error: Into, S::InitError: fmt::Debug, S::Response: Into>, - B: MessageBody, + B: MessageBody+Unpin, X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, @@ -250,7 +250,7 @@ where S::Error: Into, S::Response: Into>, S::InitError: fmt::Debug, - B: MessageBody, + B: MessageBody+Unpin, X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, @@ -408,7 +408,7 @@ where S: Service, S::Error: Into, S::Response: Into>, - B: MessageBody, + B: MessageBody+Unpin, X: Service, X::Error: Into, U: Service), Response = ()>, diff --git a/actix-http/src/h1/utils.rs b/actix-http/src/h1/utils.rs index 9ba4aa053..be6a42793 100644 --- a/actix-http/src/h1/utils.rs +++ b/actix-http/src/h1/utils.rs @@ -13,6 +13,7 @@ use crate::response::Response; #[pin_project::pin_project] pub struct SendResponse { res: Option, BodySize)>>, + #[pin] body: Option>, framed: Option>, } @@ -39,20 +40,23 @@ where { type Output = Result, Error>; + // TODO: rethink if we need loops in polls fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.get_mut(); + let mut this = self.project(); + let mut body_done = this.body.is_none(); loop { - let mut body_ready = this.body.is_some(); + let mut body_ready = !body_done; let framed = this.framed.as_mut().unwrap(); // send body - if this.res.is_none() && this.body.is_some() { - while body_ready && this.body.is_some() && !framed.is_write_buf_full() { - match this.body.as_mut().unwrap().poll_next(cx)? { + if this.res.is_none() && body_ready { + while body_ready && !body_done && !framed.is_write_buf_full() { + match this.body.as_mut().as_pin_mut().unwrap().poll_next(cx)? { Poll::Ready(item) => { - // body is done - if item.is_none() { + // body is done when item is None + body_done = item.is_none(); + if body_done { let _ = this.body.take(); } framed.write(Message::Chunk(item))?; @@ -82,7 +86,7 @@ where continue; } - if this.body.is_some() { + if body_done { if body_ready { continue; } else { diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 8b17e9479..4b3752ffe 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -168,7 +168,7 @@ struct ServiceResponse { #[pin_project::pin_project] enum ServiceResponseState { ServiceCall(#[pin] F, Option>), - SendPayload(SendStream, ResponseBody), + SendPayload(SendStream, #[pin] ResponseBody), } impl ServiceResponse @@ -338,7 +338,7 @@ where } } } else { - match body.poll_next(cx) { + match body.as_mut().poll_next(cx) { Poll::Pending => return Poll::Pending, Poll::Ready(None) => { if let Err(e) = stream.send_data(Bytes::new(), true) { diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index fcdcd7cdf..655d565ad 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -637,7 +637,7 @@ impl ResponseBuilder { /// `ResponseBuilder` can not be used after this call. pub fn streaming(&mut self, stream: S) -> Response where - S: Stream> + 'static, + S: Stream> + Unpin + 'static, E: Into + 'static, { self.body(Body::from_message(BodyStream::new(stream))) diff --git a/actix-identity/Cargo.toml b/actix-identity/Cargo.toml index efeb24bda..47c792657 100644 --- a/actix-identity/Cargo.toml +++ b/actix-identity/Cargo.toml @@ -16,8 +16,8 @@ name = "actix_identity" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0", default-features = false, features = ["secure-cookies"] } -actix-service = "1.0.2" +actix-web = { version = "3.0.0", path = "..", default-features = false, features = ["secure-cookies"] } +actix-service = "1.0.5" futures = "0.3.1" serde = "1.0" serde_json = "1.0" @@ -25,5 +25,5 @@ time = { version = "0.2.5", default-features = false, features = ["std"] } [dev-dependencies] actix-rt = "1.0.0" -actix-http = "1.0.1" -bytes = "0.5.3" +actix-http = { version = "2.0.0", path = "../actix-http" } +bytes = "0.5.4" diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index f9cd7cfd2..7273fb4ce 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -16,7 +16,7 @@ name = "actix_multipart" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-rc", default-features = false } +actix-web = { version = "2.0.0", default-features = false } actix-service = "1.0.1" actix-utils = "1.0.3" bytes = "0.5.3" @@ -29,4 +29,4 @@ twoway = "0.2" [dev-dependencies] actix-rt = "1.0.0" -actix-http = "1.0.0" +actix-http = "1.0.1" diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml index b279c9d89..d886a5d8e 100644 --- a/actix-session/Cargo.toml +++ b/actix-session/Cargo.toml @@ -22,9 +22,9 @@ default = ["cookie-session"] cookie-session = ["actix-web/secure-cookies"] [dependencies] -actix-web = "2.0.0-rc" -actix-service = "1.0.1" -bytes = "0.5.3" +actix-web = { version = "3.0.0", path = ".." } +actix-service = "1.0.5" +bytes = "0.5.4" derive_more = "0.99.2" futures = "0.3.1" serde = "1.0" diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 6f573e442..7851e1b7f 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -17,7 +17,7 @@ path = "src/lib.rs" [dependencies] actix = "0.9.0" -actix-web = "2.0.0-rc" +actix-web = "2.0.0" actix-http = "1.0.1" actix-codec = "0.2.0" bytes = "0.5.2" diff --git a/awc/CHANGES.md b/awc/CHANGES.md index d9b26e453..917549d09 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -4,6 +4,7 @@ * Fix compilation with default features off +* Accomodate breaking change: trait actix_http::MessageBody requires Unpin and accepting Pin<&mut Self> instead of &mut self in the poll_next() ## [1.0.0] - 2019-12-13 diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 67e0a3ee4..71623e2dc 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "1.0.1" +version = "2.0.0" authors = ["Nikolay Kim "] description = "Actix http client." readme = "README.md" @@ -36,7 +36,7 @@ compress = ["actix-http/compress"] [dependencies] actix-codec = "0.2.0" actix-service = "1.0.1" -actix-http = "1.0.0" +actix-http = { version = "2.0.0", path = "../actix-http" } actix-rt = "1.0.0" base64 = "0.11" @@ -55,9 +55,9 @@ rust-tls = { version = "0.16.0", package="rustls", optional = true, features = [ [dev-dependencies] actix-connect = { version = "1.0.1", features=["openssl"] } -actix-web = { version = "2.0.0-rc", features=["openssl"] } -actix-http = { version = "1.0.1", features=["openssl"] } -actix-http-test = { version = "1.0.0", features=["openssl"] } +actix-web = { version = "3.0.0", path = "..", features=["openssl"] } +actix-http = { version = "2.0.0", path = "../actix-http", features=["openssl"] } +actix-http-test = { version = "2.0.0", path = "../test-server", features=["openssl"] } actix-utils = "1.0.3" actix-server = "1.0.0" actix-tls = { version = "1.0.0", features=["openssl", "rustls"] } diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index d692132ce..661ced966 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -238,19 +238,24 @@ where } } +use pin_project::{pin_project, pinned_drop}; +#[pin_project(PinnedDrop)] pub struct StreamLog { + #[pin] body: ResponseBody, format: Option, size: usize, time: OffsetDateTime, } -impl Drop for StreamLog { - fn drop(&mut self) { - if let Some(ref format) = self.format { +#[pinned_drop] +impl PinnedDrop for StreamLog { + fn drop(self: Pin<&mut Self>) { + let this = self.project(); + if let Some(ref format) = this.format { let render = |fmt: &mut Formatter<'_>| { for unit in &format.0 { - unit.render(fmt, self.size, self.time)?; + unit.render(fmt, *this.size, *this.time)?; } Ok(()) }; @@ -264,10 +269,11 @@ impl MessageBody for StreamLog { self.body.size() } - fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>> { - match self.body.poll_next(cx) { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + let this = self.project(); + match this.body.poll_next(cx) { Poll::Ready(Some(Ok(chunk))) => { - self.size += chunk.len(); + *this.size += chunk.len(); Poll::Ready(Some(Ok(chunk))) } val => val, diff --git a/src/test.rs b/src/test.rs index 956980530..957e66638 100644 --- a/src/test.rs +++ b/src/test.rs @@ -953,7 +953,6 @@ impl Drop for TestServer { #[cfg(test)] mod tests { use actix_http::httpmessage::HttpMessage; - use futures::FutureExt; use serde::{Deserialize, Serialize}; use std::time::SystemTime; @@ -1163,6 +1162,9 @@ mod tests { assert!(res.status().is_success()); } +/* + use futures::FutureExt; + #[actix_rt::test] async fn test_actor() { use actix::Actor; @@ -1183,7 +1185,6 @@ mod tests { } } - let addr = MyActor.start(); let mut app = init_service(App::new().service(web::resource("/index.html").to( move || { @@ -1205,4 +1206,5 @@ mod tests { let res = app.call(req).await.unwrap(); assert!(res.status().is_success()); } +*/ } diff --git a/test-server/CHANGES.md b/test-server/CHANGES.md index f8b29f39d..d262d219c 100644 --- a/test-server/CHANGES.md +++ b/test-server/CHANGES.md @@ -4,6 +4,8 @@ * Update the `time` dependency to 0.2.7 +* Breaking change: trait actix_http::MessageBody requires Unpin and accepting Pin<&mut Self> instead of &mut self in the poll_next() + ## [1.0.0] - 2019-12-13 diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 774c8f0b2..7ea0a9ba4 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http-test" -version = "1.0.0" +version = "2.0.0" authors = ["Nikolay Kim "] description = "Actix http test server" readme = "README.md" @@ -37,7 +37,7 @@ actix-utils = "1.0.3" actix-rt = "1.0.0" actix-server = "1.0.0" actix-testing = "1.0.0" -awc = "1.0.0" +awc = { version = "2.0.0", path = "../awc" } base64 = "0.11" bytes = "0.5.3" @@ -55,5 +55,5 @@ time = { version = "0.2.7", default-features = false, features = ["std"] } open-ssl = { version="0.10", package="openssl", optional = true } [dev-dependencies] -actix-web = "2.0.0-rc" -actix-http = "1.0.1" +actix-web = { version = "3.0.0", path = ".." } +actix-http = { version = "2.0.0", path = "../actix-http" } From 62aba424e2a5c8ac41267b7a2e73e69fe6cdc1e2 Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Thu, 30 Jan 2020 19:33:49 +0200 Subject: [PATCH 029/157] Rollback actix-http-test dependency to show the issue --- actix-http/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 3b586521c..10e9611fd 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -95,7 +95,7 @@ fail-ure = { version = "0.1.5", package="failure", optional = true } [dev-dependencies] actix-server = "1.0.1" actix-connect = { version = "1.0.2", features=["openssl"] } -actix-http-test = { version = "2.0.0", path = "../test-server", features=["openssl"] } +actix-http-test = { version = "1.0.0", features=["openssl"] } actix-tls = { version = "1.0.0", features=["openssl"] } criterion = "0.3" futures = "0.3.1" From 09a391a3ca598a8be03f41c656ab70706fd6d2bd Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Fri, 31 Jan 2020 10:29:10 +0200 Subject: [PATCH 030/157] rollback changes to actix-web, awc and test-server for now --- Cargo.toml | 8 ++++---- actix-http/src/body.rs | 10 ++++------ actix-http/tests/test_ws.rs | 5 +++++ awc/Cargo.toml | 10 +++++----- src/middleware/logger.rs | 20 +++++++------------- test-server/Cargo.toml | 4 ++-- 6 files changed, 27 insertions(+), 30 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 11d6292a1..b9b647568 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,8 +33,8 @@ members = [ "actix-cors", "actix-files", "actix-framed", - "actix-session", - "actix-identity", +# "actix-session", +# "actix-identity", "actix-multipart", "actix-web-actors", "actix-web-codegen", @@ -71,8 +71,8 @@ actix-threadpool = "0.3.1" actix-tls = "1.0.0" actix-web-codegen = "0.2.0" -actix-http = { version = "2.0.0", path = "actix-http" } -awc = { version = "2.0.0", path = "awc", default-features = false } +actix-http = { version = "1.0.1" } +awc = { version = "1.0.1", default-features = false } bytes = "0.5.3" derive_more = "0.99.2" diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index ea742af5f..74e6e218d 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -383,7 +383,7 @@ where { pub fn new(stream: S) -> Self { BodyStream { - stream: Box::pin(stream), + stream, _t: PhantomData, } } @@ -420,7 +420,8 @@ where #[pin_project] pub struct SizedStream { size: u64, - stream: Pin>, + #[pin] + stream: S, } impl SizedStream @@ -428,10 +429,7 @@ where S: Stream> + Unpin, { pub fn new(size: u64, stream: S) -> Self { - SizedStream { - size, - stream: Box::pin(stream), - } + SizedStream { size, stream } } } diff --git a/actix-http/tests/test_ws.rs b/actix-http/tests/test_ws.rs index 7152fee48..2f2a28e2f 100644 --- a/actix-http/tests/test_ws.rs +++ b/actix-http/tests/test_ws.rs @@ -81,6 +81,9 @@ async fn service(msg: ws::Frame) -> Result { Ok(msg) } +/* +Temporarily commented out due to dependency on actix-http-test + #[actix_rt::test] async fn test_simple() { let ws_service = WsService::new(); @@ -192,3 +195,5 @@ async fn test_simple() { assert!(ws_service.was_polled()); } + +*/ \ No newline at end of file diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 71623e2dc..f7d5634e0 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "2.0.0" +version = "1.0.1" authors = ["Nikolay Kim "] description = "Actix http client." readme = "README.md" @@ -36,7 +36,7 @@ compress = ["actix-http/compress"] [dependencies] actix-codec = "0.2.0" actix-service = "1.0.1" -actix-http = { version = "2.0.0", path = "../actix-http" } +actix-http = { version = "1.0.1" } actix-rt = "1.0.0" base64 = "0.11" @@ -55,9 +55,9 @@ rust-tls = { version = "0.16.0", package="rustls", optional = true, features = [ [dev-dependencies] actix-connect = { version = "1.0.1", features=["openssl"] } -actix-web = { version = "3.0.0", path = "..", features=["openssl"] } -actix-http = { version = "2.0.0", path = "../actix-http", features=["openssl"] } -actix-http-test = { version = "2.0.0", path = "../test-server", features=["openssl"] } +actix-web = { version = "2.0.0", features=["openssl"] } +actix-http = { version = "1.0.1", features=["openssl"] } +actix-http-test = { version = "1.0.0", features=["openssl"] } actix-utils = "1.0.3" actix-server = "1.0.0" actix-tls = { version = "1.0.0", features=["openssl", "rustls"] } diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index 661ced966..d692132ce 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -238,24 +238,19 @@ where } } -use pin_project::{pin_project, pinned_drop}; -#[pin_project(PinnedDrop)] pub struct StreamLog { - #[pin] body: ResponseBody, format: Option, size: usize, time: OffsetDateTime, } -#[pinned_drop] -impl PinnedDrop for StreamLog { - fn drop(self: Pin<&mut Self>) { - let this = self.project(); - if let Some(ref format) = this.format { +impl Drop for StreamLog { + fn drop(&mut self) { + if let Some(ref format) = self.format { let render = |fmt: &mut Formatter<'_>| { for unit in &format.0 { - unit.render(fmt, *this.size, *this.time)?; + unit.render(fmt, self.size, self.time)?; } Ok(()) }; @@ -269,11 +264,10 @@ impl MessageBody for StreamLog { self.body.size() } - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { - let this = self.project(); - match this.body.poll_next(cx) { + fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>> { + match self.body.poll_next(cx) { Poll::Ready(Some(Ok(chunk))) => { - *this.size += chunk.len(); + self.size += chunk.len(); Poll::Ready(Some(Ok(chunk))) } val => val, diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 7ea0a9ba4..51c25f8d1 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http-test" -version = "2.0.0" +version = "1.0.0" authors = ["Nikolay Kim "] description = "Actix http test server" readme = "README.md" @@ -37,7 +37,7 @@ actix-utils = "1.0.3" actix-rt = "1.0.0" actix-server = "1.0.0" actix-testing = "1.0.0" -awc = { version = "2.0.0", path = "../awc" } +awc = { version = "1.0.1" } base64 = "0.11" bytes = "0.5.3" From d9c415e5403cdf28f34eafc9aeef240753d5356e Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Fri, 31 Jan 2020 12:09:18 +0200 Subject: [PATCH 031/157] disable weird poll test until actix-web based on actix-http:2 --- tests/test_weird_poll.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_weird_poll.rs b/tests/test_weird_poll.rs index 21d1d611a..768dda1c8 100644 --- a/tests/test_weird_poll.rs +++ b/tests/test_weird_poll.rs @@ -5,6 +5,9 @@ use futures::stream::once; use actix_http::body::{MessageBody, BodyStream}; use bytes::Bytes; +/* +Disable weird poll until actix-web is based on actix-http 2.0.0 + #[test] fn weird_poll() { let (sender, receiver) = futures::channel::oneshot::channel(); @@ -24,3 +27,4 @@ fn weird_poll() { let _ = std::mem::replace(&mut body_stream, Err([0; 32])).unwrap().poll_next(&mut context); } +*/ \ No newline at end of file From 835a00599c1bb68561f9495b9ae38d2a3d119f08 Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Fri, 31 Jan 2020 12:39:53 +0200 Subject: [PATCH 032/157] rollback missed dependencies and CHANGES in crates except actix-http --- CHANGES.md | 4 +--- Cargo.toml | 2 +- actix-identity/Cargo.toml | 4 ++-- actix-session/Cargo.toml | 2 +- awc/CHANGES.md | 2 -- src/test.rs | 4 ++++ test-server/CHANGES.md | 3 --- test-server/Cargo.toml | 4 ++-- tests/test_weird_poll.rs | 2 +- 9 files changed, 12 insertions(+), 15 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ce8ffd619..ab9caa7bd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,7 @@ # Changes -## [2.1.NEXT] - 2020-01-xx +## [2.0.NEXT] - 2020-01-xx ### Added @@ -15,8 +15,6 @@ * Update the `time` dependency to 0.2.7 -* Accomodate breaking change in actix-http: trait actix_http::MessageBody requires Unpin and accepting Pin<&mut Self> instead of &mut self in the poll_next() - ## [2.0.0] - 2019-12-25 ### Changed diff --git a/Cargo.toml b/Cargo.toml index b9b647568..5ba358e2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "3.0.0" +version = "2.0.0" authors = ["Nikolay Kim "] description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." readme = "README.md" diff --git a/actix-identity/Cargo.toml b/actix-identity/Cargo.toml index 47c792657..910aef48e 100644 --- a/actix-identity/Cargo.toml +++ b/actix-identity/Cargo.toml @@ -16,7 +16,7 @@ name = "actix_identity" path = "src/lib.rs" [dependencies] -actix-web = { version = "3.0.0", path = "..", default-features = false, features = ["secure-cookies"] } +actix-web = { version = "2.0.0", default-features = false, features = ["secure-cookies"] } actix-service = "1.0.5" futures = "0.3.1" serde = "1.0" @@ -25,5 +25,5 @@ time = { version = "0.2.5", default-features = false, features = ["std"] } [dev-dependencies] actix-rt = "1.0.0" -actix-http = { version = "2.0.0", path = "../actix-http" } +actix-http = { version = "1.0.1" } bytes = "0.5.4" diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml index d886a5d8e..b0a89ee29 100644 --- a/actix-session/Cargo.toml +++ b/actix-session/Cargo.toml @@ -22,7 +22,7 @@ default = ["cookie-session"] cookie-session = ["actix-web/secure-cookies"] [dependencies] -actix-web = { version = "3.0.0", path = ".." } +actix-web = { version = "2.0.0" } actix-service = "1.0.5" bytes = "0.5.4" derive_more = "0.99.2" diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 917549d09..d410ae514 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -4,8 +4,6 @@ * Fix compilation with default features off -* Accomodate breaking change: trait actix_http::MessageBody requires Unpin and accepting Pin<&mut Self> instead of &mut self in the poll_next() - ## [1.0.0] - 2019-12-13 * Release diff --git a/src/test.rs b/src/test.rs index 957e66638..6a6ef27c5 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1163,6 +1163,10 @@ mod tests { } /* + + Comment out until actix decoupled of actix-http: + https://github.com/actix/actix/issues/321 + use futures::FutureExt; #[actix_rt::test] diff --git a/test-server/CHANGES.md b/test-server/CHANGES.md index d262d219c..96c010355 100644 --- a/test-server/CHANGES.md +++ b/test-server/CHANGES.md @@ -4,9 +4,6 @@ * Update the `time` dependency to 0.2.7 -* Breaking change: trait actix_http::MessageBody requires Unpin and accepting Pin<&mut Self> instead of &mut self in the poll_next() - - ## [1.0.0] - 2019-12-13 ### Changed diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 51c25f8d1..f4ec1e238 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -55,5 +55,5 @@ time = { version = "0.2.7", default-features = false, features = ["std"] } open-ssl = { version="0.10", package="openssl", optional = true } [dev-dependencies] -actix-web = { version = "3.0.0", path = ".." } -actix-http = { version = "2.0.0", path = "../actix-http" } +actix-web = { version = "2.0.0" } +actix-http = { version = "1.0.1" } diff --git a/tests/test_weird_poll.rs b/tests/test_weird_poll.rs index 768dda1c8..571b69f45 100644 --- a/tests/test_weird_poll.rs +++ b/tests/test_weird_poll.rs @@ -27,4 +27,4 @@ fn weird_poll() { let _ = std::mem::replace(&mut body_stream, Err([0; 32])).unwrap().poll_next(&mut context); } -*/ \ No newline at end of file +*/ From eeebc653fdb5f0f2b9673923c8a79a312c78ffd1 Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Fri, 31 Jan 2020 13:29:51 +0200 Subject: [PATCH 033/157] change actix-http version to alpha --- actix-http/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 10e9611fd..dfb467d40 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "2.0.0" +version = "2.0.0-alpha" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" From 2e2ea7ab800e7325bd9a264b6cad177e5390df87 Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Fri, 31 Jan 2020 22:16:31 +0200 Subject: [PATCH 034/157] remove extra whitespaces and Unpins --- Cargo.toml | 2 +- actix-http/src/h1/dispatcher.rs | 6 +++--- actix-http/src/h1/service.rs | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5ba358e2c..550df49dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,7 +107,7 @@ opt-level = 3 codegen-units = 1 [patch.crates-io] -actix-web = { path = "." } +actix-web = { path = "." } actix-http = { path = "actix-http" } actix-http-test = { path = "test-server" } actix-web-codegen = { path = "actix-web-codegen" } diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index eb60893ff..0f897561d 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -170,7 +170,7 @@ where S: Service, S::Error: Into, S::Response: Into>, - B: MessageBody+Unpin, + B: MessageBody, X: Service, X::Error: Into, U: Service), Response = ()>, @@ -258,7 +258,7 @@ where S: Service, S::Error: Into, S::Response: Into>, - B: MessageBody+Unpin, + B: MessageBody, X: Service, X::Error: Into, U: Service), Response = ()>, @@ -688,7 +688,7 @@ where S: Service, S::Error: Into, S::Response: Into>, - B: MessageBody+Unpin, + B: MessageBody, X: Service, X::Error: Into, U: Service), Response = ()>, diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index 84e1112e9..4d1a1dc1b 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -63,7 +63,7 @@ where S::Error: Into, S::InitError: fmt::Debug, S::Response: Into>, - B: MessageBody+Unpin, + B: MessageBody, X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, @@ -106,7 +106,7 @@ mod openssl { S::Error: Into, S::InitError: fmt::Debug, S::Response: Into>, - B: MessageBody+Unpin, + B: MessageBody, X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, @@ -250,7 +250,7 @@ where S::Error: Into, S::Response: Into>, S::InitError: fmt::Debug, - B: MessageBody+Unpin, + B: MessageBody, X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, @@ -408,7 +408,7 @@ where S: Service, S::Error: Into, S::Response: Into>, - B: MessageBody+Unpin, + B: MessageBody, X: Service, X::Error: Into, U: Service), Response = ()>, From ec5c7797325e63d59eaa54597916d72e95343975 Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Mon, 3 Feb 2020 22:55:49 +0200 Subject: [PATCH 035/157] unlink MessageBody from Unpin --- actix-http/src/body.rs | 10 +- actix-http/src/h1/dispatcher.rs | 308 ++++++++++++++++++-------------- actix-http/src/h1/utils.rs | 2 +- 3 files changed, 181 insertions(+), 139 deletions(-) diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index 74e6e218d..26134723d 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -33,7 +33,7 @@ impl BodySize { } /// Type that provides this trait can be streamed to a peer. -pub trait MessageBody: Unpin { +pub trait MessageBody { fn size(&self) -> BodySize; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>>; @@ -53,14 +53,13 @@ impl MessageBody for () { } } -impl MessageBody for Box { +impl MessageBody for Box { fn size(&self) -> BodySize { self.as_ref().size() } fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { - let a: Pin<&mut T> = Pin::new(self.get_mut().as_mut()); - a.poll_next(cx) + unsafe { self.map_unchecked_mut(|boxed| boxed.as_mut()) }.poll_next(cx) } } @@ -70,8 +69,7 @@ impl MessageBody for Box { } fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { - let a: Pin<&mut dyn MessageBody> = Pin::new(self.get_mut().as_mut()); - a.poll_next(cx) + unsafe { Pin::new_unchecked(self.get_mut().as_mut()) }.poll_next(cx) } } diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 0f897561d..7429c50f7 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -10,6 +10,7 @@ use actix_service::Service; use bitflags::bitflags; use bytes::{Buf, BytesMut}; use log::{error, trace}; +use pin_project::pin_project; use crate::body::{Body, BodySize, MessageBody, ResponseBody}; use crate::cloneable::CloneableService; @@ -41,6 +42,7 @@ bitflags! { } } +#[pin_project::pin_project] /// Dispatcher for HTTP/1.1 protocol pub struct Dispatcher where @@ -52,9 +54,11 @@ where U: Service), Response = ()>, U::Error: fmt::Display, { + #[pin] inner: DispatcherState, } +#[pin_project] enum DispatcherState where S: Service, @@ -65,11 +69,12 @@ where U: Service), Response = ()>, U::Error: fmt::Display, { - Normal(InnerDispatcher), - Upgrade(Pin>), + Normal(#[pin] InnerDispatcher), + Upgrade(#[pin] U::Future), None, } +#[pin_project] struct InnerDispatcher where S: Service, @@ -88,6 +93,7 @@ where peer_addr: Option, error: Option, + #[pin] state: State, payload: Option, messages: VecDeque, @@ -107,6 +113,7 @@ enum DispatcherMessage { Error(Response<()>), } +#[pin_project] enum State where S: Service, @@ -114,9 +121,9 @@ where B: MessageBody, { None, - ExpectCall(Pin>), - ServiceCall(Pin>), - SendPayload(ResponseBody), + ExpectCall(#[pin] X::Future), + ServiceCall(#[pin] S::Future), + SendPayload(#[pin] ResponseBody), } impl State @@ -142,6 +149,21 @@ where } } +impl DispatcherState +where + S: Service, + S::Error: Into, + B: MessageBody, + X: Service, + X::Error: Into, + U: Service), Response = ()>, + U::Error: fmt::Display, +{ + fn take(self: Pin<&mut Self>) -> Self { + std::mem::replace(unsafe { self.get_unchecked_mut() }, Self::None) + } +} + enum PollResponse { Upgrade(Request), DoNothing, @@ -278,10 +300,11 @@ where } // if checked is set to true, delay disconnect until all tasks have finished. - fn client_disconnected(&mut self) { - self.flags + fn client_disconnected(self: Pin<&mut Self>) { + let this = self.project(); + this.flags .insert(Flags::READ_DISCONNECT | Flags::WRITE_DISCONNECT); - if let Some(mut payload) = self.payload.take() { + if let Some(mut payload) = this.payload.take() { payload.set_error(PayloadError::Incomplete(None)); } } @@ -290,16 +313,18 @@ where /// /// true - got whouldblock /// false - didnt get whouldblock - fn poll_flush(&mut self, cx: &mut Context<'_>) -> Result { + #[pin_project::project] + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Result { if self.write_buf.is_empty() { return Ok(false); } let len = self.write_buf.len(); let mut written = 0; + #[project] + let InnerDispatcher { mut io, write_buf, .. } = self.project(); while written < len { - match Pin::new(&mut self.io) - .poll_write(cx, &self.write_buf[written..]) + match Pin::new(&mut io).poll_write(cx, &write_buf[written..]) { Poll::Ready(Ok(0)) => { return Err(DispatchError::Io(io::Error::new( @@ -312,113 +337,120 @@ where } Poll::Pending => { if written > 0 { - self.write_buf.advance(written); + write_buf.advance(written); } return Ok(true); } Poll::Ready(Err(err)) => return Err(DispatchError::Io(err)), } } - if written == self.write_buf.len() { - unsafe { self.write_buf.set_len(0) } + if written == write_buf.len() { + unsafe { write_buf.set_len(0) } } else { - self.write_buf.advance(written); + write_buf.advance(written); } Ok(false) } fn send_response( - &mut self, + self: Pin<&mut Self>, message: Response<()>, body: ResponseBody, ) -> Result, DispatchError> { - self.codec - .encode(Message::Item((message, body.size())), &mut self.write_buf) + let mut this = self.project(); + this.codec + .encode(Message::Item((message, body.size())), &mut this.write_buf) .map_err(|err| { - if let Some(mut payload) = self.payload.take() { + if let Some(mut payload) = this.payload.take() { payload.set_error(PayloadError::Incomplete(None)); } DispatchError::Io(err) })?; - self.flags.set(Flags::KEEPALIVE, self.codec.keepalive()); + this.flags.set(Flags::KEEPALIVE, this.codec.keepalive()); match body.size() { BodySize::None | BodySize::Empty => Ok(State::None), _ => Ok(State::SendPayload(body)), } } - fn send_continue(&mut self) { - self.write_buf + fn send_continue(self: Pin<&mut Self>) { + self.project().write_buf .extend_from_slice(b"HTTP/1.1 100 Continue\r\n\r\n"); } + #[pin_project::project] fn poll_response( - &mut self, + mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Result { loop { - let state = match self.state { - State::None => match self.messages.pop_front() { + let mut this = self.as_mut().project(); + #[project] + let state = match this.state.project() { + State::None => match this.messages.pop_front() { Some(DispatcherMessage::Item(req)) => { - Some(self.handle_request(req, cx)?) + Some(self.as_mut().handle_request(req, cx)?) } Some(DispatcherMessage::Error(res)) => { - Some(self.send_response(res, ResponseBody::Other(Body::Empty))?) + Some(self.as_mut().send_response(res, ResponseBody::Other(Body::Empty))?) } Some(DispatcherMessage::Upgrade(req)) => { return Ok(PollResponse::Upgrade(req)); } None => None, }, - State::ExpectCall(ref mut fut) => { - match fut.as_mut().poll(cx) { + State::ExpectCall(fut) => { + match fut.poll(cx) { Poll::Ready(Ok(req)) => { - self.send_continue(); - self.state = State::ServiceCall(Box::pin(self.service.call(req))); + self.as_mut().send_continue(); + this = self.as_mut().project(); + this.state.set(State::ServiceCall(this.service.call(req))); continue; } Poll::Ready(Err(e)) => { let res: Response = e.into().into(); let (res, body) = res.replace_body(()); - Some(self.send_response(res, body.into_body())?) + Some(self.as_mut().send_response(res, body.into_body())?) } Poll::Pending => None, } } - State::ServiceCall(ref mut fut) => { - match fut.as_mut().poll(cx) { + State::ServiceCall(fut) => { + match fut.poll(cx) { Poll::Ready(Ok(res)) => { let (res, body) = res.into().replace_body(()); - self.state = self.send_response(res, body)?; + let state = self.as_mut().send_response(res, body)?; + this = self.as_mut().project(); + this.state.set(state); continue; } Poll::Ready(Err(e)) => { let res: Response = e.into().into(); let (res, body) = res.replace_body(()); - Some(self.send_response(res, body.into_body())?) + Some(self.as_mut().send_response(res, body.into_body())?) } Poll::Pending => None, } } - State::SendPayload(ref mut stream) => { - let mut stream = Pin::new(stream); + State::SendPayload(mut stream) => { loop { - if self.write_buf.len() < HW_BUFFER_SIZE { + if this.write_buf.len() < HW_BUFFER_SIZE { match stream.as_mut().poll_next(cx) { Poll::Ready(Some(Ok(item))) => { - self.codec.encode( + this.codec.encode( Message::Chunk(Some(item)), - &mut self.write_buf, + &mut this.write_buf, )?; continue; } Poll::Ready(None) => { - self.codec.encode( + this.codec.encode( Message::Chunk(None), - &mut self.write_buf, + &mut this.write_buf, )?; - self.state = State::None; + this = self.as_mut().project(); + this.state.set(State::None); } Poll::Ready(Some(Err(_))) => { return Err(DispatchError::Unknown) @@ -434,9 +466,11 @@ where } }; + this = self.as_mut().project(); + // set new state if let Some(state) = state { - self.state = state; + this.state.set(state); if !self.state.is_empty() { continue; } @@ -444,7 +478,7 @@ where // if read-backpressure is enabled and we consumed some data. // we may read more data and retry if self.state.is_call() { - if self.poll_request(cx)? { + if self.as_mut().poll_request(cx)? { continue; } } else if !self.messages.is_empty() { @@ -458,16 +492,16 @@ where } fn handle_request( - &mut self, + mut self: Pin<&mut Self>, req: Request, cx: &mut Context<'_>, ) -> Result, DispatchError> { // Handle `EXPECT: 100-Continue` header let req = if req.head().expect() { - let mut task = Box::pin(self.expect.call(req)); - match task.as_mut().poll(cx) { + let mut task = self.as_mut().project().expect.call(req); + match unsafe { Pin::new_unchecked(&mut task) }.poll(cx) { Poll::Ready(Ok(req)) => { - self.send_continue(); + self.as_mut().send_continue(); req } Poll::Pending => return Ok(State::ExpectCall(task)), @@ -483,8 +517,8 @@ where }; // Call service - let mut task = Box::pin(self.service.call(req)); - match task.as_mut().poll(cx) { + let mut task = self.as_mut().project().service.call(req); + match unsafe { Pin::new_unchecked(&mut task) }.poll(cx) { Poll::Ready(Ok(res)) => { let (res, body) = res.into().replace_body(()); self.send_response(res, body) @@ -500,7 +534,7 @@ where /// Process one incoming requests pub(self) fn poll_request( - &mut self, + mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Result { // limit a mount of non processed requests @@ -509,24 +543,25 @@ where } let mut updated = false; + let mut this = self.as_mut().project(); loop { - match self.codec.decode(&mut self.read_buf) { + match this.codec.decode(&mut this.read_buf) { Ok(Some(msg)) => { updated = true; - self.flags.insert(Flags::STARTED); + this.flags.insert(Flags::STARTED); match msg { Message::Item(mut req) => { - let pl = self.codec.message_type(); - req.head_mut().peer_addr = self.peer_addr; + let pl = this.codec.message_type(); + req.head_mut().peer_addr = *this.peer_addr; // set on_connect data - if let Some(ref on_connect) = self.on_connect { + if let Some(ref on_connect) = this.on_connect { on_connect.set(&mut req.extensions_mut()); } - if pl == MessageType::Stream && self.upgrade.is_some() { - self.messages.push_back(DispatcherMessage::Upgrade(req)); + if pl == MessageType::Stream && this.upgrade.is_some() { + this.messages.push_back(DispatcherMessage::Upgrade(req)); break; } if pl == MessageType::Payload || pl == MessageType::Stream { @@ -534,41 +569,43 @@ where let (req1, _) = req.replace_payload(crate::Payload::H1(pl)); req = req1; - self.payload = Some(ps); + *this.payload = Some(ps); } // handle request early - if self.state.is_empty() { - self.state = self.handle_request(req, cx)?; + if this.state.is_empty() { + let state = self.as_mut().handle_request(req, cx)?; + this = self.as_mut().project(); + this.state.set(state); } else { - self.messages.push_back(DispatcherMessage::Item(req)); + this.messages.push_back(DispatcherMessage::Item(req)); } } Message::Chunk(Some(chunk)) => { - if let Some(ref mut payload) = self.payload { + if let Some(ref mut payload) = this.payload { payload.feed_data(chunk); } else { error!( "Internal server error: unexpected payload chunk" ); - self.flags.insert(Flags::READ_DISCONNECT); - self.messages.push_back(DispatcherMessage::Error( + this.flags.insert(Flags::READ_DISCONNECT); + this.messages.push_back(DispatcherMessage::Error( Response::InternalServerError().finish().drop_body(), )); - self.error = Some(DispatchError::InternalError); + *this.error = Some(DispatchError::InternalError); break; } } Message::Chunk(None) => { - if let Some(mut payload) = self.payload.take() { + if let Some(mut payload) = this.payload.take() { payload.feed_eof(); } else { error!("Internal server error: unexpected eof"); - self.flags.insert(Flags::READ_DISCONNECT); - self.messages.push_back(DispatcherMessage::Error( + this.flags.insert(Flags::READ_DISCONNECT); + this.messages.push_back(DispatcherMessage::Error( Response::InternalServerError().finish().drop_body(), )); - self.error = Some(DispatchError::InternalError); + *this.error = Some(DispatchError::InternalError); break; } } @@ -576,44 +613,46 @@ where } Ok(None) => break, Err(ParseError::Io(e)) => { - self.client_disconnected(); - self.error = Some(DispatchError::Io(e)); + self.as_mut().client_disconnected(); + this = self.as_mut().project(); + *this.error = Some(DispatchError::Io(e)); break; } Err(e) => { - if let Some(mut payload) = self.payload.take() { + if let Some(mut payload) = this.payload.take() { payload.set_error(PayloadError::EncodingCorrupted); } // Malformed requests should be responded with 400 - self.messages.push_back(DispatcherMessage::Error( + this.messages.push_back(DispatcherMessage::Error( Response::BadRequest().finish().drop_body(), )); - self.flags.insert(Flags::READ_DISCONNECT); - self.error = Some(e.into()); + this.flags.insert(Flags::READ_DISCONNECT); + *this.error = Some(e.into()); break; } } } - if updated && self.ka_timer.is_some() { - if let Some(expire) = self.codec.config().keep_alive_expire() { - self.ka_expire = expire; + if updated && this.ka_timer.is_some() { + if let Some(expire) = this.codec.config().keep_alive_expire() { + *this.ka_expire = expire; } } Ok(updated) } /// keep-alive timer - fn poll_keepalive(&mut self, cx: &mut Context<'_>) -> Result<(), DispatchError> { - if self.ka_timer.is_none() { + fn poll_keepalive(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Result<(), DispatchError> { + let mut this = self.as_mut().project(); + if this.ka_timer.is_none() { // shutdown timeout - if self.flags.contains(Flags::SHUTDOWN) { - if let Some(interval) = self.codec.config().client_disconnect_timer() { - self.ka_timer = Some(delay_until(interval)); + if this.flags.contains(Flags::SHUTDOWN) { + if let Some(interval) = this.codec.config().client_disconnect_timer() { + *this.ka_timer = Some(delay_until(interval)); } else { - self.flags.insert(Flags::READ_DISCONNECT); - if let Some(mut payload) = self.payload.take() { + this.flags.insert(Flags::READ_DISCONNECT); + if let Some(mut payload) = this.payload.take() { payload.set_error(PayloadError::Incomplete(None)); } return Ok(()); @@ -623,55 +662,56 @@ where } } - match Pin::new(&mut self.ka_timer.as_mut().unwrap()).poll(cx) { + match Pin::new(&mut this.ka_timer.as_mut().unwrap()).poll(cx) { Poll::Ready(()) => { // if we get timeout during shutdown, drop connection - if self.flags.contains(Flags::SHUTDOWN) { + if this.flags.contains(Flags::SHUTDOWN) { return Err(DispatchError::DisconnectTimeout); - } else if self.ka_timer.as_mut().unwrap().deadline() >= self.ka_expire { + } else if this.ka_timer.as_mut().unwrap().deadline() >= *this.ka_expire { // check for any outstanding tasks - if self.state.is_empty() && self.write_buf.is_empty() { - if self.flags.contains(Flags::STARTED) { + if this.state.is_empty() && this.write_buf.is_empty() { + if this.flags.contains(Flags::STARTED) { trace!("Keep-alive timeout, close connection"); - self.flags.insert(Flags::SHUTDOWN); + this.flags.insert(Flags::SHUTDOWN); // start shutdown timer if let Some(deadline) = - self.codec.config().client_disconnect_timer() + this.codec.config().client_disconnect_timer() { - if let Some(mut timer) = self.ka_timer.as_mut() { + if let Some(mut timer) = this.ka_timer.as_mut() { timer.reset(deadline); let _ = Pin::new(&mut timer).poll(cx); } } else { // no shutdown timeout, drop socket - self.flags.insert(Flags::WRITE_DISCONNECT); + this.flags.insert(Flags::WRITE_DISCONNECT); return Ok(()); } } else { // timeout on first request (slow request) return 408 - if !self.flags.contains(Flags::STARTED) { + if !this.flags.contains(Flags::STARTED) { trace!("Slow request timeout"); - let _ = self.send_response( + let _ = self.as_mut().send_response( Response::RequestTimeout().finish().drop_body(), ResponseBody::Other(Body::Empty), ); + this = self.as_mut().project(); } else { trace!("Keep-alive connection timeout"); } - self.flags.insert(Flags::STARTED | Flags::SHUTDOWN); - self.state = State::None; + this.flags.insert(Flags::STARTED | Flags::SHUTDOWN); + this.state.set(State::None); } } else if let Some(deadline) = - self.codec.config().keep_alive_expire() + this.codec.config().keep_alive_expire() { - if let Some(mut timer) = self.ka_timer.as_mut() { + if let Some(mut timer) = this.ka_timer.as_mut() { timer.reset(deadline); let _ = Pin::new(&mut timer).poll(cx); } } - } else if let Some(mut timer) = self.ka_timer.as_mut() { - timer.reset(self.ka_expire); + } else if let Some(mut timer) = this.ka_timer.as_mut() { + timer.reset(*this.ka_expire); let _ = Pin::new(&mut timer).poll(cx); } } @@ -696,22 +736,25 @@ where { type Output = Result<(), DispatchError>; + #[pin_project::project] #[inline] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.as_mut().inner { - DispatcherState::Normal(ref mut inner) => { - inner.poll_keepalive(cx)?; + let this = self.as_mut().project(); + #[project] + match this.inner.project() { + DispatcherState::Normal(mut inner) => { + inner.as_mut().poll_keepalive(cx)?; if inner.flags.contains(Flags::SHUTDOWN) { if inner.flags.contains(Flags::WRITE_DISCONNECT) { Poll::Ready(Ok(())) } else { // flush buffer - inner.poll_flush(cx)?; + inner.as_mut().poll_flush(cx)?; if !inner.write_buf.is_empty() { Poll::Pending } else { - match Pin::new(&mut inner.io).poll_shutdown(cx) { + match Pin::new(inner.project().io).poll_shutdown(cx) { Poll::Ready(res) => { Poll::Ready(res.map_err(DispatchError::from)) } @@ -723,33 +766,34 @@ where // read socket into a buf let should_disconnect = if !inner.flags.contains(Flags::READ_DISCONNECT) { - read_available(cx, &mut inner.io, &mut inner.read_buf)? + let mut inner_p = inner.as_mut().project(); + read_available(cx, &mut inner_p.io, &mut inner_p.read_buf)? } else { None }; - inner.poll_request(cx)?; + inner.as_mut().poll_request(cx)?; if let Some(true) = should_disconnect { - inner.flags.insert(Flags::READ_DISCONNECT); - if let Some(mut payload) = inner.payload.take() { + let inner_p = inner.as_mut().project(); + inner_p.flags.insert(Flags::READ_DISCONNECT); + if let Some(mut payload) = inner_p.payload.take() { payload.feed_eof(); } }; loop { + let inner_p = inner.as_mut().project(); let remaining = - inner.write_buf.capacity() - inner.write_buf.len(); + inner_p.write_buf.capacity() - inner_p.write_buf.len(); if remaining < LW_BUFFER_SIZE { - inner.write_buf.reserve(HW_BUFFER_SIZE - remaining); + inner_p.write_buf.reserve(HW_BUFFER_SIZE - remaining); } - let result = inner.poll_response(cx)?; + let result = inner.as_mut().poll_response(cx)?; let drain = result == PollResponse::DrainWriteBuf; // switch to upgrade handler if let PollResponse::Upgrade(req) = result { - if let DispatcherState::Normal(inner) = - std::mem::replace(&mut self.inner, DispatcherState::None) - { + if let DispatcherState::Normal(inner) = self.as_mut().project().inner.take() { let mut parts = FramedParts::with_read_buf( inner.io, inner.codec, @@ -757,9 +801,8 @@ where ); parts.write_buf = inner.write_buf; let framed = Framed::from_parts(parts); - self.inner = DispatcherState::Upgrade( - Box::pin(inner.upgrade.unwrap().call((req, framed))), - ); + let upgrade = inner.upgrade.unwrap().call((req, framed)); + self.as_mut().project().inner.set(DispatcherState::Upgrade(upgrade)); return self.poll(cx); } else { panic!() @@ -769,7 +812,7 @@ where // we didnt get WouldBlock from write operation, // so data get written to kernel completely (OSX) // and we have to write again otherwise response can get stuck - if inner.poll_flush(cx)? || !drain { + if inner.as_mut().poll_flush(cx)? || !drain { break; } } @@ -781,25 +824,26 @@ where let is_empty = inner.state.is_empty(); + let inner_p = inner.as_mut().project(); // read half is closed and we do not processing any responses - if inner.flags.contains(Flags::READ_DISCONNECT) && is_empty { - inner.flags.insert(Flags::SHUTDOWN); + if inner_p.flags.contains(Flags::READ_DISCONNECT) && is_empty { + inner_p.flags.insert(Flags::SHUTDOWN); } // keep-alive and stream errors - if is_empty && inner.write_buf.is_empty() { - if let Some(err) = inner.error.take() { + if is_empty && inner_p.write_buf.is_empty() { + if let Some(err) = inner_p.error.take() { Poll::Ready(Err(err)) } // disconnect if keep-alive is not enabled - else if inner.flags.contains(Flags::STARTED) - && !inner.flags.intersects(Flags::KEEPALIVE) + else if inner_p.flags.contains(Flags::STARTED) + && !inner_p.flags.intersects(Flags::KEEPALIVE) { - inner.flags.insert(Flags::SHUTDOWN); + inner_p.flags.insert(Flags::SHUTDOWN); self.poll(cx) } // disconnect if shutdown - else if inner.flags.contains(Flags::SHUTDOWN) { + else if inner_p.flags.contains(Flags::SHUTDOWN) { self.poll(cx) } else { Poll::Pending diff --git a/actix-http/src/h1/utils.rs b/actix-http/src/h1/utils.rs index be6a42793..89013129a 100644 --- a/actix-http/src/h1/utils.rs +++ b/actix-http/src/h1/utils.rs @@ -36,7 +36,7 @@ where impl Future for SendResponse where T: AsyncRead + AsyncWrite, - B: MessageBody, + B: MessageBody + Unpin, { type Output = Result, Error>; From 69dab0063c27bf4ac5b684884d787e7772da1c50 Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Mon, 10 Feb 2020 12:12:23 +0200 Subject: [PATCH 036/157] Get rid of one more unsafe --- actix-http/src/h1/dispatcher.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 7429c50f7..6e226a30d 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -853,8 +853,8 @@ where } } } - DispatcherState::Upgrade(ref mut fut) => { - fut.as_mut().poll(cx).map_err(|e| { + DispatcherState::Upgrade(fut) => { + fut.poll(cx).map_err(|e| { error!("Upgrade handler error: {}", e); DispatchError::Upgrade }) From c05f9475c547bdc09049a6e939e2b0acac3c0a0c Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Mon, 10 Feb 2020 13:17:38 +0200 Subject: [PATCH 037/157] refactor dispatcher to avoid possible UB with DispatcherState Pin --- actix-http/src/h1/dispatcher.rs | 62 +++++++++++---------------------- 1 file changed, 21 insertions(+), 41 deletions(-) diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 6e226a30d..043271cb5 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -71,7 +71,6 @@ where { Normal(#[pin] InnerDispatcher), Upgrade(#[pin] U::Future), - None, } #[pin_project] @@ -101,7 +100,7 @@ where ka_expire: Instant, ka_timer: Option, - io: T, + io: Option, read_buf: BytesMut, write_buf: BytesMut, codec: Codec, @@ -148,22 +147,6 @@ where } } } - -impl DispatcherState -where - S: Service, - S::Error: Into, - B: MessageBody, - X: Service, - X::Error: Into, - U: Service), Response = ()>, - U::Error: fmt::Display, -{ - fn take(self: Pin<&mut Self>) -> Self { - std::mem::replace(unsafe { self.get_unchecked_mut() }, Self::None) - } -} - enum PollResponse { Upgrade(Request), DoNothing, @@ -258,7 +241,7 @@ where state: State::None, error: None, messages: VecDeque::new(), - io, + io: Some(io), codec, read_buf, service, @@ -322,9 +305,10 @@ where let len = self.write_buf.len(); let mut written = 0; #[project] - let InnerDispatcher { mut io, write_buf, .. } = self.project(); + let InnerDispatcher { io, write_buf, .. } = self.project(); + let mut io = Pin::new(io.as_mut().unwrap()); while written < len { - match Pin::new(&mut io).poll_write(cx, &write_buf[written..]) + match io.as_mut().poll_write(cx, &write_buf[written..]) { Poll::Ready(Ok(0)) => { return Err(DispatchError::Io(io::Error::new( @@ -751,10 +735,10 @@ where } else { // flush buffer inner.as_mut().poll_flush(cx)?; - if !inner.write_buf.is_empty() { + if !inner.write_buf.is_empty() || inner.io.is_none() { Poll::Pending } else { - match Pin::new(inner.project().io).poll_shutdown(cx) { + match Pin::new(inner.project().io).as_pin_mut().unwrap().poll_shutdown(cx) { Poll::Ready(res) => { Poll::Ready(res.map_err(DispatchError::from)) } @@ -767,7 +751,7 @@ where let should_disconnect = if !inner.flags.contains(Flags::READ_DISCONNECT) { let mut inner_p = inner.as_mut().project(); - read_available(cx, &mut inner_p.io, &mut inner_p.read_buf)? + read_available(cx, inner_p.io.as_mut().unwrap(), &mut inner_p.read_buf)? } else { None }; @@ -793,20 +777,17 @@ where // switch to upgrade handler if let PollResponse::Upgrade(req) = result { - if let DispatcherState::Normal(inner) = self.as_mut().project().inner.take() { - let mut parts = FramedParts::with_read_buf( - inner.io, - inner.codec, - inner.read_buf, - ); - parts.write_buf = inner.write_buf; - let framed = Framed::from_parts(parts); - let upgrade = inner.upgrade.unwrap().call((req, framed)); - self.as_mut().project().inner.set(DispatcherState::Upgrade(upgrade)); - return self.poll(cx); - } else { - panic!() - } + let inner_p = inner.as_mut().project(); + let mut parts = FramedParts::with_read_buf( + inner_p.io.take().unwrap(), + std::mem::take(inner_p.codec), + std::mem::take(inner_p.read_buf), + ); + parts.write_buf = std::mem::take(inner_p.write_buf); + let framed = Framed::from_parts(parts); + let upgrade = inner_p.upgrade.take().unwrap().call((req, framed)); + self.as_mut().project().inner.set(DispatcherState::Upgrade(upgrade)); + return self.poll(cx); } // we didnt get WouldBlock from write operation, @@ -859,7 +840,6 @@ where DispatchError::Upgrade }) } - DispatcherState::None => panic!(), } } } @@ -949,9 +929,9 @@ mod tests { Poll::Ready(res) => assert!(res.is_err()), } - if let DispatcherState::Normal(ref inner) = h1.inner { + if let DispatcherState::Normal(ref mut inner) = h1.inner { assert!(inner.flags.contains(Flags::READ_DISCONNECT)); - assert_eq!(&inner.io.write_buf[..26], b"HTTP/1.1 400 Bad Request\r\n"); + assert_eq!(&inner.io.take().unwrap().write_buf[..26], b"HTTP/1.1 400 Bad Request\r\n"); } }) .await; From a84b37199af18d198f4a28c559a932804c8c92e7 Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Mon, 10 Feb 2020 15:06:11 +0200 Subject: [PATCH 038/157] Add Unpin to Body to get rid of unsafe in MessageBody --- actix-http/src/body.rs | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index 26134723d..912f22e33 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -59,21 +59,10 @@ impl MessageBody for Box { } fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { - unsafe { self.map_unchecked_mut(|boxed| boxed.as_mut()) }.poll_next(cx) + Pin::new(self.get_mut().as_mut()).poll_next(cx) } } -impl MessageBody for Box { - fn size(&self) -> BodySize { - self.as_ref().size() - } - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { - unsafe { Pin::new_unchecked(self.get_mut().as_mut()) }.poll_next(cx) - } -} - - #[pin_project] pub enum ResponseBody { Body(#[pin] B), @@ -149,7 +138,7 @@ pub enum Body { /// Specific response body. Bytes(Bytes), /// Generic message body. - Message(#[pin] Box), + Message(Box), } impl Body { @@ -159,7 +148,7 @@ impl Body { } /// Create body from generic message body. - pub fn from_message(body: B) -> Body { + pub fn from_message(body: B) -> Body { Body::Message(Box::new(body)) } } @@ -188,7 +177,7 @@ impl MessageBody for Body { Poll::Ready(Some(Ok(mem::replace(bin, Bytes::new())))) } } - Body::Message(body) => body.poll_next(cx), + Body::Message(ref mut body) => Pin::new(body.as_mut()).poll_next(cx), } } } From e6078bf79272e4c1bfbc0493431d1b356d883fac Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Mon, 10 Feb 2020 15:19:56 +0200 Subject: [PATCH 039/157] Fix EncoderBody enum to align with Body::Message --- actix-http/src/encoding/encoder.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index 6530609e1..8a075e8b7 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -83,7 +83,7 @@ impl Encoder { enum EncoderBody { Bytes(Bytes), Stream(#[pin] B), - BoxedStream(#[pin] Box), + BoxedStream(Box), } impl MessageBody for EncoderBody { @@ -107,7 +107,7 @@ impl MessageBody for EncoderBody { } } EncoderBody::Stream(b) => b.poll_next(cx), - EncoderBody::BoxedStream(b) => b.poll_next(cx), + EncoderBody::BoxedStream(ref mut b) => Pin::new(b.as_mut()).poll_next(cx), } } } From de815dd99cfd95e2759080a7d27535b70b6544b4 Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Mon, 10 Feb 2020 16:19:48 +0200 Subject: [PATCH 040/157] Fixed condition for finishing transfer of response --- actix-http/src/h1/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-http/src/h1/utils.rs b/actix-http/src/h1/utils.rs index 89013129a..c44925c7a 100644 --- a/actix-http/src/h1/utils.rs +++ b/actix-http/src/h1/utils.rs @@ -86,7 +86,7 @@ where continue; } - if body_done { + if !body_done { if body_ready { continue; } else { From 78749a4b7e81fe2f37f4f84469d83c43264a0b08 Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Sat, 15 Feb 2020 17:26:46 +0200 Subject: [PATCH 041/157] rollback actix-http version change --- Cargo.toml | 2 +- actix-http/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 550df49dc..af400b7b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -108,7 +108,7 @@ codegen-units = 1 [patch.crates-io] actix-web = { path = "." } -actix-http = { path = "actix-http" } +#actix-http = { path = "actix-http" } actix-http-test = { path = "test-server" } actix-web-codegen = { path = "actix-web-codegen" } actix-cors = { path = "actix-cors" } diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index dfb467d40..212129331 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "2.0.0-alpha" +version = "1.0.1" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" From 0a86907dd228f8267940b67ad1b9883474f47625 Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Sun, 16 Feb 2020 19:48:09 +0200 Subject: [PATCH 042/157] use mem::replace instead of mem::take rust 1.40+ --- actix-http/src/h1/dispatcher.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 043271cb5..4bfcabab8 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -780,10 +780,10 @@ where let inner_p = inner.as_mut().project(); let mut parts = FramedParts::with_read_buf( inner_p.io.take().unwrap(), - std::mem::take(inner_p.codec), - std::mem::take(inner_p.read_buf), + std::mem::replace(inner_p.codec, Codec::default()), + std::mem::replace(inner_p.read_buf, BytesMut::default()), ); - parts.write_buf = std::mem::take(inner_p.write_buf); + parts.write_buf = std::mem::replace(inner_p.write_buf, BytesMut::default()); let framed = Framed::from_parts(parts); let upgrade = inner_p.upgrade.take().unwrap().call((req, framed)); self.as_mut().project().inner.set(DispatcherState::Upgrade(upgrade)); From e5f2feec45147321b05ece99a049aa4eb1b52025 Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Tue, 18 Feb 2020 20:28:45 +0200 Subject: [PATCH 043/157] reenable actix-http from local path --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index af400b7b4..550df49dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -108,7 +108,7 @@ codegen-units = 1 [patch.crates-io] actix-web = { path = "." } -#actix-http = { path = "actix-http" } +actix-http = { path = "actix-http" } actix-http-test = { path = "test-server" } actix-web-codegen = { path = "actix-web-codegen" } actix-cors = { path = "actix-cors" } From 77058ef779449ad53a2ed9dd5b62b8c718b2bed3 Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Tue, 18 Feb 2020 20:48:37 +0200 Subject: [PATCH 044/157] adopt MessageBody Pin changes to actix-web root --- src/middleware/logger.rs | 17 ++++++++++++----- src/test.rs | 6 +++--- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index d692132ce..e40fe648a 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -238,15 +238,20 @@ where } } +use pin_project::{pin_project, pinned_drop}; + +#[pin_project(PinnedDrop)] pub struct StreamLog { + #[pin] body: ResponseBody, format: Option, size: usize, time: OffsetDateTime, } -impl Drop for StreamLog { - fn drop(&mut self) { +#[pinned_drop] +impl PinnedDrop for StreamLog { + fn drop(self: Pin<&mut Self>) { if let Some(ref format) = self.format { let render = |fmt: &mut Formatter<'_>| { for unit in &format.0 { @@ -259,15 +264,17 @@ impl Drop for StreamLog { } } + impl MessageBody for StreamLog { fn size(&self) -> BodySize { self.body.size() } - fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>> { - match self.body.poll_next(cx) { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + let this = self.project(); + match this.body.poll_next(cx) { Poll::Ready(Some(Ok(chunk))) => { - self.size += chunk.len(); + *this.size += chunk.len(); Poll::Ready(Some(Ok(chunk))) } val => val, diff --git a/src/test.rs b/src/test.rs index 6a6ef27c5..0eb02ff7c 100644 --- a/src/test.rs +++ b/src/test.rs @@ -150,7 +150,7 @@ where pub async fn read_response(app: &mut S, req: Request) -> Bytes where S: Service, Error = Error>, - B: MessageBody, + B: MessageBody + Unpin, { let mut resp = app .call(req) @@ -193,7 +193,7 @@ where /// ``` pub async fn read_body(mut res: ServiceResponse) -> Bytes where - B: MessageBody, + B: MessageBody + Unpin, { let mut body = res.take_body(); let mut bytes = BytesMut::new(); @@ -251,7 +251,7 @@ where pub async fn read_response_json(app: &mut S, req: Request) -> T where S: Service, Error = Error>, - B: MessageBody, + B: MessageBody + Unpin, T: DeserializeOwned, { let body = read_response(app, req).await; From ea28219d0fd6b7d3b97c84947c2c80ecebe1383f Mon Sep 17 00:00:00 2001 From: Maksym Vorobiov Date: Tue, 18 Feb 2020 23:04:47 +0200 Subject: [PATCH 045/157] reenable actix-http test-ws --- actix-http/tests/test_ws.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/actix-http/tests/test_ws.rs b/actix-http/tests/test_ws.rs index 2f2a28e2f..7152fee48 100644 --- a/actix-http/tests/test_ws.rs +++ b/actix-http/tests/test_ws.rs @@ -81,9 +81,6 @@ async fn service(msg: ws::Frame) -> Result { Ok(msg) } -/* -Temporarily commented out due to dependency on actix-http-test - #[actix_rt::test] async fn test_simple() { let ws_service = WsService::new(); @@ -195,5 +192,3 @@ async fn test_simple() { assert!(ws_service.was_polled()); } - -*/ \ No newline at end of file From cd1765035cf5fec4f83bb0eb84ccdfc4d5b479a5 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 26 Feb 2020 09:41:15 +0900 Subject: [PATCH 046/157] Avoid re-definition --- actix-http/src/error.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 8a1c1b5dc..4b8f13cf0 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -59,12 +59,6 @@ impl Error { } } -/// A struct with a private constructor, for use with -/// `__private_get_type_id__`. Its single field is private, -/// ensuring that it can only be constructed from this module -#[doc(hidden)] -pub struct PrivateHelper(()); - /// Error that can be converted to `Response` pub trait ResponseError: fmt::Debug + fmt::Display { /// Response's status code From d3ccf46e9216a86cbac9777c3a88b8f7ab8a35e5 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 27 Feb 2020 09:53:27 +0900 Subject: [PATCH 047/157] Clean-up metadata --- Cargo.toml | 2 +- actix-identity/Cargo.toml | 2 +- awc/Cargo.toml | 2 +- test-server/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 550df49dc..73d068966 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,7 +71,7 @@ actix-threadpool = "0.3.1" actix-tls = "1.0.0" actix-web-codegen = "0.2.0" -actix-http = { version = "1.0.1" } +actix-http = "1.0.1" awc = { version = "1.0.1", default-features = false } bytes = "0.5.3" diff --git a/actix-identity/Cargo.toml b/actix-identity/Cargo.toml index 910aef48e..f97b66291 100644 --- a/actix-identity/Cargo.toml +++ b/actix-identity/Cargo.toml @@ -25,5 +25,5 @@ time = { version = "0.2.5", default-features = false, features = ["std"] } [dev-dependencies] actix-rt = "1.0.0" -actix-http = { version = "1.0.1" } +actix-http = "1.0.1" bytes = "0.5.4" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index f7d5634e0..37392d28e 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -36,7 +36,7 @@ compress = ["actix-http/compress"] [dependencies] actix-codec = "0.2.0" actix-service = "1.0.1" -actix-http = { version = "1.0.1" } +actix-http = "1.0.1" actix-rt = "1.0.0" base64 = "0.11" diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index f4ec1e238..a458fb341 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -37,7 +37,7 @@ actix-utils = "1.0.3" actix-rt = "1.0.0" actix-server = "1.0.0" actix-testing = "1.0.0" -awc = { version = "1.0.1" } +awc = "1.0.1" base64 = "0.11" bytes = "0.5.3" From 903ae47baa99999a68d8b74897e12d2336cac105 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 27 Feb 2020 11:08:45 +0900 Subject: [PATCH 048/157] dev-deps: Update env_logger to 0.7 --- actix-http/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 212129331..b87494627 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -99,7 +99,7 @@ actix-http-test = { version = "1.0.0", features=["openssl"] } actix-tls = { version = "1.0.0", features=["openssl"] } criterion = "0.3" futures = "0.3.1" -env_logger = "0.6" +env_logger = "0.7" serde_derive = "1.0" open-ssl = { version="0.10", package = "openssl" } rust-tls = { version="0.16", package = "rustls" } From 7ba14fd113406034b9b7843bac184d74800fe1a7 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 27 Feb 2020 11:10:55 +0900 Subject: [PATCH 049/157] Run rustfmt --- actix-http/examples/echo.rs | 27 ++--- actix-http/src/body.rs | 109 +++++++++++++++----- actix-http/src/client/h1proto.rs | 2 +- actix-http/src/client/pool.rs | 2 +- actix-http/src/config.rs | 12 ++- actix-http/src/cookie/builder.rs | 3 +- actix-http/src/cookie/jar.rs | 2 +- actix-http/src/cookie/mod.rs | 4 +- actix-http/src/cookie/parse.rs | 21 +++- actix-http/src/encoding/encoder.rs | 15 ++- actix-http/src/error.rs | 4 +- actix-http/src/h1/dispatcher.rs | 123 +++++++++++++---------- actix-http/src/h2/dispatcher.rs | 99 +++++++++--------- actix-http/src/h2/service.rs | 12 +-- actix-http/src/header/shared/httpdate.rs | 22 ++-- actix-http/src/macros.rs | 2 +- actix-http/src/time_parser.rs | 6 +- actix-http/tests/test_openssl.rs | 8 +- actix-http/tests/test_rustls.rs | 8 +- 19 files changed, 284 insertions(+), 197 deletions(-) diff --git a/actix-http/examples/echo.rs b/actix-http/examples/echo.rs index 3d57a472a..b2b88a7ea 100644 --- a/actix-http/examples/echo.rs +++ b/actix-http/examples/echo.rs @@ -17,23 +17,18 @@ async fn main() -> io::Result<()> { HttpService::build() .client_timeout(1000) .client_disconnect(1000) - .finish(|mut req: Request| { - async move { - let mut body = BytesMut::new(); - while let Some(item) = req.payload().next().await { - body.extend_from_slice(&item?); - } - - info!("request body: {:?}", body); - Ok::<_, Error>( - Response::Ok() - .header( - "x-head", - HeaderValue::from_static("dummy value!"), - ) - .body(body), - ) + .finish(|mut req: Request| async move { + let mut body = BytesMut::new(); + while let Some(item) = req.payload().next().await { + body.extend_from_slice(&item?); } + + info!("request body: {:?}", body); + Ok::<_, Error>( + Response::Ok() + .header("x-head", HeaderValue::from_static("dummy value!")) + .body(body), + ) }) .tcp() })? diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index 912f22e33..c581db604 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -36,7 +36,10 @@ impl BodySize { pub trait MessageBody { fn size(&self) -> BodySize; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>>; + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>>; downcast_get_type_id!(); } @@ -48,7 +51,10 @@ impl MessageBody for () { BodySize::Empty } - fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll>> { + fn poll_next( + self: Pin<&mut Self>, + _: &mut Context<'_>, + ) -> Poll>> { Poll::Ready(None) } } @@ -58,7 +64,10 @@ impl MessageBody for Box { self.as_ref().size() } - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { Pin::new(self.get_mut().as_mut()).poll_next(cx) } } @@ -103,7 +112,10 @@ impl MessageBody for ResponseBody { } #[project] - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { #[project] match self.project() { ResponseBody::Body(body) => body.poll_next(cx), @@ -164,7 +176,10 @@ impl MessageBody for Body { } #[project] - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { #[project] match self.project() { Body::None => Poll::Ready(None), @@ -285,7 +300,10 @@ impl MessageBody for Bytes { BodySize::Sized(self.len()) } - fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll>> { + fn poll_next( + self: Pin<&mut Self>, + _: &mut Context<'_>, + ) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { @@ -299,11 +317,16 @@ impl MessageBody for BytesMut { BodySize::Sized(self.len()) } - fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll>> { + fn poll_next( + self: Pin<&mut Self>, + _: &mut Context<'_>, + ) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { - Poll::Ready(Some(Ok(mem::replace(self.get_mut(), BytesMut::new()).freeze()))) + Poll::Ready(Some(Ok( + mem::replace(self.get_mut(), BytesMut::new()).freeze() + ))) } } } @@ -313,7 +336,10 @@ impl MessageBody for &'static str { BodySize::Sized(self.len()) } - fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll>> { + fn poll_next( + self: Pin<&mut Self>, + _: &mut Context<'_>, + ) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { @@ -329,11 +355,17 @@ impl MessageBody for Vec { BodySize::Sized(self.len()) } - fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll>> { + fn poll_next( + self: Pin<&mut Self>, + _: &mut Context<'_>, + ) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { - Poll::Ready(Some(Ok(Bytes::from(mem::replace(self.get_mut(), Vec::new()))))) + Poll::Ready(Some(Ok(Bytes::from(mem::replace( + self.get_mut(), + Vec::new(), + ))))) } } } @@ -343,7 +375,10 @@ impl MessageBody for String { BodySize::Sized(self.len()) } - fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll>> { + fn poll_next( + self: Pin<&mut Self>, + _: &mut Context<'_>, + ) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { @@ -390,7 +425,10 @@ where /// Empty values are skipped to prevent [`BodyStream`]'s transmission being /// ended on a zero-length chunk, but rather proceed until the underlying /// [`Stream`] ends. - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { let mut stream = self.project().stream; loop { let stream = stream.as_mut(); @@ -433,7 +471,10 @@ where /// Empty values are skipped to prevent [`SizedStream`]'s transmission being /// ended on a zero-length chunk, but rather proceed until the underlying /// [`Stream`] ends. - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { let mut stream: Pin<&mut S> = self.project().stream; loop { let stream = stream.as_mut(); @@ -478,7 +519,10 @@ mod tests { assert_eq!("test".size(), BodySize::Sized(4)); assert_eq!( - poll_fn(|cx| Pin::new(&mut "test").poll_next(cx)).await.unwrap().ok(), + poll_fn(|cx| Pin::new(&mut "test").poll_next(cx)) + .await + .unwrap() + .ok(), Some(Bytes::from("test")) ); } @@ -497,10 +541,7 @@ mod tests { assert_eq!(sb.size(), BodySize::Sized(4)); assert_eq!( - poll_fn(|cx| sb.as_mut().poll_next(cx)) - .await - .unwrap() - .ok(), + poll_fn(|cx| sb.as_mut().poll_next(cx)).await.unwrap().ok(), Some(Bytes::from("test")) ); } @@ -535,7 +576,7 @@ mod tests { Some(Bytes::from("test")) ); } - + #[actix_rt::test] async fn test_bytes_mut() { let b = BytesMut::from("test"); @@ -569,7 +610,9 @@ mod tests { #[actix_rt::test] async fn test_unit() { assert_eq!(().size(), BodySize::Empty); - assert!(poll_fn(|cx| Pin::new(&mut ()).poll_next(cx)).await.is_none()); + assert!(poll_fn(|cx| Pin::new(&mut ()).poll_next(cx)) + .await + .is_none()); } #[actix_rt::test] @@ -628,11 +671,17 @@ mod tests { pin_mut!(body); assert_eq!( - poll_fn(|cx| body.as_mut().poll_next(cx)).await.unwrap().ok(), + poll_fn(|cx| body.as_mut().poll_next(cx)) + .await + .unwrap() + .ok(), Some(Bytes::from("1")), ); assert_eq!( - poll_fn(|cx| body.as_mut().poll_next(cx)).await.unwrap().ok(), + poll_fn(|cx| body.as_mut().poll_next(cx)) + .await + .unwrap() + .ok(), Some(Bytes::from("2")), ); } @@ -648,14 +697,14 @@ mod tests { let _z = **y; Ok::<_, ()>(Bytes::new()) }))); - + let waker = noop_waker(); let mut context = Context::from_waker(&waker); pin_mut!(body_stream); let _ = body_stream.as_mut().unwrap().poll_next(&mut context); sender.send(()).unwrap(); - let _ = std::mem::replace(&mut body_stream, Err([0; 32])).unwrap().poll_next(&mut context); + let _ = std::mem::replace(&mut body_stream, Err([0; 32])).unwrap().poll_next(&mut context); }*/ } @@ -670,11 +719,17 @@ mod tests { ); pin_mut!(body); assert_eq!( - poll_fn(|cx| body.as_mut().poll_next(cx)).await.unwrap().ok(), + poll_fn(|cx| body.as_mut().poll_next(cx)) + .await + .unwrap() + .ok(), Some(Bytes::from("1")), ); assert_eq!( - poll_fn(|cx| body.as_mut().poll_next(cx)).await.unwrap().ok(), + poll_fn(|cx| body.as_mut().poll_next(cx)) + .await + .unwrap() + .ok(), Some(Bytes::from("2")), ); } diff --git a/actix-http/src/client/h1proto.rs b/actix-http/src/client/h1proto.rs index c1863b920..51e853b3d 100644 --- a/actix-http/src/client/h1proto.rs +++ b/actix-http/src/client/h1proto.rs @@ -8,7 +8,7 @@ use bytes::buf::BufMutExt; use bytes::{Bytes, BytesMut}; use futures_core::Stream; use futures_util::future::poll_fn; -use futures_util::{SinkExt, StreamExt, pin_mut}; +use futures_util::{pin_mut, SinkExt, StreamExt}; use crate::error::PayloadError; use crate::h1; diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index 139cf9f66..38a51b558 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -16,8 +16,8 @@ use fxhash::FxHashMap; use h2::client::{handshake, Connection, SendRequest}; use http::uri::Authority; use indexmap::IndexSet; -use slab::Slab; use pin_project::pin_project; +use slab::Slab; use super::connection::{ConnectionType, IoConnection}; use super::error::ConnectError; diff --git a/actix-http/src/config.rs b/actix-http/src/config.rs index a38a80e76..899046231 100644 --- a/actix-http/src/config.rs +++ b/actix-http/src/config.rs @@ -211,7 +211,12 @@ impl Date { } fn update(&mut self) { self.pos = 0; - write!(self, "{}", OffsetDateTime::now().format("%a, %d %b %Y %H:%M:%S GMT")).unwrap(); + write!( + self, + "{}", + OffsetDateTime::now().format("%a, %d %b %Y %H:%M:%S GMT") + ) + .unwrap(); } } @@ -282,7 +287,6 @@ impl DateService { mod tests { use super::*; - // Test modifying the date from within the closure // passed to `set_date` #[test] @@ -290,9 +294,7 @@ mod tests { let service = DateService::new(); // Make sure that `check_date` doesn't try to spawn a task service.0.update(); - service.set_date(|_| { - service.0.reset() - }); + service.set_date(|_| service.0.reset()); } #[test] diff --git a/actix-http/src/cookie/builder.rs b/actix-http/src/cookie/builder.rs index c3820abf0..80e7ee71f 100644 --- a/actix-http/src/cookie/builder.rs +++ b/actix-http/src/cookie/builder.rs @@ -109,7 +109,8 @@ impl CookieBuilder { pub fn max_age_time(mut self, value: Duration) -> CookieBuilder { // Truncate any nanoseconds from the Duration, as they aren't represented within `Max-Age` // and would cause two otherwise identical `Cookie` instances to not be equivalent to one another. - self.cookie.set_max_age(Duration::seconds(value.whole_seconds())); + self.cookie + .set_max_age(Duration::seconds(value.whole_seconds())); self } diff --git a/actix-http/src/cookie/jar.rs b/actix-http/src/cookie/jar.rs index 64922897b..dd4ec477e 100644 --- a/actix-http/src/cookie/jar.rs +++ b/actix-http/src/cookie/jar.rs @@ -533,8 +533,8 @@ mod test { #[test] #[cfg(feature = "secure-cookies")] fn delta() { - use time::Duration; use std::collections::HashMap; + use time::Duration; let mut c = CookieJar::new(); diff --git a/actix-http/src/cookie/mod.rs b/actix-http/src/cookie/mod.rs index 8dccd0b6d..360d80883 100644 --- a/actix-http/src/cookie/mod.rs +++ b/actix-http/src/cookie/mod.rs @@ -1015,7 +1015,9 @@ mod tests { assert_eq!(&cookie.to_string(), "foo=bar; Domain=www.rust-lang.org"); let time_str = "Wed, 21 Oct 2015 07:28:00 GMT"; - let expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%M:%S").unwrap().assume_utc(); + let expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%M:%S") + .unwrap() + .assume_utc(); let cookie = Cookie::build("foo", "bar").expires(expires).finish(); assert_eq!( &cookie.to_string(), diff --git a/actix-http/src/cookie/parse.rs b/actix-http/src/cookie/parse.rs index 537069de3..ce261c758 100644 --- a/actix-http/src/cookie/parse.rs +++ b/actix-http/src/cookie/parse.rs @@ -376,7 +376,9 @@ mod tests { ); let time_str = "Wed, 21 Oct 2015 07:28:00 GMT"; - let expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%M:%S").unwrap().assume_utc(); + let expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%M:%S") + .unwrap() + .assume_utc(); expected.set_expires(expires); assert_eq_parse!( " foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \ @@ -385,7 +387,9 @@ mod tests { ); unexpected.set_domain("foo.com"); - let bad_expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%S:%M").unwrap().assume_utc(); + let bad_expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%S:%M") + .unwrap() + .assume_utc(); expected.set_expires(bad_expires); assert_ne_parse!( " foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \ @@ -414,8 +418,15 @@ mod tests { #[test] fn do_not_panic_on_large_max_ages() { let max_duration = Duration::max_value(); - let expected = Cookie::build("foo", "bar").max_age_time(max_duration).finish(); - let overflow_duration = max_duration.checked_add(Duration::nanoseconds(1)).unwrap_or(max_duration); - assert_eq_parse!(format!(" foo=bar; Max-Age={:?}", overflow_duration.whole_seconds()), expected); + let expected = Cookie::build("foo", "bar") + .max_age_time(max_duration) + .finish(); + let overflow_duration = max_duration + .checked_add(Duration::nanoseconds(1)) + .unwrap_or(max_duration); + assert_eq_parse!( + format!(" foo=bar; Max-Age={:?}", overflow_duration.whole_seconds()), + expected + ); } } diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index 8a075e8b7..72bb7d603 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -94,9 +94,12 @@ impl MessageBody for EncoderBody { EncoderBody::BoxedStream(ref b) => b.size(), } } - + #[project] - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { #[project] match self.project() { EncoderBody::Bytes(b) => { @@ -112,7 +115,6 @@ impl MessageBody for EncoderBody { } } - impl MessageBody for Encoder { fn size(&self) -> BodySize { if self.encoder.is_none() { @@ -121,8 +123,11 @@ impl MessageBody for Encoder { BodySize::Stream } } - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { let mut this = self.project(); loop { if *this.eof { diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 4b8f13cf0..b13481551 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -954,12 +954,12 @@ impl ResponseError for fail_ure::Error {} #[cfg(feature = "actors")] /// `InternalServerError` for `actix::MailboxError` /// This is supported on feature=`actors` only -impl ResponseError for actix::MailboxError {} +impl ResponseError for actix::MailboxError {} #[cfg(feature = "actors")] /// `InternalServerError` for `actix::ResolverError` /// This is supported on feature=`actors` only -impl ResponseError for actix::actors::resolver::ResolverError {} +impl ResponseError for actix::actors::resolver::ResolverError {} #[cfg(test)] mod tests { diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 4bfcabab8..acbb09960 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -297,7 +297,10 @@ where /// true - got whouldblock /// false - didnt get whouldblock #[pin_project::project] - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Result { + fn poll_flush( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Result { if self.write_buf.is_empty() { return Ok(false); } @@ -308,8 +311,7 @@ where let InnerDispatcher { io, write_buf, .. } = self.project(); let mut io = Pin::new(io.as_mut().unwrap()); while written < len { - match io.as_mut().poll_write(cx, &write_buf[written..]) - { + match io.as_mut().poll_write(cx, &write_buf[written..]) { Poll::Ready(Ok(0)) => { return Err(DispatchError::Io(io::Error::new( io::ErrorKind::WriteZero, @@ -359,7 +361,8 @@ where } fn send_continue(self: Pin<&mut Self>) { - self.project().write_buf + self.project() + .write_buf .extend_from_slice(b"HTTP/1.1 100 Continue\r\n\r\n"); } @@ -376,47 +379,44 @@ where Some(DispatcherMessage::Item(req)) => { Some(self.as_mut().handle_request(req, cx)?) } - Some(DispatcherMessage::Error(res)) => { - Some(self.as_mut().send_response(res, ResponseBody::Other(Body::Empty))?) - } + Some(DispatcherMessage::Error(res)) => Some( + self.as_mut() + .send_response(res, ResponseBody::Other(Body::Empty))?, + ), Some(DispatcherMessage::Upgrade(req)) => { return Ok(PollResponse::Upgrade(req)); } None => None, }, - State::ExpectCall(fut) => { - match fut.poll(cx) { - Poll::Ready(Ok(req)) => { - self.as_mut().send_continue(); - this = self.as_mut().project(); - this.state.set(State::ServiceCall(this.service.call(req))); - continue; - } - Poll::Ready(Err(e)) => { - let res: Response = e.into().into(); - let (res, body) = res.replace_body(()); - Some(self.as_mut().send_response(res, body.into_body())?) - } - Poll::Pending => None, + State::ExpectCall(fut) => match fut.poll(cx) { + Poll::Ready(Ok(req)) => { + self.as_mut().send_continue(); + this = self.as_mut().project(); + this.state.set(State::ServiceCall(this.service.call(req))); + continue; } - } - State::ServiceCall(fut) => { - match fut.poll(cx) { - Poll::Ready(Ok(res)) => { - let (res, body) = res.into().replace_body(()); - let state = self.as_mut().send_response(res, body)?; - this = self.as_mut().project(); - this.state.set(state); - continue; - } - Poll::Ready(Err(e)) => { - let res: Response = e.into().into(); - let (res, body) = res.replace_body(()); - Some(self.as_mut().send_response(res, body.into_body())?) - } - Poll::Pending => None, + Poll::Ready(Err(e)) => { + let res: Response = e.into().into(); + let (res, body) = res.replace_body(()); + Some(self.as_mut().send_response(res, body.into_body())?) } - } + Poll::Pending => None, + }, + State::ServiceCall(fut) => match fut.poll(cx) { + Poll::Ready(Ok(res)) => { + let (res, body) = res.into().replace_body(()); + let state = self.as_mut().send_response(res, body)?; + this = self.as_mut().project(); + this.state.set(state); + continue; + } + Poll::Ready(Err(e)) => { + let res: Response = e.into().into(); + let (res, body) = res.replace_body(()); + Some(self.as_mut().send_response(res, body.into_body())?) + } + Poll::Pending => None, + }, State::SendPayload(mut stream) => { loop { if this.write_buf.len() < HW_BUFFER_SIZE { @@ -627,7 +627,10 @@ where } /// keep-alive timer - fn poll_keepalive(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Result<(), DispatchError> { + fn poll_keepalive( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Result<(), DispatchError> { let mut this = self.as_mut().project(); if this.ka_timer.is_none() { // shutdown timeout @@ -738,7 +741,11 @@ where if !inner.write_buf.is_empty() || inner.io.is_none() { Poll::Pending } else { - match Pin::new(inner.project().io).as_pin_mut().unwrap().poll_shutdown(cx) { + match Pin::new(inner.project().io) + .as_pin_mut() + .unwrap() + .poll_shutdown(cx) + { Poll::Ready(res) => { Poll::Ready(res.map_err(DispatchError::from)) } @@ -751,7 +758,11 @@ where let should_disconnect = if !inner.flags.contains(Flags::READ_DISCONNECT) { let mut inner_p = inner.as_mut().project(); - read_available(cx, inner_p.io.as_mut().unwrap(), &mut inner_p.read_buf)? + read_available( + cx, + inner_p.io.as_mut().unwrap(), + &mut inner_p.read_buf, + )? } else { None }; @@ -783,11 +794,18 @@ where std::mem::replace(inner_p.codec, Codec::default()), std::mem::replace(inner_p.read_buf, BytesMut::default()), ); - parts.write_buf = std::mem::replace(inner_p.write_buf, BytesMut::default()); + parts.write_buf = std::mem::replace( + inner_p.write_buf, + BytesMut::default(), + ); let framed = Framed::from_parts(parts); - let upgrade = inner_p.upgrade.take().unwrap().call((req, framed)); - self.as_mut().project().inner.set(DispatcherState::Upgrade(upgrade)); - return self.poll(cx); + let upgrade = + inner_p.upgrade.take().unwrap().call((req, framed)); + self.as_mut() + .project() + .inner + .set(DispatcherState::Upgrade(upgrade)); + return self.poll(cx); } // we didnt get WouldBlock from write operation, @@ -834,12 +852,10 @@ where } } } - DispatcherState::Upgrade(fut) => { - fut.poll(cx).map_err(|e| { - error!("Upgrade handler error: {}", e); - DispatchError::Upgrade - }) - } + DispatcherState::Upgrade(fut) => fut.poll(cx).map_err(|e| { + error!("Upgrade handler error: {}", e); + DispatchError::Upgrade + }), } } } @@ -931,7 +947,10 @@ mod tests { if let DispatcherState::Normal(ref mut inner) = h1.inner { assert!(inner.flags.contains(Flags::READ_DISCONNECT)); - assert_eq!(&inner.io.take().unwrap().write_buf[..26], b"HTTP/1.1 400 Bad Request\r\n"); + assert_eq!( + &inner.io.take().unwrap().write_buf[..26], + b"HTTP/1.1 400 Bad Request\r\n" + ); } }) .await; diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 4b3752ffe..b07764a03 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -255,63 +255,60 @@ where #[project] match this.state.project() { - ServiceResponseState::ServiceCall(call, send) => { - match call.poll(cx) { - Poll::Ready(Ok(res)) => { - let (res, body) = res.into().replace_body(()); + ServiceResponseState::ServiceCall(call, send) => match call.poll(cx) { + Poll::Ready(Ok(res)) => { + let (res, body) = res.into().replace_body(()); - let mut send = send.take().unwrap(); - let mut size = body.size(); - let h2_res = - self.as_mut().prepare_response(res.head(), &mut size); - this = self.as_mut().project(); + let mut send = send.take().unwrap(); + let mut size = body.size(); + let h2_res = self.as_mut().prepare_response(res.head(), &mut size); + this = self.as_mut().project(); - let stream = match send.send_response(h2_res, size.is_eof()) { - Err(e) => { - trace!("Error sending h2 response: {:?}", e); - return Poll::Ready(()); - } - Ok(stream) => stream, - }; - - if size.is_eof() { - Poll::Ready(()) - } else { - this.state.set(ServiceResponseState::SendPayload(stream, body)); - self.poll(cx) + let stream = match send.send_response(h2_res, size.is_eof()) { + Err(e) => { + trace!("Error sending h2 response: {:?}", e); + return Poll::Ready(()); } - } - Poll::Pending => Poll::Pending, - Poll::Ready(Err(e)) => { - let res: Response = e.into().into(); - let (res, body) = res.replace_body(()); + Ok(stream) => stream, + }; - let mut send = send.take().unwrap(); - let mut size = body.size(); - let h2_res = - self.as_mut().prepare_response(res.head(), &mut size); - this = self.as_mut().project(); - - let stream = match send.send_response(h2_res, size.is_eof()) { - Err(e) => { - trace!("Error sending h2 response: {:?}", e); - return Poll::Ready(()); - } - Ok(stream) => stream, - }; - - if size.is_eof() { - Poll::Ready(()) - } else { - this.state.set(ServiceResponseState::SendPayload( - stream, - body.into_body(), - )); - self.poll(cx) - } + if size.is_eof() { + Poll::Ready(()) + } else { + this.state + .set(ServiceResponseState::SendPayload(stream, body)); + self.poll(cx) } } - } + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => { + let res: Response = e.into().into(); + let (res, body) = res.replace_body(()); + + let mut send = send.take().unwrap(); + let mut size = body.size(); + let h2_res = self.as_mut().prepare_response(res.head(), &mut size); + this = self.as_mut().project(); + + let stream = match send.send_response(h2_res, size.is_eof()) { + Err(e) => { + trace!("Error sending h2 response: {:?}", e); + return Poll::Ready(()); + } + Ok(stream) => stream, + }; + + if size.is_eof() { + Poll::Ready(()) + } else { + this.state.set(ServiceResponseState::SendPayload( + stream, + body.into_body(), + )); + self.poll(cx) + } + } + }, ServiceResponseState::SendPayload(ref mut stream, ref mut body) => loop { loop { if let Some(ref mut buffer) = this.buffer { diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index ff3f69faf..eef5dd02c 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -83,13 +83,11 @@ where Error = DispatchError, InitError = S::InitError, > { - pipeline_factory(fn_factory(|| { - async { - Ok::<_, S::InitError>(fn_service(|io: TcpStream| { - let peer_addr = io.peer_addr().ok(); - ok::<_, DispatchError>((io, peer_addr)) - })) - } + pipeline_factory(fn_factory(|| async { + Ok::<_, S::InitError>(fn_service(|io: TcpStream| { + let peer_addr = io.peer_addr().ok(); + ok::<_, DispatchError>((io, peer_addr)) + })) })) .and_then(self) } diff --git a/actix-http/src/header/shared/httpdate.rs b/actix-http/src/header/shared/httpdate.rs index 5227118fa..81caf6d53 100644 --- a/actix-http/src/header/shared/httpdate.rs +++ b/actix-http/src/header/shared/httpdate.rs @@ -5,7 +5,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use bytes::{buf::BufMutExt, BytesMut}; use http::header::{HeaderValue, InvalidHeaderValue}; -use time::{PrimitiveDateTime, OffsetDateTime, offset}; +use time::{offset, OffsetDateTime, PrimitiveDateTime}; use crate::error::ParseError; use crate::header::IntoHeaderValue; @@ -21,7 +21,7 @@ impl FromStr for HttpDate { fn from_str(s: &str) -> Result { match time_parser::parse_http_date(s) { Some(t) => Ok(HttpDate(t.assume_utc())), - None => Err(ParseError::Header) + None => Err(ParseError::Header), } } } @@ -49,7 +49,14 @@ impl IntoHeaderValue for HttpDate { fn try_into(self) -> Result { let mut wrt = BytesMut::with_capacity(29).writer(); - write!(wrt, "{}", self.0.to_offset(offset!(UTC)).format("%a, %d %b %Y %H:%M:%S GMT")).unwrap(); + write!( + wrt, + "{}", + self.0 + .to_offset(offset!(UTC)) + .format("%a, %d %b %Y %H:%M:%S GMT") + ) + .unwrap(); HeaderValue::from_maybe_shared(wrt.get_mut().split().freeze()) } } @@ -66,14 +73,13 @@ impl From for SystemTime { #[cfg(test)] mod tests { use super::HttpDate; - use time::{PrimitiveDateTime, date, time}; + use time::{date, time, PrimitiveDateTime}; #[test] fn test_date() { - let nov_07 = HttpDate(PrimitiveDateTime::new( - date!(1994-11-07), - time!(8:48:37) - ).assume_utc()); + let nov_07 = HttpDate( + PrimitiveDateTime::new(date!(1994 - 11 - 07), time!(8:48:37)).assume_utc(), + ); assert_eq!( "Sun, 07 Nov 1994 08:48:37 GMT".parse::().unwrap(), diff --git a/actix-http/src/macros.rs b/actix-http/src/macros.rs index 0aaf1abec..b970b14f2 100644 --- a/actix-http/src/macros.rs +++ b/actix-http/src/macros.rs @@ -21,7 +21,7 @@ macro_rules! downcast_get_type_id { { (std::any::TypeId::of::(), PrivateHelper(())) } - } + }; } //Generate implementation for dyn $name diff --git a/actix-http/src/time_parser.rs b/actix-http/src/time_parser.rs index 34fac139e..b5b07ccba 100644 --- a/actix-http/src/time_parser.rs +++ b/actix-http/src/time_parser.rs @@ -1,4 +1,4 @@ -use time::{OffsetDateTime, PrimitiveDateTime, Date}; +use time::{Date, OffsetDateTime, PrimitiveDateTime}; /// Attempt to parse a `time` string as one of either RFC 1123, RFC 850, or asctime. pub fn parse_http_date(time: &str) -> Option { @@ -29,10 +29,10 @@ fn try_parse_rfc_850(time: &str) -> Option { match Date::try_from_ymd(expanded_year, dt.month(), dt.day()) { Ok(date) => Some(PrimitiveDateTime::new(date, dt.time())), - Err(_) => None + Err(_) => None, } } - Err(_) => None + Err(_) => None, } } diff --git a/actix-http/tests/test_openssl.rs b/actix-http/tests/test_openssl.rs index b25f05272..77caa045b 100644 --- a/actix-http/tests/test_openssl.rs +++ b/actix-http/tests/test_openssl.rs @@ -97,11 +97,9 @@ async fn test_h2_body() -> io::Result<()> { let data = "HELLOWORLD".to_owned().repeat(64 * 1024); let mut srv = test_server(move || { HttpService::build() - .h2(|mut req: Request<_>| { - async move { - let body = load_body(req.take_payload()).await?; - Ok::<_, Error>(Response::Ok().body(body)) - } + .h2(|mut req: Request<_>| async move { + let body = load_body(req.take_payload()).await?; + Ok::<_, Error>(Response::Ok().body(body)) }) .openssl(ssl_acceptor()) .map_err(|_| ()) diff --git a/actix-http/tests/test_rustls.rs b/actix-http/tests/test_rustls.rs index bc0c91cc3..933a6c894 100644 --- a/actix-http/tests/test_rustls.rs +++ b/actix-http/tests/test_rustls.rs @@ -104,11 +104,9 @@ async fn test_h2_body1() -> io::Result<()> { let data = "HELLOWORLD".to_owned().repeat(64 * 1024); let mut srv = test_server(move || { HttpService::build() - .h2(|mut req: Request<_>| { - async move { - let body = load_body(req.take_payload()).await?; - Ok::<_, Error>(Response::Ok().body(body)) - } + .h2(|mut req: Request<_>| async move { + let body = load_body(req.take_payload()).await?; + Ok::<_, Error>(Response::Ok().body(body)) }) .rustls(ssl_acceptor()) }); From f27dd19093014de6fed68cd3025e77f21f52a9e8 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 27 Feb 2020 11:20:30 +0900 Subject: [PATCH 050/157] Fix Clippy warnings --- actix-http/src/error.rs | 1 - actix-http/src/h1/decoder.rs | 1 - actix-http/src/header/common/content_disposition.rs | 10 +++++----- actix-http/src/helpers.rs | 4 ++-- actix-http/src/response.rs | 1 - actix-http/src/ws/frame.rs | 1 - actix-http/src/ws/proto.rs | 2 -- 7 files changed, 7 insertions(+), 13 deletions(-) diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index b13481551..0850e18ff 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -14,7 +14,6 @@ use derive_more::{Display, From}; pub use futures_channel::oneshot::Canceled; use http::uri::InvalidUri; use http::{header, Error as HttpError, StatusCode}; -use httparse; use serde::de::value::Error as DeError; use serde_json::error::Error as JsonError; use serde_urlencoded::ser::Error as FormError; diff --git a/actix-http/src/h1/decoder.rs b/actix-http/src/h1/decoder.rs index e113fd52d..d3ccd8e5a 100644 --- a/actix-http/src/h1/decoder.rs +++ b/actix-http/src/h1/decoder.rs @@ -8,7 +8,6 @@ use actix_codec::Decoder; use bytes::{Buf, Bytes, BytesMut}; use http::header::{HeaderName, HeaderValue}; use http::{header, Method, StatusCode, Uri, Version}; -use httparse; use log::{debug, error, trace}; use crate::error::ParseError; diff --git a/actix-http/src/header/common/content_disposition.rs b/actix-http/src/header/common/content_disposition.rs index d0d5af765..aa2e00ec0 100644 --- a/actix-http/src/header/common/content_disposition.rs +++ b/actix-http/src/header/common/content_disposition.rs @@ -423,7 +423,7 @@ impl ContentDisposition { /// Return the value of *name* if exists. pub fn get_name(&self) -> Option<&str> { - self.parameters.iter().filter_map(|p| p.as_name()).nth(0) + self.parameters.iter().filter_map(|p| p.as_name()).next() } /// Return the value of *filename* if exists. @@ -431,7 +431,7 @@ impl ContentDisposition { self.parameters .iter() .filter_map(|p| p.as_filename()) - .nth(0) + .next() } /// Return the value of *filename\** if exists. @@ -439,7 +439,7 @@ impl ContentDisposition { self.parameters .iter() .filter_map(|p| p.as_filename_ext()) - .nth(0) + .next() } /// Return the value of the parameter which the `name` matches. @@ -448,7 +448,7 @@ impl ContentDisposition { self.parameters .iter() .filter_map(|p| p.as_unknown(name)) - .nth(0) + .next() } /// Return the value of the extended parameter which the `name` matches. @@ -457,7 +457,7 @@ impl ContentDisposition { self.parameters .iter() .filter_map(|p| p.as_unknown_ext(name)) - .nth(0) + .next() } } diff --git a/actix-http/src/helpers.rs b/actix-http/src/helpers.rs index 6599f6a32..86f8250b6 100644 --- a/actix-http/src/helpers.rs +++ b/actix-http/src/helpers.rs @@ -117,7 +117,7 @@ pub fn write_content_length(n: usize, bytes: &mut BytesMut) { } else if n < 1_000_000 { let n = n as u32; - let d100000 = (n / 100000) as u8; + let d100000 = (n / 100_000) as u8; let d10000 = ((n / 10000) % 10) as u8; let d1000 = ((n / 1000) % 10) as u8; let d100 = ((n / 100) % 10) as u8; @@ -149,7 +149,7 @@ pub(crate) fn write_usize(n: usize, bytes: &mut BytesMut) { let lsd = (n % 10) as u8; // remove the lsd from n - n = n / 10; + n /= 10; buf.put_u8(DIGITS_START + lsd); } diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index 655d565ad..7a9b82df2 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -9,7 +9,6 @@ use std::{fmt, str}; use bytes::{Bytes, BytesMut}; use futures_core::Stream; use serde::Serialize; -use serde_json; use crate::body::{Body, BodyStream, MessageBody, ResponseBody}; use crate::cookie::{Cookie, CookieJar}; diff --git a/actix-http/src/ws/frame.rs b/actix-http/src/ws/frame.rs index 3c70eb2bd..8f7004f18 100644 --- a/actix-http/src/ws/frame.rs +++ b/actix-http/src/ws/frame.rs @@ -2,7 +2,6 @@ use std::convert::TryFrom; use bytes::{Buf, BufMut, BytesMut}; use log::debug; -use rand; use crate::ws::mask::apply_mask; use crate::ws::proto::{CloseCode, CloseReason, OpCode}; diff --git a/actix-http/src/ws/proto.rs b/actix-http/src/ws/proto.rs index 60af6f08b..7b55cbf1a 100644 --- a/actix-http/src/ws/proto.rs +++ b/actix-http/src/ws/proto.rs @@ -1,5 +1,3 @@ -use base64; -use sha1; use std::convert::{From, Into}; use std::fmt; From 0173f99726379abad7ecb5b44c32d22316e46253 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 27 Feb 2020 11:20:41 +0900 Subject: [PATCH 051/157] Update changelog --- actix-http/CHANGES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 8a25efea1..229fcbbae 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,10 +1,10 @@ # Changes -# [Unreleased] +## [2.0.0-alpha.1] - 2020-02-27 ### Changed -* Update the `time` dependency to 0.2.7 +* Update the `time` dependency to 0.2.7. * Moved actors messages support from actix crate, enabled with feature `actors`. From 15a25878876eb2f8557fe4a7008b16512445ea15 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 27 Feb 2020 11:22:38 +0900 Subject: [PATCH 052/157] Bump up to 2.0.0-alpha.1 --- actix-http/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index b87494627..356e68405 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "1.0.1" +version = "2.0.0-alpha.1" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" From 3b675c9125c0d0b826c01ff163b5c4bd5d15ec9c Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 27 Feb 2020 11:34:58 +0900 Subject: [PATCH 053/157] Update actix-http to 2.0.0-alpha.1 --- Cargo.toml | 2 +- actix-files/Cargo.toml | 2 +- actix-framed/Cargo.toml | 2 +- actix-multipart/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- awc/Cargo.toml | 4 ++-- test-server/Cargo.toml | 4 ++-- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 73d068966..d9ec862db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,7 +71,7 @@ actix-threadpool = "0.3.1" actix-tls = "1.0.0" actix-web-codegen = "0.2.0" -actix-http = "1.0.1" +actix-http = "2.0.0-alpha.1" awc = { version = "1.0.1", default-features = false } bytes = "0.5.3" diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 104eb3dfa..269ea5371 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -19,7 +19,7 @@ path = "src/lib.rs" [dependencies] actix-web = { version = "2.0.0-rc", default-features = false } -actix-http = "1.0.1" +actix-http = "2.0.0-alpha.1" actix-service = "1.0.1" bitflags = "1" bytes = "0.5.3" diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index 7e322e1d4..dc974e402 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -23,7 +23,7 @@ actix-codec = "0.2.0" actix-service = "1.0.1" actix-router = "0.2.1" actix-rt = "1.0.0" -actix-http = "1.0.1" +actix-http = "2.0.0-alpha.1" bytes = "0.5.3" futures = "0.3.1" diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 7273fb4ce..a1d91c6a4 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -29,4 +29,4 @@ twoway = "0.2" [dev-dependencies] actix-rt = "1.0.0" -actix-http = "1.0.1" +actix-http = "2.0.0-alpha.1" diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 7851e1b7f..ed12380c8 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -18,7 +18,7 @@ path = "src/lib.rs" [dependencies] actix = "0.9.0" actix-web = "2.0.0" -actix-http = "1.0.1" +actix-http = "2.0.0-alpha.1" actix-codec = "0.2.0" bytes = "0.5.2" futures = "0.3.1" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 37392d28e..71b23ece3 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -36,7 +36,7 @@ compress = ["actix-http/compress"] [dependencies] actix-codec = "0.2.0" actix-service = "1.0.1" -actix-http = "1.0.1" +actix-http = "2.0.0-alpha.1" actix-rt = "1.0.0" base64 = "0.11" @@ -56,7 +56,7 @@ rust-tls = { version = "0.16.0", package="rustls", optional = true, features = [ [dev-dependencies] actix-connect = { version = "1.0.1", features=["openssl"] } actix-web = { version = "2.0.0", features=["openssl"] } -actix-http = { version = "1.0.1", features=["openssl"] } +actix-http = { version = "2.0.0-alpha.1", features=["openssl"] } actix-http-test = { version = "1.0.0", features=["openssl"] } actix-utils = "1.0.3" actix-server = "1.0.0" diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index a458fb341..117c1a318 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -55,5 +55,5 @@ time = { version = "0.2.7", default-features = false, features = ["std"] } open-ssl = { version="0.10", package="openssl", optional = true } [dev-dependencies] -actix-web = { version = "2.0.0" } -actix-http = { version = "1.0.1" } +actix-web = "2.0.0" +actix-http = "2.0.0-alpha.1" From 6cc83dbb673a43a010b0f98bb551ea9d80d58362 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 27 Feb 2020 12:45:11 +0900 Subject: [PATCH 054/157] Allow clippy lint for compatibility --- actix-http/src/h1/dispatcher.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index acbb09960..6276653d3 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -1,3 +1,6 @@ +// Because MSRV is 1.39.0. +#![allow(clippy::mem_replace_with_default)] + use std::collections::VecDeque; use std::future::Future; use std::pin::Pin; From c9fdcc596db0618495ab8611e52b730e829e36e5 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 27 Feb 2020 12:46:29 +0900 Subject: [PATCH 055/157] Update actix to 0.10.0-alpha.1 --- Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d9ec862db..125008870 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,7 +93,7 @@ open-ssl = { version="0.10", package = "openssl", optional = true } rust-tls = { version = "0.16.0", package = "rustls", optional = true } [dev-dependencies] -actix = "0.9.0" +actix = "0.10.0-alpha.1" rand = "0.7" env_logger = "0.6" serde_derive = "1.0" diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index ed12380c8..333edb8c0 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -16,7 +16,7 @@ name = "actix_web_actors" path = "src/lib.rs" [dependencies] -actix = "0.9.0" +actix = "0.10.0-alpha.1" actix-web = "2.0.0" actix-http = "2.0.0-alpha.1" actix-codec = "0.2.0" From 3dc859af58fa0b4676f86f7ba8a5cf5db546831a Mon Sep 17 00:00:00 2001 From: __JM_Joy__ <918734043@qq.com> Date: Thu, 27 Feb 2020 21:34:06 +0800 Subject: [PATCH 056/157] Fix missing `std::error::Error` implement for `MultipartError`. (#1382) * Fix missing `std::error::Error` implement for `MultipartError`. * Update actix-multipart CHANGES.md. --- actix-multipart/CHANGES.md | 2 ++ actix-multipart/src/error.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/actix-multipart/CHANGES.md b/actix-multipart/CHANGES.md index d73a69393..ed5c8ad3f 100644 --- a/actix-multipart/CHANGES.md +++ b/actix-multipart/CHANGES.md @@ -4,6 +4,8 @@ * Remove the unused `time` dependency +* Fix missing `std::error::Error` implement for `MultipartError`. + ## [0.2.0] - 2019-12-20 * Release diff --git a/actix-multipart/src/error.rs b/actix-multipart/src/error.rs index 6677f69c7..cdbb5d395 100644 --- a/actix-multipart/src/error.rs +++ b/actix-multipart/src/error.rs @@ -33,6 +33,8 @@ pub enum MultipartError { NotConsumed, } +impl std::error::Error for MultipartError {} + /// Return `BadRequest` for `MultipartError` impl ResponseError for MultipartError { fn status_code(&self) -> StatusCode { From b4d63667df9aea8865a7c28d849701e560df0c51 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 27 Feb 2020 22:35:57 +0900 Subject: [PATCH 057/157] Demote lint level to warn --- actix-http/src/cookie/mod.rs | 2 +- actix-http/src/lib.rs | 2 +- awc/src/lib.rs | 2 +- src/lib.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/actix-http/src/cookie/mod.rs b/actix-http/src/cookie/mod.rs index 360d80883..7f74abc95 100644 --- a/actix-http/src/cookie/mod.rs +++ b/actix-http/src/cookie/mod.rs @@ -47,7 +47,7 @@ //! ``` #![doc(html_root_url = "https://docs.rs/cookie/0.11")] -#![deny(missing_docs)] +#![warn(missing_docs)] mod builder; mod delta; diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index 1cda9437f..9f615a129 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -1,5 +1,5 @@ //! Basic http primitives for actix-net framework. -#![deny(rust_2018_idioms, warnings)] +#![warn(rust_2018_idioms, warnings)] #![allow( clippy::type_complexity, clippy::too_many_arguments, diff --git a/awc/src/lib.rs b/awc/src/lib.rs index 8944fe229..952a15369 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -1,4 +1,4 @@ -#![deny(rust_2018_idioms, warnings)] +#![warn(rust_2018_idioms, warnings)] #![allow( clippy::type_complexity, clippy::borrow_interior_mutable_const, diff --git a/src/lib.rs b/src/lib.rs index a898bd704..d7cb45074 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![deny(rust_2018_idioms, warnings)] +#![warn(rust_2018_idioms, warnings)] #![allow( clippy::needless_doctest_main, clippy::type_complexity, From 117d28f7ba104cb162307772aaf4a3d30367167a Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 4 Mar 2020 15:09:31 +0900 Subject: [PATCH 058/157] Update `actix-connect` to 2.0.0-alpha.1 --- actix-framed/Cargo.toml | 2 +- actix-http/Cargo.toml | 4 ++-- awc/Cargo.toml | 2 +- test-server/Cargo.toml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index dc974e402..133b8083d 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -32,6 +32,6 @@ log = "0.4" [dev-dependencies] actix-server = "1.0.0" -actix-connect = { version = "1.0.0", features=["openssl"] } +actix-connect = { version = "2.0.0-alpha.1", features=["openssl"] } actix-http-test = { version = "1.0.0", features=["openssl"] } actix-utils = "1.0.3" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 356e68405..ea33b42d0 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -45,7 +45,7 @@ actors = ["actix"] [dependencies] actix-service = "1.0.5" actix-codec = "0.2.0" -actix-connect = "1.0.2" +actix-connect = "2.0.0-alpha.1" actix-utils = "1.0.6" actix-rt = "1.0.0" actix-threadpool = "0.3.1" @@ -94,7 +94,7 @@ fail-ure = { version = "0.1.5", package="failure", optional = true } [dev-dependencies] actix-server = "1.0.1" -actix-connect = { version = "1.0.2", features=["openssl"] } +actix-connect = { version = "2.0.0-alpha.1", features=["openssl"] } actix-http-test = { version = "1.0.0", features=["openssl"] } actix-tls = { version = "1.0.0", features=["openssl"] } criterion = "0.3" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 71b23ece3..ec6ebc77a 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -54,7 +54,7 @@ open-ssl = { version="0.10", package="openssl", optional = true } rust-tls = { version = "0.16.0", package="rustls", optional = true, features = ["dangerous_configuration"] } [dev-dependencies] -actix-connect = { version = "1.0.1", features=["openssl"] } +actix-connect = { version = "2.0.0-alpha.1", features=["openssl"] } actix-web = { version = "2.0.0", features=["openssl"] } actix-http = { version = "2.0.0-alpha.1", features=["openssl"] } actix-http-test = { version = "1.0.0", features=["openssl"] } diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 117c1a318..ba8e1c9ea 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -32,7 +32,7 @@ openssl = ["open-ssl", "awc/openssl"] [dependencies] actix-service = "1.0.1" actix-codec = "0.2.0" -actix-connect = "1.0.0" +actix-connect = "2.0.0-alpha.1" actix-utils = "1.0.3" actix-rt = "1.0.0" actix-server = "1.0.0" From b9b52079e0eb27dfff1135df880618539284ca07 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 4 Mar 2020 15:10:23 +0900 Subject: [PATCH 059/157] Update `actix-tls` to 2.0.0-alpha.1 --- Cargo.toml | 2 +- actix-http/Cargo.toml | 4 ++-- awc/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 125008870..af6a728ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,7 +68,7 @@ actix-server = "1.0.0" actix-testing = "1.0.0" actix-macros = "0.1.0" actix-threadpool = "0.3.1" -actix-tls = "1.0.0" +actix-tls = "2.0.0-alpha.1" actix-web-codegen = "0.2.0" actix-http = "2.0.0-alpha.1" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index ea33b42d0..9efe16996 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -49,7 +49,7 @@ actix-connect = "2.0.0-alpha.1" actix-utils = "1.0.6" actix-rt = "1.0.0" actix-threadpool = "0.3.1" -actix-tls = { version = "1.0.0", optional = true } +actix-tls = { version = "2.0.0-alpha.1", optional = true } actix = { version = "0.10.0-alpha.1", optional = true } base64 = "0.11" @@ -96,7 +96,7 @@ fail-ure = { version = "0.1.5", package="failure", optional = true } actix-server = "1.0.1" actix-connect = { version = "2.0.0-alpha.1", features=["openssl"] } actix-http-test = { version = "1.0.0", features=["openssl"] } -actix-tls = { version = "1.0.0", features=["openssl"] } +actix-tls = { version = "2.0.0-alpha.1", features=["openssl"] } criterion = "0.3" futures = "0.3.1" env_logger = "0.7" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index ec6ebc77a..99f0e3eba 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -60,7 +60,7 @@ actix-http = { version = "2.0.0-alpha.1", features=["openssl"] } actix-http-test = { version = "1.0.0", features=["openssl"] } actix-utils = "1.0.3" actix-server = "1.0.0" -actix-tls = { version = "1.0.0", features=["openssl", "rustls"] } +actix-tls = { version = "2.0.0-alpha.1", features=["openssl", "rustls"] } brotli2 = "0.3.2" flate2 = "1.0.13" futures = "0.3.1" From 687dc609dd19602c0af6a3b3bba6acdc1875221c Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 4 Mar 2020 15:11:31 +0900 Subject: [PATCH 060/157] Update `rustls` to 0.17 --- Cargo.toml | 2 +- actix-http/Cargo.toml | 2 +- awc/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index af6a728ab..fa29c225f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,7 +90,7 @@ serde_urlencoded = "0.6.1" time = { version = "0.2.7", default-features = false, features = ["std"] } url = "2.1" open-ssl = { version="0.10", package = "openssl", optional = true } -rust-tls = { version = "0.16.0", package = "rustls", optional = true } +rust-tls = { version = "0.17.0", package = "rustls", optional = true } [dev-dependencies] actix = "0.10.0-alpha.1" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 9efe16996..df9653aa3 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -102,7 +102,7 @@ futures = "0.3.1" env_logger = "0.7" serde_derive = "1.0" open-ssl = { version="0.10", package = "openssl" } -rust-tls = { version="0.16", package = "rustls" } +rust-tls = { version="0.17", package = "rustls" } [[bench]] name = "content-length" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 99f0e3eba..d27615f7a 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -51,7 +51,7 @@ serde = "1.0" serde_json = "1.0" serde_urlencoded = "0.6.1" open-ssl = { version="0.10", package="openssl", optional = true } -rust-tls = { version = "0.16.0", package="rustls", optional = true, features = ["dangerous_configuration"] } +rust-tls = { version = "0.17.0", package="rustls", optional = true, features = ["dangerous_configuration"] } [dev-dependencies] actix-connect = { version = "2.0.0-alpha.1", features=["openssl"] } From 9d661dc4f3edf9deca8e13d517f95151bdeb4111 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 4 Mar 2020 15:20:14 +0900 Subject: [PATCH 061/157] Update changelog --- CHANGES.md | 4 ++-- actix-http/CHANGES.md | 10 +++++++--- test-server/CHANGES.md | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ab9caa7bd..da4a77a80 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,10 +10,10 @@ ### Changed * Use `sha-1` crate instead of unmaintained `sha1` crate - * Skip empty chunks when returning response from a `Stream` #1308 - * Update the `time` dependency to 0.2.7 +* Update `actix-tls` dependency to 2.0.0-alpha.1 +* Update `rustls` dependency to 0.17 ## [2.0.0] - 2019-12-25 diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 229fcbbae..4ec981174 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,15 +1,19 @@ # Changes +## [2.0.0-alpha.2] - someday + +### Changed + +* Update `actix-connect` and `actix-tls` dependency to 2.0.0-alpha.1 + + ## [2.0.0-alpha.1] - 2020-02-27 ### Changed * Update the `time` dependency to 0.2.7. - * Moved actors messages support from actix crate, enabled with feature `actors`. - * Breaking change: trait MessageBody requires Unpin and accepting Pin<&mut Self> instead of &mut self in the poll_next(). - * MessageBody is not implemented for &'static [u8] anymore. ### Fixed diff --git a/test-server/CHANGES.md b/test-server/CHANGES.md index 96c010355..3a990a594 100644 --- a/test-server/CHANGES.md +++ b/test-server/CHANGES.md @@ -3,6 +3,7 @@ ## [Unreleased] - 2020-xx-xx * Update the `time` dependency to 0.2.7 +* Update `actix-connect` dependency to 2.0.0-alpha.1 ## [1.0.0] - 2019-12-13 From e90950fee137f3e161a975bbb27b5aab5d25306f Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Wed, 4 Mar 2020 11:27:58 -0500 Subject: [PATCH 062/157] Re-apply commit 2cf7b3ad20fb823314426a5e33b0805045ec1d8a This ended up getting reverted by #1367, which re-introduced an unsound use of `Pin::new_unchecked` See my original PR #1374 for the reasoning behind this change. --- actix-http/src/h1/dispatcher.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 6276653d3..ffdcd8d9b 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -73,7 +73,7 @@ where U::Error: fmt::Display, { Normal(#[pin] InnerDispatcher), - Upgrade(#[pin] U::Future), + Upgrade(Pin>), } #[pin_project] @@ -123,8 +123,8 @@ where B: MessageBody, { None, - ExpectCall(#[pin] X::Future), - ServiceCall(#[pin] S::Future), + ExpectCall(Pin>), + ServiceCall(Pin>), SendPayload(#[pin] ResponseBody), } @@ -391,11 +391,11 @@ where } None => None, }, - State::ExpectCall(fut) => match fut.poll(cx) { + State::ExpectCall(fut) => match fut.as_mut().poll(cx) { Poll::Ready(Ok(req)) => { self.as_mut().send_continue(); this = self.as_mut().project(); - this.state.set(State::ServiceCall(this.service.call(req))); + this.state.set(State::ServiceCall(Box::pin(this.service.call(req)))); continue; } Poll::Ready(Err(e)) => { @@ -405,7 +405,7 @@ where } Poll::Pending => None, }, - State::ServiceCall(fut) => match fut.poll(cx) { + State::ServiceCall(fut) => match fut.as_mut().poll(cx) { Poll::Ready(Ok(res)) => { let (res, body) = res.into().replace_body(()); let state = self.as_mut().send_response(res, body)?; @@ -485,8 +485,8 @@ where ) -> Result, DispatchError> { // Handle `EXPECT: 100-Continue` header let req = if req.head().expect() { - let mut task = self.as_mut().project().expect.call(req); - match unsafe { Pin::new_unchecked(&mut task) }.poll(cx) { + let mut task = Box::pin(self.as_mut().project().expect.call(req)); + match task.as_mut().poll(cx) { Poll::Ready(Ok(req)) => { self.as_mut().send_continue(); req @@ -504,8 +504,8 @@ where }; // Call service - let mut task = self.as_mut().project().service.call(req); - match unsafe { Pin::new_unchecked(&mut task) }.poll(cx) { + let mut task = Box::pin(self.as_mut().project().service.call(req)); + match task.as_mut().poll(cx) { Poll::Ready(Ok(res)) => { let (res, body) = res.into().replace_body(()); self.send_response(res, body) @@ -807,7 +807,7 @@ where self.as_mut() .project() .inner - .set(DispatcherState::Upgrade(upgrade)); + .set(DispatcherState::Upgrade(Box::pin(upgrade))); return self.poll(cx); } @@ -855,7 +855,7 @@ where } } } - DispatcherState::Upgrade(fut) => fut.poll(cx).map_err(|e| { + DispatcherState::Upgrade(fut) => fut.as_mut().poll(cx).map_err(|e| { error!("Upgrade handler error: {}", e); DispatchError::Upgrade }), From 10e3e72595f14fd416ab471fff6e23f11661cc4e Mon Sep 17 00:00:00 2001 From: Maxim Vorobjov Date: Sat, 7 Mar 2020 04:09:31 +0200 Subject: [PATCH 063/157] Http2 client configuration to improve performance (#1394) * add defaults for http2 client configuration * fix spaces * Add changes text for extended H2 defaults buffers * client: configurable H2 window sizes and max_http_version * add H2 window size configuration and max_http_version to awc::ClientBuilder * add awc::ClientBuilder H2 window sizes and max_http_version * add test for H2 window size settings * cleanup comment * Apply code review fixes * Code review fix for awc ClientBuilder * Remove unnecessary comments on code review * pin quote version to resolve build issue * max_http_version to accept http::Version * revert fix for quote broken build --- actix-http/CHANGES.md | 5 + actix-http/src/client/config.rs | 39 ++++++++ actix-http/src/client/connector.rs | 146 ++++++++++++++++------------- actix-http/src/client/h2proto.rs | 22 ++++- actix-http/src/client/mod.rs | 1 + actix-http/src/client/pool.rs | 46 +++++---- awc/CHANGES.md | 6 ++ awc/src/builder.rs | 86 +++++++++++++---- awc/tests/test_connector.rs | 61 ++++++++++++ 9 files changed, 302 insertions(+), 110 deletions(-) create mode 100644 actix-http/src/client/config.rs create mode 100644 awc/tests/test_connector.rs diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 4ec981174..ccfdc81d6 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -6,6 +6,11 @@ * Update `actix-connect` and `actix-tls` dependency to 2.0.0-alpha.1 +* Change default initial window size and connection window size for HTTP2 to 2MB and 1MB respectively to improve download speed for awc when downloading large objects. + +* client::Connector accepts initial_window_size and initial_connection_window_size HTTP2 configuration + +* client::Connector allowing to set max_http_version to limit HTTP version to be used ## [2.0.0-alpha.1] - 2020-02-27 diff --git a/actix-http/src/client/config.rs b/actix-http/src/client/config.rs new file mode 100644 index 000000000..c86c697a2 --- /dev/null +++ b/actix-http/src/client/config.rs @@ -0,0 +1,39 @@ +use std::time::Duration; + +// These values are taken from hyper/src/proto/h2/client.rs +const DEFAULT_H2_CONN_WINDOW: u32 = 1024 * 1024 * 2; // 2mb +const DEFAULT_H2_STREAM_WINDOW: u32 = 1024 * 1024; // 1mb + +/// Connector configuration +#[derive(Clone)] +pub(crate) struct ConnectorConfig { + pub(crate) timeout: Duration, + pub(crate) conn_lifetime: Duration, + pub(crate) conn_keep_alive: Duration, + pub(crate) disconnect_timeout: Option, + pub(crate) limit: usize, + pub(crate) conn_window_size: u32, + pub(crate) stream_window_size: u32, +} + +impl Default for ConnectorConfig { + fn default() -> Self { + Self { + timeout: Duration::from_secs(1), + conn_lifetime: Duration::from_secs(75), + conn_keep_alive: Duration::from_secs(15), + disconnect_timeout: Some(Duration::from_millis(3000)), + limit: 100, + conn_window_size: DEFAULT_H2_CONN_WINDOW, + stream_window_size: DEFAULT_H2_STREAM_WINDOW, + } + } +} + +impl ConnectorConfig { + pub(crate) fn no_disconnect_timeout(&self) -> Self { + let mut res = self.clone(); + res.disconnect_timeout = None; + res + } +} diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index 055d4276d..adb88bbed 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -11,6 +11,7 @@ use actix_service::{apply_fn, Service}; use actix_utils::timeout::{TimeoutError, TimeoutService}; use http::Uri; +use super::config::ConnectorConfig; use super::connection::Connection; use super::error::ConnectError; use super::pool::{ConnectionPool, Protocol}; @@ -48,11 +49,7 @@ type SslConnector = (); /// ``` pub struct Connector { connector: T, - timeout: Duration, - conn_lifetime: Duration, - conn_keep_alive: Duration, - disconnect_timeout: Duration, - limit: usize, + config: ConnectorConfig, #[allow(dead_code)] ssl: SslConnector, _t: PhantomData, @@ -71,42 +68,49 @@ impl Connector<(), ()> { > + Clone, TcpStream, > { - let ssl = { - #[cfg(feature = "openssl")] - { - use actix_connect::ssl::openssl::SslMethod; - - let mut ssl = OpensslConnector::builder(SslMethod::tls()).unwrap(); - let _ = ssl - .set_alpn_protos(b"\x02h2\x08http/1.1") - .map_err(|e| error!("Can not set alpn protocol: {:?}", e)); - SslConnector::Openssl(ssl.build()) - } - #[cfg(all(not(feature = "openssl"), feature = "rustls"))] - { - let protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; - let mut config = ClientConfig::new(); - config.set_protocols(&protos); - config - .root_store - .add_server_trust_anchors(&actix_tls::rustls::TLS_SERVER_ROOTS); - SslConnector::Rustls(Arc::new(config)) - } - #[cfg(not(any(feature = "openssl", feature = "rustls")))] - {} - }; - Connector { - ssl, + ssl: Self::build_ssl(vec![b"h2".to_vec(), b"http/1.1".to_vec()]), connector: default_connector(), - timeout: Duration::from_secs(1), - conn_lifetime: Duration::from_secs(75), - conn_keep_alive: Duration::from_secs(15), - disconnect_timeout: Duration::from_millis(3000), - limit: 100, + config: ConnectorConfig::default(), _t: PhantomData, } } + + // Build Ssl connector with openssl, based on supplied alpn protocols + #[cfg(feature = "openssl")] + fn build_ssl(protocols: Vec>) -> SslConnector + { + use actix_connect::ssl::openssl::SslMethod; + use bytes::{BufMut, BytesMut}; + + let mut alpn = BytesMut::with_capacity(20); + for proto in protocols.iter() { + alpn.put_u8(proto.len() as u8); + alpn.put(proto.as_slice()); + } + + let mut ssl = OpensslConnector::builder(SslMethod::tls()).unwrap(); + let _ = ssl + .set_alpn_protos(&alpn) + .map_err(|e| error!("Can not set alpn protocol: {:?}", e)); + SslConnector::Openssl(ssl.build()) + } + + // Build Ssl connector with rustls, based on supplied alpn protocols + #[cfg(all(not(feature = "openssl"), feature = "rustls"))] + fn build_ssl(protocols: Vec>) -> SslConnector + { + let mut config = ClientConfig::new(); + config.set_protocols(&protocols); + config + .root_store + .add_server_trust_anchors(&actix_tls::rustls::TLS_SERVER_ROOTS); + SslConnector::Rustls(Arc::new(config)) + } + + // ssl turned off, provides empty ssl connector + #[cfg(not(any(feature = "openssl", feature = "rustls")))] + fn build_ssl(_: Vec>) -> SslConnector {} } impl Connector { @@ -122,11 +126,7 @@ impl Connector { { Connector { connector, - timeout: self.timeout, - conn_lifetime: self.conn_lifetime, - conn_keep_alive: self.conn_keep_alive, - disconnect_timeout: self.disconnect_timeout, - limit: self.limit, + config: self.config, ssl: self.ssl, _t: PhantomData, } @@ -146,7 +146,7 @@ where /// Connection timeout, i.e. max time to connect to remote host including dns name resolution. /// Set to 1 second by default. pub fn timeout(mut self, timeout: Duration) -> Self { - self.timeout = timeout; + self.config.timeout = timeout; self } @@ -163,12 +163,42 @@ where self } + /// Maximum supported http major version + /// Supported versions http/1.1, http/2 + pub fn max_http_version(mut self, val: http::Version) -> Self { + let versions = match val { + http::Version::HTTP_11 => vec![b"http/1.1".to_vec()], + http::Version::HTTP_2 => vec![b"h2".to_vec(), b"http/1.1".to_vec()], + _ => unimplemented!("actix-http:client: supported versions http/1.1, http/2"), + }; + self.ssl = Connector::build_ssl(versions); + self + } + + /// Indicates the initial window size (in octets) for + /// HTTP2 stream-level flow control for received data. + /// + /// The default value is 65,535 and is good for APIs, but not for big objects. + pub fn initial_window_size(mut self, size: u32) -> Self { + self.config.stream_window_size = size; + self + } + + /// Indicates the initial window size (in octets) for + /// HTTP2 connection-level flow control for received data. + /// + /// The default value is 65,535 and is good for APIs, but not for big objects. + pub fn initial_connection_window_size(mut self, size: u32) -> Self { + self.config.conn_window_size = size; + self + } + /// Set total number of simultaneous connections per type of scheme. /// /// If limit is 0, the connector has no limit. /// The default limit size is 100. pub fn limit(mut self, limit: usize) -> Self { - self.limit = limit; + self.config.limit = limit; self } @@ -179,7 +209,7 @@ where /// exceeds this period, the connection is closed. /// Default keep-alive period is 15 seconds. pub fn conn_keep_alive(mut self, dur: Duration) -> Self { - self.conn_keep_alive = dur; + self.config.conn_keep_alive = dur; self } @@ -189,7 +219,7 @@ where /// until it is closed regardless of keep-alive period. /// Default lifetime period is 75 seconds. pub fn conn_lifetime(mut self, dur: Duration) -> Self { - self.conn_lifetime = dur; + self.config.conn_lifetime = dur; self } @@ -202,7 +232,7 @@ where /// /// By default disconnect timeout is set to 3000 milliseconds. pub fn disconnect_timeout(mut self, dur: Duration) -> Self { - self.disconnect_timeout = dur; + self.config.disconnect_timeout = Some(dur); self } @@ -216,7 +246,7 @@ where #[cfg(not(any(feature = "openssl", feature = "rustls")))] { let connector = TimeoutService::new( - self.timeout, + self.config.timeout, apply_fn(self.connector, |msg: Connect, srv| { srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr)) }) @@ -231,10 +261,7 @@ where connect_impl::InnerConnector { tcp_pool: ConnectionPool::new( connector, - self.conn_lifetime, - self.conn_keep_alive, - None, - self.limit, + self.config.no_disconnect_timeout(), ), } } @@ -248,7 +275,7 @@ where use actix_service::{boxed::service, pipeline}; let ssl_service = TimeoutService::new( - self.timeout, + self.config.timeout, pipeline( apply_fn(self.connector.clone(), |msg: Connect, srv| { srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr)) @@ -301,7 +328,7 @@ where }); let tcp_service = TimeoutService::new( - self.timeout, + self.config.timeout, apply_fn(self.connector, |msg: Connect, srv| { srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr)) }) @@ -316,18 +343,9 @@ where connect_impl::InnerConnector { tcp_pool: ConnectionPool::new( tcp_service, - self.conn_lifetime, - self.conn_keep_alive, - None, - self.limit, - ), - ssl_pool: ConnectionPool::new( - ssl_service, - self.conn_lifetime, - self.conn_keep_alive, - Some(self.disconnect_timeout), - self.limit, + self.config.no_disconnect_timeout(), ), + ssl_pool: ConnectionPool::new(ssl_service, self.config), } } } diff --git a/actix-http/src/client/h2proto.rs b/actix-http/src/client/h2proto.rs index 69d20752a..2afd2d80b 100644 --- a/actix-http/src/client/h2proto.rs +++ b/actix-http/src/client/h2proto.rs @@ -1,11 +1,15 @@ use std::convert::TryFrom; +use std::future::Future; use std::time; use actix_codec::{AsyncRead, AsyncWrite}; use bytes::Bytes; use futures_util::future::poll_fn; use futures_util::pin_mut; -use h2::{client::SendRequest, SendStream}; +use h2::{ + client::{Builder, Connection, SendRequest}, + SendStream, +}; use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, TRANSFER_ENCODING}; use http::{request::Request, Method, Version}; @@ -14,6 +18,7 @@ use crate::header::HeaderMap; use crate::message::{RequestHeadType, ResponseHead}; use crate::payload::Payload; +use super::config::ConnectorConfig; use super::connection::{ConnectionType, IoConnection}; use super::error::SendRequestError; use super::pool::Acquired; @@ -185,3 +190,18 @@ fn release( } } } + +pub(crate) fn handshake( + io: Io, + config: &ConnectorConfig, +) -> impl Future, Connection), h2::Error>> +where + Io: AsyncRead + AsyncWrite + Unpin + 'static, +{ + let mut builder = Builder::new(); + builder + .initial_window_size(config.stream_window_size) + .initial_connection_window_size(config.conn_window_size) + .enable_push(false); + builder.handshake(io) +} diff --git a/actix-http/src/client/mod.rs b/actix-http/src/client/mod.rs index a45aebcd5..dd1e9b25a 100644 --- a/actix-http/src/client/mod.rs +++ b/actix-http/src/client/mod.rs @@ -1,6 +1,7 @@ //! Http client api use http::Uri; +mod config; mod connection; mod connector; mod error; diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index 38a51b558..983396f92 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -13,14 +13,16 @@ use actix_utils::{oneshot, task::LocalWaker}; use bytes::Bytes; use futures_util::future::{poll_fn, FutureExt, LocalBoxFuture}; use fxhash::FxHashMap; -use h2::client::{handshake, Connection, SendRequest}; +use h2::client::{Connection, SendRequest}; use http::uri::Authority; use indexmap::IndexSet; use pin_project::pin_project; use slab::Slab; +use super::config::ConnectorConfig; use super::connection::{ConnectionType, IoConnection}; use super::error::ConnectError; +use super::h2proto::handshake; use super::Connect; #[derive(Clone, Copy, PartialEq)] @@ -50,20 +52,11 @@ where T: Service + 'static, { - pub(crate) fn new( - connector: T, - conn_lifetime: Duration, - conn_keep_alive: Duration, - disconnect_timeout: Option, - limit: usize, - ) -> Self { + pub(crate) fn new(connector: T, config: ConnectorConfig) -> Self { ConnectionPool( Rc::new(RefCell::new(connector)), Rc::new(RefCell::new(Inner { - conn_lifetime, - conn_keep_alive, - disconnect_timeout, - limit, + config, acquired: 0, waiters: Slab::new(), waiters_queue: IndexSet::new(), @@ -129,6 +122,8 @@ where // open tcp connection let (io, proto) = connector.call(req).await?; + let config = inner.borrow().config.clone(); + let guard = OpenGuard::new(key, inner); if proto == Protocol::Http1 { @@ -138,7 +133,7 @@ where Some(guard.consume()), )) } else { - let (snd, connection) = handshake(io).await?; + let (snd, connection) = handshake(io, &config).await?; actix_rt::spawn(connection.map(|_| ())); Ok(IoConnection::new( ConnectionType::H2(snd), @@ -255,10 +250,7 @@ struct AvailableConnection { } pub(crate) struct Inner { - conn_lifetime: Duration, - conn_keep_alive: Duration, - disconnect_timeout: Option, - limit: usize, + config: ConnectorConfig, acquired: usize, available: FxHashMap>>, waiters: Slab< @@ -311,7 +303,7 @@ where fn acquire(&mut self, key: &Key, cx: &mut Context<'_>) -> Acquire { // check limits - if self.limit > 0 && self.acquired >= self.limit { + if self.config.limit > 0 && self.acquired >= self.config.limit { return Acquire::NotAvailable; } @@ -323,10 +315,10 @@ where let now = Instant::now(); while let Some(conn) = connections.pop_back() { // check if it still usable - if (now - conn.used) > self.conn_keep_alive - || (now - conn.created) > self.conn_lifetime + if (now - conn.used) > self.config.conn_keep_alive + || (now - conn.created) > self.config.conn_lifetime { - if let Some(timeout) = self.disconnect_timeout { + if let Some(timeout) = self.config.disconnect_timeout { if let ConnectionType::H1(io) = conn.io { actix_rt::spawn(CloseConnection::new(io, timeout)) } @@ -338,7 +330,7 @@ where match Pin::new(s).poll_read(cx, &mut buf) { Poll::Pending => (), Poll::Ready(Ok(n)) if n > 0 => { - if let Some(timeout) = self.disconnect_timeout { + if let Some(timeout) = self.config.disconnect_timeout { if let ConnectionType::H1(io) = io { actix_rt::spawn(CloseConnection::new( io, timeout, @@ -372,7 +364,7 @@ where fn release_close(&mut self, io: ConnectionType) { self.acquired -= 1; - if let Some(timeout) = self.disconnect_timeout { + if let Some(timeout) = self.config.disconnect_timeout { if let ConnectionType::H1(io) = io { actix_rt::spawn(CloseConnection::new(io, timeout)) } @@ -381,7 +373,7 @@ where } fn check_availibility(&self) { - if !self.waiters_queue.is_empty() && self.acquired < self.limit { + if !self.waiters_queue.is_empty() && self.acquired < self.config.limit { self.waker.wake(); } } @@ -480,6 +472,7 @@ where tx, this.inner.clone(), this.connector.call(connect), + inner.config.clone(), ); } } @@ -506,6 +499,7 @@ where >, rx: Option, ConnectError>>>, inner: Option>>>, + config: ConnectorConfig, } impl OpenWaitingConnection @@ -518,6 +512,7 @@ where rx: oneshot::Sender, ConnectError>>, inner: Rc>>, fut: F, + config: ConnectorConfig, ) { actix_rt::spawn(OpenWaitingConnection { key, @@ -525,6 +520,7 @@ where h2: None, rx: Some(rx), inner: Some(inner), + config, }) } } @@ -594,7 +590,7 @@ where ))); Poll::Ready(()) } else { - *this.h2 = Some(handshake(io).boxed_local()); + *this.h2 = Some(handshake(io, this.config).boxed_local()); self.poll(cx) } } diff --git a/awc/CHANGES.md b/awc/CHANGES.md index d410ae514..1d6e03708 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,5 +1,11 @@ # Changes +## [NEXT] + +* ClientBuilder accepts initial_window_size and initial_connection_window_size HTTP2 configuration + +* ClientBuilder allowing to set max_http_version to limit HTTP version to be used + ## [1.0.1] - 2019-12-15 * Fix compilation with default features off diff --git a/awc/src/builder.rs b/awc/src/builder.rs index 7bd0171ec..2b2e5df9f 100644 --- a/awc/src/builder.rs +++ b/awc/src/builder.rs @@ -4,11 +4,11 @@ use std::fmt; use std::rc::Rc; use std::time::Duration; -use actix_http::client::{Connect, ConnectError, Connection, Connector}; -use actix_http::http::{header, Error as HttpError, HeaderMap, HeaderName}; +use actix_http::client::{Connect as HttpConnect, ConnectError, Connection, Connector}; +use actix_http::http::{header, Error as HttpError, HeaderMap, HeaderName, self}; use actix_service::Service; -use crate::connect::ConnectorWrapper; +use crate::connect::{ConnectorWrapper, Connect}; use crate::{Client, ClientConfig}; /// An HTTP Client builder @@ -16,10 +16,15 @@ use crate::{Client, ClientConfig}; /// This type can be used to construct an instance of `Client` through a /// builder-like pattern. pub struct ClientBuilder { - config: ClientConfig, default_headers: bool, allow_redirects: bool, max_redirects: usize, + max_http_version: Option, + stream_window_size: Option, + conn_window_size: Option, + headers: HeaderMap, + timeout: Option, + connector: Option>>, } impl Default for ClientBuilder { @@ -34,25 +39,24 @@ impl ClientBuilder { default_headers: true, allow_redirects: true, max_redirects: 10, - config: ClientConfig { - headers: HeaderMap::new(), - timeout: Some(Duration::from_secs(5)), - connector: RefCell::new(Box::new(ConnectorWrapper( - Connector::new().finish(), - ))), - }, + headers: HeaderMap::new(), + timeout: Some(Duration::from_secs(5)), + connector: None, + max_http_version: None, + stream_window_size: None, + conn_window_size: None, } } /// Use custom connector service. pub fn connector(mut self, connector: T) -> Self where - T: Service + 'static, + T: Service + 'static, T::Response: Connection, ::Future: 'static, T::Future: 'static, { - self.config.connector = RefCell::new(Box::new(ConnectorWrapper(connector))); + self.connector = Some(RefCell::new(Box::new(ConnectorWrapper(connector)))); self } @@ -61,13 +65,13 @@ impl ClientBuilder { /// Request timeout is the total time before a response must be received. /// Default value is 5 seconds. pub fn timeout(mut self, timeout: Duration) -> Self { - self.config.timeout = Some(timeout); + self.timeout = Some(timeout); self } /// Disable request timeout. pub fn disable_timeout(mut self) -> Self { - self.config.timeout = None; + self.timeout = None; self } @@ -79,6 +83,31 @@ impl ClientBuilder { self } + /// Maximum supported http major version + /// Supported versions http/1.1, http/2 + pub fn max_http_version(mut self, val: http::Version) -> Self { + self.max_http_version = Some(val); + self + } + + /// Indicates the initial window size (in octets) for + /// HTTP2 stream-level flow control for received data. + /// + /// The default value is 65,535 and is good for APIs, but not for big objects. + pub fn initial_window_size(mut self, size: u32) -> Self { + self.stream_window_size = Some(size); + self + } + + /// Indicates the initial window size (in octets) for + /// HTTP2 connection-level flow control for received data. + /// + /// The default value is 65,535 and is good for APIs, but not for big objects. + pub fn initial_connection_window_size(mut self, size: u32) -> Self { + self.conn_window_size = Some(size); + self + } + /// Set max number of redirects. /// /// Max redirects is set to 10 by default. @@ -106,7 +135,7 @@ impl ClientBuilder { match HeaderName::try_from(key) { Ok(key) => match value.try_into() { Ok(value) => { - self.config.headers.append(key, value); + self.headers.append(key, value); } Err(e) => log::error!("Header value error: {:?}", e), }, @@ -140,7 +169,27 @@ impl ClientBuilder { /// Finish build process and create `Client` instance. pub fn finish(self) -> Client { - Client(Rc::new(self.config)) + let connector = if let Some(connector) = self.connector { + connector + } else { + let mut connector = Connector::new(); + if let Some(val) = self.max_http_version { + connector = connector.max_http_version(val) + }; + if let Some(val) = self.conn_window_size { + connector = connector.initial_connection_window_size(val) + }; + if let Some(val) = self.stream_window_size { + connector = connector.initial_window_size(val) + }; + RefCell::new(Box::new(ConnectorWrapper(connector.finish())) as Box) + }; + let config = ClientConfig { + headers: self.headers, + timeout: self.timeout, + connector, + }; + Client(Rc::new(config)) } } @@ -153,7 +202,6 @@ mod tests { let client = ClientBuilder::new().basic_auth("username", Some("password")); assert_eq!( client - .config .headers .get(header::AUTHORIZATION) .unwrap() @@ -165,7 +213,6 @@ mod tests { let client = ClientBuilder::new().basic_auth("username", None); assert_eq!( client - .config .headers .get(header::AUTHORIZATION) .unwrap() @@ -180,7 +227,6 @@ mod tests { let client = ClientBuilder::new().bearer_auth("someS3cr3tAutht0k3n"); assert_eq!( client - .config .headers .get(header::AUTHORIZATION) .unwrap() diff --git a/awc/tests/test_connector.rs b/awc/tests/test_connector.rs new file mode 100644 index 000000000..8b295cda7 --- /dev/null +++ b/awc/tests/test_connector.rs @@ -0,0 +1,61 @@ +#![cfg(feature = "openssl")] +use actix_http::HttpService; +use actix_http_test::test_server; +use actix_service::{map_config, ServiceFactory}; +use actix_web::http::Version; +use actix_web::{dev::AppConfig, web, App, HttpResponse}; +use open_ssl::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode}; + +fn ssl_acceptor() -> SslAcceptor { + // load ssl keys + let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); + builder + .set_private_key_file("../tests/key.pem", SslFiletype::PEM) + .unwrap(); + builder + .set_certificate_chain_file("../tests/cert.pem") + .unwrap(); + builder.set_alpn_select_callback(|_, protos| { + const H2: &[u8] = b"\x02h2"; + if protos.windows(3).any(|window| window == H2) { + Ok(b"h2") + } else { + Err(open_ssl::ssl::AlpnError::NOACK) + } + }); + builder.set_alpn_protos(b"\x02h2").unwrap(); + builder.build() +} + +#[actix_rt::test] +async fn test_connection_window_size() { + let srv = test_server(move || { + HttpService::build() + .h2(map_config( + App::new().service( + web::resource("/").route(web::to(|| HttpResponse::Ok())), + ), + |_| AppConfig::default(), + )) + .openssl(ssl_acceptor()) + .map_err(|_| ()) + }); + + // disable ssl verification + let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); + builder.set_verify(SslVerifyMode::NONE); + let _ = builder + .set_alpn_protos(b"\x02h2\x08http/1.1") + .map_err(|e| log::error!("Can not set alpn protocol: {:?}", e)); + + let client = awc::Client::build() + .connector(awc::Connector::new().ssl(builder.build()).finish()) + .initial_window_size(100) + .initial_connection_window_size(100) + .finish(); + + let request = client.get(srv.surl("/")).send(); + let response = request.await.unwrap(); + assert!(response.status().is_success()); + assert_eq!(response.version(), Version::HTTP_2); +} From cf721c5fff098af5d45ac5a1680ef421fc0f1bf4 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 8 Mar 2020 00:43:01 +0900 Subject: [PATCH 064/157] Update README example --- actix-http/README.md | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/actix-http/README.md b/actix-http/README.md index d75e822ba..9acad3e6d 100644 --- a/actix-http/README.md +++ b/actix-http/README.md @@ -14,19 +14,34 @@ Actix http ```rust // see examples/framed_hello.rs for complete list of used crates. -extern crate actix_http; -use actix_http::{h1, Response, ServiceConfig}; +use std::{env, io}; -fn main() { - Server::new().bind("framed_hello", "127.0.0.1:8080", || { - IntoFramed::new(|| h1::Codec::new(ServiceConfig::default())) // <- create h1 codec - .and_then(TakeItem::new().map_err(|_| ())) // <- read one request - .and_then(|(_req, _framed): (_, Framed<_, _>)| { // <- send response and close conn - SendResponse::send(_framed, Response::Ok().body("Hello world!")) - .map_err(|_| ()) - .map(|_| ()) - }) - }).unwrap().run(); +use actix_http::{HttpService, Response}; +use actix_server::Server; +use futures::future; +use http::header::HeaderValue; +use log::info; + +#[actix_rt::main] +async fn main() -> io::Result<()> { + env::set_var("RUST_LOG", "hello_world=info"); + env_logger::init(); + + Server::build() + .bind("hello-world", "127.0.0.1:8080", || { + HttpService::build() + .client_timeout(1000) + .client_disconnect(1000) + .finish(|_req| { + info!("{:?}", _req); + let mut res = Response::Ok(); + res.header("x-head", HeaderValue::from_static("dummy value!")); + future::ok::<_, ()>(res.body("Hello world!")) + }) + .tcp() + })? + .run() + .await } ``` From 7172885beb1edef4985157d6f9abd5c726d3af99 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 8 Mar 2020 00:43:17 +0900 Subject: [PATCH 065/157] Update changelog --- actix-http/CHANGES.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index ccfdc81d6..fb1c3a329 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,16 +1,20 @@ # Changes -## [2.0.0-alpha.2] - someday +## [2.0.0-alpha.2] - 2020-03-07 ### Changed -* Update `actix-connect` and `actix-tls` dependency to 2.0.0-alpha.1 +* Update `actix-connect` and `actix-tls` dependency to 2.0.0-alpha.1. [#1395] -* Change default initial window size and connection window size for HTTP2 to 2MB and 1MB respectively to improve download speed for awc when downloading large objects. +* Change default initial window size and connection window size for HTTP2 to 2MB and 1MB respectively + to improve download speed for awc when downloading large objects. [#1394] -* client::Connector accepts initial_window_size and initial_connection_window_size HTTP2 configuration +* client::Connector accepts initial_window_size and initial_connection_window_size HTTP2 configuration. [#1394] -* client::Connector allowing to set max_http_version to limit HTTP version to be used +* client::Connector allowing to set max_http_version to limit HTTP version to be used. [#1394] + +[#1394]: https://github.com/actix/actix-web/pull/1394 +[#1395]: https://github.com/actix/actix-web/pull/1395 ## [2.0.0-alpha.1] - 2020-02-27 From 6f63acaf01cacf1b36f622f0177a58c613656552 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 8 Mar 2020 00:48:45 +0900 Subject: [PATCH 066/157] Bump up to 2.0.0-alpha.2 --- actix-http/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index df9653aa3..c8b6d7d7d 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "2.0.0-alpha.1" +version = "2.0.0-alpha.2" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" From 7941594f94545d0a4b3a4e4a86faa9fcc8b2dfb9 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 8 Mar 2020 00:50:20 +0900 Subject: [PATCH 067/157] Update `actix-http` dependency --- Cargo.toml | 2 +- actix-files/Cargo.toml | 2 +- actix-framed/Cargo.toml | 2 +- actix-multipart/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- awc/Cargo.toml | 4 ++-- test-server/Cargo.toml | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fa29c225f..0cb0506ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,7 +71,7 @@ actix-threadpool = "0.3.1" actix-tls = "2.0.0-alpha.1" actix-web-codegen = "0.2.0" -actix-http = "2.0.0-alpha.1" +actix-http = "2.0.0-alpha.2" awc = { version = "1.0.1", default-features = false } bytes = "0.5.3" diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 269ea5371..c37d023f6 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -19,7 +19,7 @@ path = "src/lib.rs" [dependencies] actix-web = { version = "2.0.0-rc", default-features = false } -actix-http = "2.0.0-alpha.1" +actix-http = "2.0.0-alpha.2" actix-service = "1.0.1" bitflags = "1" bytes = "0.5.3" diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index 133b8083d..4389fe69b 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -23,7 +23,7 @@ actix-codec = "0.2.0" actix-service = "1.0.1" actix-router = "0.2.1" actix-rt = "1.0.0" -actix-http = "2.0.0-alpha.1" +actix-http = "2.0.0-alpha.2" bytes = "0.5.3" futures = "0.3.1" diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index a1d91c6a4..3c8fe6de1 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -29,4 +29,4 @@ twoway = "0.2" [dev-dependencies] actix-rt = "1.0.0" -actix-http = "2.0.0-alpha.1" +actix-http = "2.0.0-alpha.2" diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 333edb8c0..7941e192c 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -18,7 +18,7 @@ path = "src/lib.rs" [dependencies] actix = "0.10.0-alpha.1" actix-web = "2.0.0" -actix-http = "2.0.0-alpha.1" +actix-http = "2.0.0-alpha.2" actix-codec = "0.2.0" bytes = "0.5.2" futures = "0.3.1" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index d27615f7a..efbe0abd6 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -36,7 +36,7 @@ compress = ["actix-http/compress"] [dependencies] actix-codec = "0.2.0" actix-service = "1.0.1" -actix-http = "2.0.0-alpha.1" +actix-http = "2.0.0-alpha.2" actix-rt = "1.0.0" base64 = "0.11" @@ -56,7 +56,7 @@ rust-tls = { version = "0.17.0", package="rustls", optional = true, features = [ [dev-dependencies] actix-connect = { version = "2.0.0-alpha.1", features=["openssl"] } actix-web = { version = "2.0.0", features=["openssl"] } -actix-http = { version = "2.0.0-alpha.1", features=["openssl"] } +actix-http = { version = "2.0.0-alpha.2", features=["openssl"] } actix-http-test = { version = "1.0.0", features=["openssl"] } actix-utils = "1.0.3" actix-server = "1.0.0" diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index ba8e1c9ea..894099720 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -56,4 +56,4 @@ open-ssl = { version="0.10", package="openssl", optional = true } [dev-dependencies] actix-web = "2.0.0" -actix-http = "2.0.0-alpha.1" +actix-http = "2.0.0-alpha.2" From 0d5646a8b6f870b3182033313b644c613a0f667f Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 8 Mar 2020 00:52:39 +0900 Subject: [PATCH 068/157] Run rustfmt --- actix-http/src/client/connector.rs | 10 +++++----- actix-http/src/h1/dispatcher.rs | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index adb88bbed..e1aed6382 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -78,8 +78,7 @@ impl Connector<(), ()> { // Build Ssl connector with openssl, based on supplied alpn protocols #[cfg(feature = "openssl")] - fn build_ssl(protocols: Vec>) -> SslConnector - { + fn build_ssl(protocols: Vec>) -> SslConnector { use actix_connect::ssl::openssl::SslMethod; use bytes::{BufMut, BytesMut}; @@ -98,8 +97,7 @@ impl Connector<(), ()> { // Build Ssl connector with rustls, based on supplied alpn protocols #[cfg(all(not(feature = "openssl"), feature = "rustls"))] - fn build_ssl(protocols: Vec>) -> SslConnector - { + fn build_ssl(protocols: Vec>) -> SslConnector { let mut config = ClientConfig::new(); config.set_protocols(&protocols); config @@ -169,7 +167,9 @@ where let versions = match val { http::Version::HTTP_11 => vec![b"http/1.1".to_vec()], http::Version::HTTP_2 => vec![b"h2".to_vec(), b"http/1.1".to_vec()], - _ => unimplemented!("actix-http:client: supported versions http/1.1, http/2"), + _ => { + unimplemented!("actix-http:client: supported versions http/1.1, http/2") + } }; self.ssl = Connector::build_ssl(versions); self diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index ffdcd8d9b..ec01261e5 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -395,7 +395,8 @@ where Poll::Ready(Ok(req)) => { self.as_mut().send_continue(); this = self.as_mut().project(); - this.state.set(State::ServiceCall(Box::pin(this.service.call(req)))); + this.state + .set(State::ServiceCall(Box::pin(this.service.call(req)))); continue; } Poll::Ready(Err(e)) => { From 6b626c7d7762b8b19122618b08d93d18cd07422c Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 1 Mar 2020 10:44:26 +0900 Subject: [PATCH 069/157] dev-deps: Update `env_logger` to 0.7 --- awc/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awc/Cargo.toml b/awc/Cargo.toml index efbe0abd6..1db869f8a 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -64,5 +64,5 @@ actix-tls = { version = "2.0.0-alpha.1", features=["openssl", "rustls"] } brotli2 = "0.3.2" flate2 = "1.0.13" futures = "0.3.1" -env_logger = "0.6" +env_logger = "0.7" webpki = "0.21" From 294523a32fc58f01ba37ad3bd7f36498f8611a6c Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 1 Mar 2020 10:45:26 +0900 Subject: [PATCH 070/157] Bump up to 2.0.0-alpha.1 --- awc/CHANGES.md | 5 +++-- awc/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 1d6e03708..b3df19e56 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,9 +1,10 @@ # Changes -## [NEXT] +## [2.0.0-alpha.1] - 2020-03-08 +* Update `actix-http` dependency to 2.0.0-alpha.2 +* Update `rustls` dependency to 0.17 * ClientBuilder accepts initial_window_size and initial_connection_window_size HTTP2 configuration - * ClientBuilder allowing to set max_http_version to limit HTTP version to be used ## [1.0.1] - 2019-12-15 diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 1db869f8a..23c22e76e 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "1.0.1" +version = "2.0.0-alpha.1" authors = ["Nikolay Kim "] description = "Actix http client." readme = "README.md" From 6f33b7ea4248e2286de295b42aef95a716ad9d95 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 1 Mar 2020 10:47:24 +0900 Subject: [PATCH 071/157] Update `awc` dependency --- Cargo.toml | 2 +- test-server/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0cb0506ca..44e7f9aef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,7 @@ actix-tls = "2.0.0-alpha.1" actix-web-codegen = "0.2.0" actix-http = "2.0.0-alpha.2" -awc = { version = "1.0.1", default-features = false } +awc = { version = "2.0.0-alpha.1", default-features = false } bytes = "0.5.3" derive_more = "0.99.2" diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 894099720..32efafd48 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -37,7 +37,7 @@ actix-utils = "1.0.3" actix-rt = "1.0.0" actix-server = "1.0.0" actix-testing = "1.0.0" -awc = "1.0.1" +awc = "2.0.0-alpha.1" base64 = "0.11" bytes = "0.5.3" From b93e1555ec63e482763ae5d1accc4a36ae508c66 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 8 Mar 2020 15:23:38 +0900 Subject: [PATCH 072/157] Update `actix-connect` to 2.0.0-alpha.2 --- actix-framed/Cargo.toml | 2 +- actix-http/Cargo.toml | 4 ++-- awc/Cargo.toml | 2 +- test-server/CHANGES.md | 2 +- test-server/Cargo.toml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index 4389fe69b..adcda1422 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -32,6 +32,6 @@ log = "0.4" [dev-dependencies] actix-server = "1.0.0" -actix-connect = { version = "2.0.0-alpha.1", features=["openssl"] } +actix-connect = { version = "2.0.0-alpha.2", features=["openssl"] } actix-http-test = { version = "1.0.0", features=["openssl"] } actix-utils = "1.0.3" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index c8b6d7d7d..e78c74624 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -45,7 +45,7 @@ actors = ["actix"] [dependencies] actix-service = "1.0.5" actix-codec = "0.2.0" -actix-connect = "2.0.0-alpha.1" +actix-connect = "2.0.0-alpha.2" actix-utils = "1.0.6" actix-rt = "1.0.0" actix-threadpool = "0.3.1" @@ -94,7 +94,7 @@ fail-ure = { version = "0.1.5", package="failure", optional = true } [dev-dependencies] actix-server = "1.0.1" -actix-connect = { version = "2.0.0-alpha.1", features=["openssl"] } +actix-connect = { version = "2.0.0-alpha.2", features=["openssl"] } actix-http-test = { version = "1.0.0", features=["openssl"] } actix-tls = { version = "2.0.0-alpha.1", features=["openssl"] } criterion = "0.3" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 23c22e76e..5f4b493f8 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -54,7 +54,7 @@ open-ssl = { version="0.10", package="openssl", optional = true } rust-tls = { version = "0.17.0", package="rustls", optional = true, features = ["dangerous_configuration"] } [dev-dependencies] -actix-connect = { version = "2.0.0-alpha.1", features=["openssl"] } +actix-connect = { version = "2.0.0-alpha.2", features=["openssl"] } actix-web = { version = "2.0.0", features=["openssl"] } actix-http = { version = "2.0.0-alpha.2", features=["openssl"] } actix-http-test = { version = "1.0.0", features=["openssl"] } diff --git a/test-server/CHANGES.md b/test-server/CHANGES.md index 3a990a594..4a1a0cfcd 100644 --- a/test-server/CHANGES.md +++ b/test-server/CHANGES.md @@ -3,7 +3,7 @@ ## [Unreleased] - 2020-xx-xx * Update the `time` dependency to 0.2.7 -* Update `actix-connect` dependency to 2.0.0-alpha.1 +* Update `actix-connect` dependency to 2.0.0-alpha.2 ## [1.0.0] - 2019-12-13 diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 32efafd48..c34446fc8 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -32,7 +32,7 @@ openssl = ["open-ssl", "awc/openssl"] [dependencies] actix-service = "1.0.1" actix-codec = "0.2.0" -actix-connect = "2.0.0-alpha.1" +actix-connect = "2.0.0-alpha.2" actix-utils = "1.0.3" actix-rt = "1.0.0" actix-server = "1.0.0" From a9a475d5554a4cb891c3d3e6a019a6249d997f64 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 8 Mar 2020 16:42:26 +0900 Subject: [PATCH 073/157] Make `test_server` `async` fn --- test-server/CHANGES.md | 1 + test-server/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test-server/CHANGES.md b/test-server/CHANGES.md index 4a1a0cfcd..35552030c 100644 --- a/test-server/CHANGES.md +++ b/test-server/CHANGES.md @@ -4,6 +4,7 @@ * Update the `time` dependency to 0.2.7 * Update `actix-connect` dependency to 2.0.0-alpha.2 +* Make `test_server` `async` fn. ## [1.0.0] - 2019-12-13 diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index 27326c67a..c36cc706f 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -43,7 +43,7 @@ pub use actix_testing::*; /// assert!(response.status().is_success()); /// } /// ``` -pub fn test_server>(factory: F) -> TestServer { +pub async fn test_server>(factory: F) -> TestServer { let (tx, rx) = mpsc::channel(); // run server in separate thread @@ -92,7 +92,7 @@ pub fn test_server>(factory: F) -> TestServer { Client::build().connector(connector).finish() }; - actix_connect::start_default_resolver(); + actix_connect::start_default_resolver().await.unwrap(); TestServer { addr, From e718f6512183d4c3d2515bfc378874d2c32dd91d Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 8 Mar 2020 16:42:45 +0900 Subject: [PATCH 074/157] Update tests --- actix-framed/tests/test_server.rs | 6 ++- actix-http/tests/test_client.rs | 9 ++-- actix-http/tests/test_openssl.rs | 41 +++++++++++------ actix-http/tests/test_rustls.rs | 47 +++++++++++++------- actix-http/tests/test_server.rs | 74 ++++++++++++++++++++----------- actix-http/tests/test_ws.rs | 3 +- awc/src/builder.rs | 8 ++-- awc/tests/test_client.rs | 33 +++++++------- awc/tests/test_connector.rs | 8 ++-- awc/tests/test_rustls_client.rs | 3 +- awc/tests/test_ssl_client.rs | 3 +- awc/tests/test_ws.rs | 3 +- 12 files changed, 151 insertions(+), 87 deletions(-) diff --git a/actix-framed/tests/test_server.rs b/actix-framed/tests/test_server.rs index 7d6fc08a6..150fc10f3 100644 --- a/actix-framed/tests/test_server.rs +++ b/actix-framed/tests/test_server.rs @@ -47,7 +47,8 @@ async fn test_simple() { ) .finish(|_| future::ok::<_, Error>(Response::NotFound())) .tcp() - }); + }) + .await; assert!(srv.ws_at("/test").await.is_err()); @@ -108,7 +109,8 @@ async fn test_service() { .map_err(|_| ()), ), ) - }); + }) + .await; // non ws request let res = srv.get("/index.html").send().await.unwrap(); diff --git a/actix-http/tests/test_client.rs b/actix-http/tests/test_client.rs index 9da3b04a2..5347971a6 100644 --- a/actix-http/tests/test_client.rs +++ b/actix-http/tests/test_client.rs @@ -33,7 +33,8 @@ async fn test_h1_v2() { HttpService::build() .finish(|_| future::ok::<_, ()>(Response::Ok().body(STR))) .tcp() - }); + }) + .await; let response = srv.get("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -61,7 +62,8 @@ async fn test_connection_close() { .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) .tcp() .map(|_| ()) - }); + }) + .await; let response = srv.get("/").force_close().send().await.unwrap(); assert!(response.status().is_success()); @@ -80,7 +82,8 @@ async fn test_with_query_parameter() { }) .tcp() .map(|_| ()) - }); + }) + .await; let request = srv.request(http::Method::GET, srv.url("/?qp=5")); let response = request.send().await.unwrap(); diff --git a/actix-http/tests/test_openssl.rs b/actix-http/tests/test_openssl.rs index 77caa045b..4af3a0a47 100644 --- a/actix-http/tests/test_openssl.rs +++ b/actix-http/tests/test_openssl.rs @@ -67,7 +67,8 @@ async fn test_h2() -> io::Result<()> { .h2(|_| ok::<_, Error>(Response::Ok().finish())) .openssl(ssl_acceptor()) .map_err(|_| ()) - }); + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -85,7 +86,8 @@ async fn test_h2_1() -> io::Result<()> { }) .openssl(ssl_acceptor()) .map_err(|_| ()) - }); + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -103,7 +105,8 @@ async fn test_h2_body() -> io::Result<()> { }) .openssl(ssl_acceptor()) .map_err(|_| ()) - }); + }) + .await; let response = srv.sget("/").send_body(data.clone()).await.unwrap(); assert!(response.status().is_success()); @@ -131,7 +134,8 @@ async fn test_h2_content_length() { }) .openssl(ssl_acceptor()) .map_err(|_| ()) - }); + }) + .await; let header = HeaderName::from_static("content-length"); let value = HeaderValue::from_static("0"); @@ -192,7 +196,7 @@ async fn test_h2_headers() { }) .openssl(ssl_acceptor()) .map_err(|_| ()) - }); + }).await; let response = srv.sget("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -231,7 +235,8 @@ async fn test_h2_body2() { .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) .openssl(ssl_acceptor()) .map_err(|_| ()) - }); + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -248,7 +253,8 @@ async fn test_h2_head_empty() { .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) .openssl(ssl_acceptor()) .map_err(|_| ()) - }); + }) + .await; let response = srv.shead("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -273,7 +279,8 @@ async fn test_h2_head_binary() { }) .openssl(ssl_acceptor()) .map_err(|_| ()) - }); + }) + .await; let response = srv.shead("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -295,7 +302,8 @@ async fn test_h2_head_binary2() { .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) .openssl(ssl_acceptor()) .map_err(|_| ()) - }); + }) + .await; let response = srv.shead("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -318,7 +326,8 @@ async fn test_h2_body_length() { }) .openssl(ssl_acceptor()) .map_err(|_| ()) - }); + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -342,7 +351,8 @@ async fn test_h2_body_chunked_explicit() { }) .openssl(ssl_acceptor()) .map_err(|_| ()) - }); + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -369,7 +379,8 @@ async fn test_h2_response_http_error_handling() { })) .openssl(ssl_acceptor()) .map_err(|_| ()) - }); + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR); @@ -386,7 +397,8 @@ async fn test_h2_service_error() { .h2(|_| err::(ErrorBadRequest("error"))) .openssl(ssl_acceptor()) .map_err(|_| ()) - }); + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert_eq!(response.status(), StatusCode::BAD_REQUEST); @@ -407,7 +419,8 @@ async fn test_h2_on_connect() { }) .openssl(ssl_acceptor()) .map_err(|_| ()) - }); + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert!(response.status().is_success()); diff --git a/actix-http/tests/test_rustls.rs b/actix-http/tests/test_rustls.rs index 933a6c894..1c5583b08 100644 --- a/actix-http/tests/test_rustls.rs +++ b/actix-http/tests/test_rustls.rs @@ -45,7 +45,8 @@ async fn test_h1() -> io::Result<()> { HttpService::build() .h1(|_| future::ok::<_, Error>(Response::Ok().finish())) .rustls(ssl_acceptor()) - }); + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -58,7 +59,8 @@ async fn test_h2() -> io::Result<()> { HttpService::build() .h2(|_| future::ok::<_, Error>(Response::Ok().finish())) .rustls(ssl_acceptor()) - }); + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -75,7 +77,8 @@ async fn test_h1_1() -> io::Result<()> { future::ok::<_, Error>(Response::Ok().finish()) }) .rustls(ssl_acceptor()) - }); + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -92,7 +95,8 @@ async fn test_h2_1() -> io::Result<()> { future::ok::<_, Error>(Response::Ok().finish()) }) .rustls(ssl_acceptor()) - }); + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -109,7 +113,8 @@ async fn test_h2_body1() -> io::Result<()> { Ok::<_, Error>(Response::Ok().body(body)) }) .rustls(ssl_acceptor()) - }); + }) + .await; let response = srv.sget("/").send_body(data.clone()).await.unwrap(); assert!(response.status().is_success()); @@ -136,7 +141,8 @@ async fn test_h2_content_length() { future::ok::<_, ()>(Response::new(statuses[indx])) }) .rustls(ssl_acceptor()) - }); + }) + .await; let header = HeaderName::from_static("content-length"); let value = HeaderValue::from_static("0"); @@ -195,7 +201,7 @@ async fn test_h2_headers() { future::ok::<_, ()>(config.body(data.clone())) }) .rustls(ssl_acceptor()) - }); + }).await; let response = srv.sget("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -233,7 +239,8 @@ async fn test_h2_body2() { HttpService::build() .h2(|_| future::ok::<_, ()>(Response::Ok().body(STR))) .rustls(ssl_acceptor()) - }); + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -249,7 +256,8 @@ async fn test_h2_head_empty() { HttpService::build() .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) .rustls(ssl_acceptor()) - }); + }) + .await; let response = srv.shead("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -276,7 +284,8 @@ async fn test_h2_head_binary() { ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) }) .rustls(ssl_acceptor()) - }); + }) + .await; let response = srv.shead("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -300,7 +309,8 @@ async fn test_h2_head_binary2() { HttpService::build() .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) .rustls(ssl_acceptor()) - }); + }) + .await; let response = srv.shead("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -325,7 +335,8 @@ async fn test_h2_body_length() { ) }) .rustls(ssl_acceptor()) - }); + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -348,7 +359,8 @@ async fn test_h2_body_chunked_explicit() { ) }) .rustls(ssl_acceptor()) - }); + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -376,7 +388,8 @@ async fn test_h2_response_http_error_handling() { })) })) .rustls(ssl_acceptor()) - }); + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR); @@ -392,7 +405,8 @@ async fn test_h2_service_error() { HttpService::build() .h2(|_| err::(error::ErrorBadRequest("error"))) .rustls(ssl_acceptor()) - }); + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); @@ -408,7 +422,8 @@ async fn test_h1_service_error() { HttpService::build() .h1(|_| err::(error::ErrorBadRequest("error"))) .rustls(ssl_acceptor()) - }); + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index a84692f9d..1ec819434 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -27,7 +27,8 @@ async fn test_h1() { future::ok::<_, ()>(Response::Ok().finish()) }) .tcp() - }); + }) + .await; let response = srv.get("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -46,7 +47,8 @@ async fn test_h1_2() { future::ok::<_, ()>(Response::Ok().finish()) }) .tcp() - }); + }) + .await; let response = srv.get("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -65,7 +67,8 @@ async fn test_expect_continue() { })) .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) .tcp() - }); + }) + .await; let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let _ = stream.write_all(b"GET /test HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); @@ -95,7 +98,8 @@ async fn test_expect_continue_h1() { })) .h1(fn_service(|_| future::ok::<_, ()>(Response::Ok().finish()))) .tcp() - }); + }) + .await; let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let _ = stream.write_all(b"GET /test HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); @@ -130,7 +134,8 @@ async fn test_chunked_payload() { }) })) .tcp() - }); + }) + .await; let returned_size = { let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); @@ -172,7 +177,8 @@ async fn test_slow_request() { .client_timeout(100) .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) .tcp() - }); + }) + .await; let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n"); @@ -187,7 +193,8 @@ async fn test_http1_malformed_request() { HttpService::build() .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .tcp() - }); + }) + .await; let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let _ = stream.write_all(b"GET /test/tests/test HTTP1.1\r\n"); @@ -202,7 +209,8 @@ async fn test_http1_keepalive() { HttpService::build() .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .tcp() - }); + }) + .await; let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); @@ -223,7 +231,8 @@ async fn test_http1_keepalive_timeout() { .keep_alive(1) .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .tcp() - }); + }) + .await; let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); @@ -243,7 +252,8 @@ async fn test_http1_keepalive_close() { HttpService::build() .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .tcp() - }); + }) + .await; let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let _ = @@ -263,7 +273,8 @@ async fn test_http10_keepalive_default_close() { HttpService::build() .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .tcp() - }); + }) + .await; let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let _ = stream.write_all(b"GET /test/tests/test HTTP/1.0\r\n\r\n"); @@ -282,7 +293,8 @@ async fn test_http10_keepalive() { HttpService::build() .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .tcp() - }); + }) + .await; let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let _ = stream @@ -309,7 +321,8 @@ async fn test_http1_keepalive_disabled() { .keep_alive(KeepAlive::Disabled) .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .tcp() - }); + }) + .await; let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); @@ -344,7 +357,8 @@ async fn test_content_length() { future::ok::<_, ()>(Response::new(statuses[indx])) }) .tcp() - }); + }) + .await; let header = HeaderName::from_static("content-length"); let value = HeaderValue::from_static("0"); @@ -397,7 +411,7 @@ async fn test_h1_headers() { } future::ok::<_, ()>(builder.body(data.clone())) }).tcp() - }); + }).await; let response = srv.get("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -435,7 +449,8 @@ async fn test_h1_body() { HttpService::build() .h1(|_| ok::<_, ()>(Response::Ok().body(STR))) .tcp() - }); + }) + .await; let response = srv.get("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -451,7 +466,8 @@ async fn test_h1_head_empty() { HttpService::build() .h1(|_| ok::<_, ()>(Response::Ok().body(STR))) .tcp() - }); + }) + .await; let response = srv.head("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -477,7 +493,8 @@ async fn test_h1_head_binary() { ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) }) .tcp() - }); + }) + .await; let response = srv.head("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -501,7 +518,8 @@ async fn test_h1_head_binary2() { HttpService::build() .h1(|_| ok::<_, ()>(Response::Ok().body(STR))) .tcp() - }); + }) + .await; let response = srv.head("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -526,7 +544,8 @@ async fn test_h1_body_length() { ) }) .tcp() - }); + }) + .await; let response = srv.get("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -549,7 +568,8 @@ async fn test_h1_body_chunked_explicit() { ) }) .tcp() - }); + }) + .await; let response = srv.get("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -579,7 +599,8 @@ async fn test_h1_body_chunked_implicit() { ok::<_, ()>(Response::Ok().streaming(body)) }) .tcp() - }); + }) + .await; let response = srv.get("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -611,7 +632,8 @@ async fn test_h1_response_http_error_handling() { ) })) .tcp() - }); + }) + .await; let response = srv.get("/").send().await.unwrap(); assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR); @@ -627,7 +649,8 @@ async fn test_h1_service_error() { HttpService::build() .h1(|_| future::err::(error::ErrorBadRequest("error"))) .tcp() - }); + }) + .await; let response = srv.get("/").send().await.unwrap(); assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); @@ -647,7 +670,8 @@ async fn test_h1_on_connect() { future::ok::<_, ()>(Response::Ok().finish()) }) .tcp() - }); + }) + .await; let response = srv.get("/").send().await.unwrap(); assert!(response.status().is_success()); diff --git a/actix-http/tests/test_ws.rs b/actix-http/tests/test_ws.rs index 7152fee48..4b4b8f089 100644 --- a/actix-http/tests/test_ws.rs +++ b/actix-http/tests/test_ws.rs @@ -93,7 +93,8 @@ async fn test_simple() { .finish(|_| future::ok::<_, ()>(Response::NotFound())) .tcp() } - }); + }) + .await; // client service let mut framed = srv.ws().await.unwrap(); diff --git a/awc/src/builder.rs b/awc/src/builder.rs index 2b2e5df9f..7cd659c38 100644 --- a/awc/src/builder.rs +++ b/awc/src/builder.rs @@ -5,10 +5,10 @@ use std::rc::Rc; use std::time::Duration; use actix_http::client::{Connect as HttpConnect, ConnectError, Connection, Connector}; -use actix_http::http::{header, Error as HttpError, HeaderMap, HeaderName, self}; +use actix_http::http::{self, header, Error as HttpError, HeaderMap, HeaderName}; use actix_service::Service; -use crate::connect::{ConnectorWrapper, Connect}; +use crate::connect::{Connect, ConnectorWrapper}; use crate::{Client, ClientConfig}; /// An HTTP Client builder @@ -182,7 +182,9 @@ impl ClientBuilder { if let Some(val) = self.stream_window_size { connector = connector.initial_window_size(val) }; - RefCell::new(Box::new(ConnectorWrapper(connector.finish())) as Box) + RefCell::new( + Box::new(ConnectorWrapper(connector.finish())) as Box + ) }; let config = ClientConfig { headers: self.headers, diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs index 8fb04b005..449734a9f 100644 --- a/awc/tests/test_client.rs +++ b/awc/tests/test_client.rs @@ -107,17 +107,15 @@ async fn test_form() { #[actix_rt::test] async fn test_timeout() { let srv = test::start(|| { - App::new().service(web::resource("/").route(web::to(|| { - async { - actix_rt::time::delay_for(Duration::from_millis(200)).await; - Ok::<_, Error>(HttpResponse::Ok().body(STR)) - } + App::new().service(web::resource("/").route(web::to(|| async { + actix_rt::time::delay_for(Duration::from_millis(200)).await; + Ok::<_, Error>(HttpResponse::Ok().body(STR)) }))) }); let connector = awc::Connector::new() .connector(actix_connect::new_connector( - actix_connect::start_default_resolver(), + actix_connect::start_default_resolver().await.unwrap(), )) .timeout(Duration::from_secs(15)) .finish(); @@ -137,11 +135,9 @@ async fn test_timeout() { #[actix_rt::test] async fn test_timeout_override() { let srv = test::start(|| { - App::new().service(web::resource("/").route(web::to(|| { - async { - actix_rt::time::delay_for(Duration::from_millis(200)).await; - Ok::<_, Error>(HttpResponse::Ok().body(STR)) - } + App::new().service(web::resource("/").route(web::to(|| async { + actix_rt::time::delay_for(Duration::from_millis(200)).await; + Ok::<_, Error>(HttpResponse::Ok().body(STR)) }))) }); @@ -177,7 +173,8 @@ async fn test_connection_reuse() { )) .tcp(), ) - }); + }) + .await; let client = awc::Client::default(); @@ -214,7 +211,8 @@ async fn test_connection_force_close() { )) .tcp(), ) - }); + }) + .await; let client = awc::Client::default(); @@ -253,7 +251,8 @@ async fn test_connection_server_close() { )) .tcp(), ) - }); + }) + .await; let client = awc::Client::default(); @@ -291,7 +290,8 @@ async fn test_connection_wait_queue() { )) .tcp(), ) - }); + }) + .await; let client = awc::Client::build() .connector(awc::Connector::new().limit(1).finish()) @@ -339,7 +339,8 @@ async fn test_connection_wait_queue_force_close() { )) .tcp(), ) - }); + }) + .await; let client = awc::Client::build() .connector(awc::Connector::new().limit(1).finish()) diff --git a/awc/tests/test_connector.rs b/awc/tests/test_connector.rs index 8b295cda7..b352eaab4 100644 --- a/awc/tests/test_connector.rs +++ b/awc/tests/test_connector.rs @@ -32,14 +32,14 @@ async fn test_connection_window_size() { let srv = test_server(move || { HttpService::build() .h2(map_config( - App::new().service( - web::resource("/").route(web::to(|| HttpResponse::Ok())), - ), + App::new() + .service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), |_| AppConfig::default(), )) .openssl(ssl_acceptor()) .map_err(|_| ()) - }); + }) + .await; // disable ssl verification let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); diff --git a/awc/tests/test_rustls_client.rs b/awc/tests/test_rustls_client.rs index 8863dfcbe..7407a33fc 100644 --- a/awc/tests/test_rustls_client.rs +++ b/awc/tests/test_rustls_client.rs @@ -72,7 +72,8 @@ async fn _test_connection_reuse_h2() { .openssl(ssl_acceptor()) .map_err(|_| ()), ) - }); + }) + .await; // disable ssl verification let mut config = ClientConfig::new(); diff --git a/awc/tests/test_ssl_client.rs b/awc/tests/test_ssl_client.rs index d3995b4be..8e128ad0c 100644 --- a/awc/tests/test_ssl_client.rs +++ b/awc/tests/test_ssl_client.rs @@ -53,7 +53,8 @@ async fn test_connection_reuse_h2() { .openssl(ssl_acceptor()) .map_err(|_| ()), ) - }); + }) + .await; // disable ssl verification let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); diff --git a/awc/tests/test_ws.rs b/awc/tests/test_ws.rs index ee937e43e..779a4301c 100644 --- a/awc/tests/test_ws.rs +++ b/awc/tests/test_ws.rs @@ -38,7 +38,8 @@ async fn test_simple() { }) .finish(|_| ok::<_, Error>(Response::NotFound())) .tcp() - }); + }) + .await; // client service let mut framed = srv.ws().await.unwrap(); From ce1e996b411bd7fa3855c6ee7a406d08fe710834 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 11 Mar 2020 20:42:45 +0900 Subject: [PATCH 075/157] Update release date --- awc/CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awc/CHANGES.md b/awc/CHANGES.md index b3df19e56..e6f731540 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [2.0.0-alpha.1] - 2020-03-08 +## [2.0.0-alpha.1] - 2020-03-11 * Update `actix-http` dependency to 2.0.0-alpha.2 * Update `rustls` dependency to 0.17 From 95f9a12a5e3fc5759b4ba2f9ea474a4d7735d01e Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 12 Mar 2020 02:58:22 +0900 Subject: [PATCH 076/157] dev-deps: Update `env_logger` to 0.7 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 44e7f9aef..3cd6a5a61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -95,7 +95,7 @@ rust-tls = { version = "0.17.0", package = "rustls", optional = true } [dev-dependencies] actix = "0.10.0-alpha.1" rand = "0.7" -env_logger = "0.6" +env_logger = "0.7" serde_derive = "1.0" brotli2 = "0.3.2" flate2 = "1.0.13" From c147b9483290a9707253d0b5c1f0c3e7d042c08d Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 12 Mar 2020 03:03:22 +0900 Subject: [PATCH 077/157] Bump up to 3.0.0-alpha.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3cd6a5a61..4d50f50ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "2.0.0" +version = "3.0.0-alpha.1" authors = ["Nikolay Kim "] description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." readme = "README.md" From 1b28a5d48bc54490096589cc08e866ffb39594f3 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 12 Mar 2020 03:03:50 +0900 Subject: [PATCH 078/157] Update `actix-web` dependency to 3.0.0-alpha.1 --- actix-files/Cargo.toml | 4 ++-- actix-multipart/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- actix-web-codegen/Cargo.toml | 2 +- awc/Cargo.toml | 2 +- test-server/Cargo.toml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index c37d023f6..ed887e323 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -18,7 +18,7 @@ name = "actix_files" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-rc", default-features = false } +actix-web = { version = "3.0.0-alpha.1", default-features = false } actix-http = "2.0.0-alpha.2" actix-service = "1.0.1" bitflags = "1" @@ -33,4 +33,4 @@ v_htmlescape = "0.4" [dev-dependencies] actix-rt = "1.0.0" -actix-web = { version = "2.0.0-rc", features=["openssl"] } +actix-web = { version = "3.0.0-alpha.1", features=["openssl"] } diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 3c8fe6de1..e81b07cb7 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -16,7 +16,7 @@ name = "actix_multipart" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0", default-features = false } +actix-web = { version = "3.0.0-alpha.1", default-features = false } actix-service = "1.0.1" actix-utils = "1.0.3" bytes = "0.5.3" diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 7941e192c..7e24d8c99 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -17,7 +17,7 @@ path = "src/lib.rs" [dependencies] actix = "0.10.0-alpha.1" -actix-web = "2.0.0" +actix-web = "3.0.0-alpha.1" actix-http = "2.0.0-alpha.2" actix-codec = "0.2.0" bytes = "0.5.2" diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 0b926b807..42befc0dd 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -18,5 +18,5 @@ proc-macro2 = "^1" [dev-dependencies] actix-rt = "1.0.0" -actix-web = "2.0.0" +actix-web = "3.0.0-alpha.1" futures = "0.3.1" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 5f4b493f8..fde136eb0 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -55,7 +55,7 @@ rust-tls = { version = "0.17.0", package="rustls", optional = true, features = [ [dev-dependencies] actix-connect = { version = "2.0.0-alpha.2", features=["openssl"] } -actix-web = { version = "2.0.0", features=["openssl"] } +actix-web = { version = "3.0.0-alpha.1", features=["openssl"] } actix-http = { version = "2.0.0-alpha.2", features=["openssl"] } actix-http-test = { version = "1.0.0", features=["openssl"] } actix-utils = "1.0.3" diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index c34446fc8..29896898e 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -55,5 +55,5 @@ time = { version = "0.2.7", default-features = false, features = ["std"] } open-ssl = { version="0.10", package="openssl", optional = true } [dev-dependencies] -actix-web = "2.0.0" +actix-web = "3.0.0-alpha.1" actix-http = "2.0.0-alpha.2" From a6a47b95ee6ffb27d9a6ff903cfa986f02e9a6e7 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 12 Mar 2020 03:04:31 +0900 Subject: [PATCH 079/157] Exclude `actix-cors` --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4d50f50ce..1ab2561e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ members = [ ".", "awc", "actix-http", - "actix-cors", +# "actix-cors", "actix-files", "actix-framed", # "actix-session", From 0152cedc5df4e41cd9f694ee17523b742ba67157 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 12 Mar 2020 03:24:15 +0900 Subject: [PATCH 080/157] Update changelog --- CHANGES.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index da4a77a80..460b24f57 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,6 @@ # Changes - -## [2.0.NEXT] - 2020-01-xx +## [3.0.0-alpha.1] - 2020-03-11 ### Added @@ -10,11 +9,13 @@ ### Changed * Use `sha-1` crate instead of unmaintained `sha1` crate -* Skip empty chunks when returning response from a `Stream` #1308 +* Skip empty chunks when returning response from a `Stream` [#1308] * Update the `time` dependency to 0.2.7 * Update `actix-tls` dependency to 2.0.0-alpha.1 * Update `rustls` dependency to 0.17 +[#1308]: https://github.com/actix/actix-web/pull/1308 + ## [2.0.0] - 2019-12-25 ### Changed From c02d3e205b68983c4033c9fe578f4ae1bfaf8e61 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 12 Mar 2020 07:57:20 +0900 Subject: [PATCH 081/157] Clean-up metadata --- Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1ab2561e7..6bd36279a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,9 +111,6 @@ actix-web = { path = "." } actix-http = { path = "actix-http" } actix-http-test = { path = "test-server" } actix-web-codegen = { path = "actix-web-codegen" } -actix-cors = { path = "actix-cors" } -actix-identity = { path = "actix-identity" } -actix-session = { path = "actix-session" } actix-files = { path = "actix-files" } actix-multipart = { path = "actix-multipart" } awc = { path = "awc" } From 51518721e531983616460578fed647bcdf9a7673 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 12 Mar 2020 07:57:38 +0900 Subject: [PATCH 082/157] Add notes to READMEs --- actix-cors/README.md | 2 ++ actix-identity/README.md | 2 ++ actix-session/README.md | 2 ++ 3 files changed, 6 insertions(+) diff --git a/actix-cors/README.md b/actix-cors/README.md index a77f6c6d3..c860ec5ae 100644 --- a/actix-cors/README.md +++ b/actix-cors/README.md @@ -1,5 +1,7 @@ # Cors Middleware for actix web framework [![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) [![crates.io](https://meritbadge.herokuapp.com/actix-cors)](https://crates.io/crates/actix-cors) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +**This crate moved to https://github.com/actix/actix-extras.** + ## Documentation & community resources * [User Guide](https://actix.rs/docs/) diff --git a/actix-identity/README.md b/actix-identity/README.md index 60b615c76..62a40137f 100644 --- a/actix-identity/README.md +++ b/actix-identity/README.md @@ -1,5 +1,7 @@ # Identity service for actix web framework [![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) [![crates.io](https://meritbadge.herokuapp.com/actix-identity)](https://crates.io/crates/actix-identity) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +**This crate moved to https://github.com/actix/actix-extras.** + ## Documentation & community resources * [User Guide](https://actix.rs/docs/) diff --git a/actix-session/README.md b/actix-session/README.md index 0aee756fd..00e580120 100644 --- a/actix-session/README.md +++ b/actix-session/README.md @@ -1,5 +1,7 @@ # Session for actix web framework [![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) [![crates.io](https://meritbadge.herokuapp.com/actix-session)](https://crates.io/crates/actix-session) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +**This crate moved to https://github.com/actix/actix-extras.** + ## Documentation & community resources * [User Guide](https://actix.rs/docs/) From 7e0d898d5aff2439623af0ad08e8307a69cb48e7 Mon Sep 17 00:00:00 2001 From: Otavio Salvador Date: Thu, 12 Mar 2020 00:52:21 -0300 Subject: [PATCH 083/157] Fix clippy warnings Signed-off-by: Otavio Salvador --- awc/src/sender.rs | 1 - src/types/json.rs | 1 - src/types/query.rs | 1 - tests/test_weird_poll.rs | 2 +- 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/awc/src/sender.rs b/awc/src/sender.rs index ec18f12e3..983e730e1 100644 --- a/awc/src/sender.rs +++ b/awc/src/sender.rs @@ -9,7 +9,6 @@ use bytes::Bytes; use derive_more::From; use futures_core::{Future, Stream}; use serde::Serialize; -use serde_json; use actix_http::body::{Body, BodyStream}; use actix_http::http::header::{self, IntoHeaderValue}; diff --git a/src/types/json.rs b/src/types/json.rs index fb00bf7a6..ead2d0c9a 100644 --- a/src/types/json.rs +++ b/src/types/json.rs @@ -11,7 +11,6 @@ use futures::future::{err, ok, FutureExt, LocalBoxFuture, Ready}; use futures::StreamExt; use serde::de::DeserializeOwned; use serde::Serialize; -use serde_json; use actix_http::http::{header::CONTENT_LENGTH, StatusCode}; use actix_http::{HttpMessage, Payload, Response}; diff --git a/src/types/query.rs b/src/types/query.rs index a6c18d9be..73ea14f17 100644 --- a/src/types/query.rs +++ b/src/types/query.rs @@ -6,7 +6,6 @@ use std::{fmt, ops}; use actix_http::error::Error; use futures::future::{err, ok, Ready}; use serde::de; -use serde_urlencoded; use crate::dev::Payload; use crate::error::QueryPayloadError; diff --git a/tests/test_weird_poll.rs b/tests/test_weird_poll.rs index 571b69f45..7e4300901 100644 --- a/tests/test_weird_poll.rs +++ b/tests/test_weird_poll.rs @@ -1,11 +1,11 @@ // Regression test for #/1321 +/* use futures::task::{noop_waker, Context}; use futures::stream::once; use actix_http::body::{MessageBody, BodyStream}; use bytes::Bytes; -/* Disable weird poll until actix-web is based on actix-http 2.0.0 #[test] From d602a7e3863fbc8fcd7e0bf950c3c72ede529fd8 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 13 Mar 2020 05:52:58 +0900 Subject: [PATCH 084/157] Fix `read_body` doc --- src/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test.rs b/src/test.rs index 0eb02ff7c..15e66a230 100644 --- a/src/test.rs +++ b/src/test.rs @@ -187,7 +187,7 @@ where /// .to_request(); /// /// let resp = test::call_service(&mut app, req).await; -/// let result = test::read_body(resp); +/// let result = test::read_body(resp).await; /// assert_eq!(result, Bytes::from_static(b"welcome!")); /// } /// ``` From c67e4c1fe8ee80dd736dba1bfd133652df493c44 Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Sun, 15 Mar 2020 01:23:28 +0300 Subject: [PATCH 085/157] Refactored macros (#1333) Co-authored-by: Yuki Okushi --- actix-web-codegen/src/lib.rs | 83 ++++++++++++++++------------------ actix-web-codegen/src/route.rs | 37 ++++++++------- 2 files changed, 56 insertions(+), 64 deletions(-) diff --git a/actix-web-codegen/src/lib.rs b/actix-web-codegen/src/lib.rs index 0a727ed69..affb4dfec 100644 --- a/actix-web-codegen/src/lib.rs +++ b/actix-web-codegen/src/lib.rs @@ -45,6 +45,8 @@ extern crate proc_macro; mod route; use proc_macro::TokenStream; +use quote::ToTokens; +use route::Route; use syn::parse_macro_input; /// Creates route handler with `GET` method guard. @@ -58,11 +60,10 @@ use syn::parse_macro_input; #[proc_macro_attribute] pub fn get(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as syn::AttributeArgs); - let gen = match route::Route::new(args, input, route::GuardType::Get) { - Ok(gen) => gen, - Err(err) => return err.to_compile_error().into(), - }; - gen.generate() + match Route::new(args, input, route::GuardType::Get) { + Ok(route) => route.into_token_stream().into(), + Err(err) => err.to_compile_error().into(), + } } /// Creates route handler with `POST` method guard. @@ -73,11 +74,10 @@ pub fn get(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn post(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as syn::AttributeArgs); - let gen = match route::Route::new(args, input, route::GuardType::Post) { - Ok(gen) => gen, - Err(err) => return err.to_compile_error().into(), - }; - gen.generate() + match Route::new(args, input, route::GuardType::Post) { + Ok(route) => route.into_token_stream().into(), + Err(err) => err.to_compile_error().into(), + } } /// Creates route handler with `PUT` method guard. @@ -88,11 +88,10 @@ pub fn post(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn put(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as syn::AttributeArgs); - let gen = match route::Route::new(args, input, route::GuardType::Put) { - Ok(gen) => gen, - Err(err) => return err.to_compile_error().into(), - }; - gen.generate() + match Route::new(args, input, route::GuardType::Put) { + Ok(route) => route.into_token_stream().into(), + Err(err) => err.to_compile_error().into(), + } } /// Creates route handler with `DELETE` method guard. @@ -103,11 +102,10 @@ pub fn put(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn delete(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as syn::AttributeArgs); - let gen = match route::Route::new(args, input, route::GuardType::Delete) { - Ok(gen) => gen, - Err(err) => return err.to_compile_error().into(), - }; - gen.generate() + match Route::new(args, input, route::GuardType::Delete) { + Ok(route) => route.into_token_stream().into(), + Err(err) => err.to_compile_error().into(), + } } /// Creates route handler with `HEAD` method guard. @@ -118,11 +116,10 @@ pub fn delete(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn head(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as syn::AttributeArgs); - let gen = match route::Route::new(args, input, route::GuardType::Head) { - Ok(gen) => gen, - Err(err) => return err.to_compile_error().into(), - }; - gen.generate() + match Route::new(args, input, route::GuardType::Head) { + Ok(route) => route.into_token_stream().into(), + Err(err) => err.to_compile_error().into(), + } } /// Creates route handler with `CONNECT` method guard. @@ -133,11 +130,10 @@ pub fn head(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn connect(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as syn::AttributeArgs); - let gen = match route::Route::new(args, input, route::GuardType::Connect) { - Ok(gen) => gen, - Err(err) => return err.to_compile_error().into(), - }; - gen.generate() + match Route::new(args, input, route::GuardType::Connect) { + Ok(route) => route.into_token_stream().into(), + Err(err) => err.to_compile_error().into(), + } } /// Creates route handler with `OPTIONS` method guard. @@ -148,11 +144,10 @@ pub fn connect(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn options(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as syn::AttributeArgs); - let gen = match route::Route::new(args, input, route::GuardType::Options) { - Ok(gen) => gen, - Err(err) => return err.to_compile_error().into(), - }; - gen.generate() + match Route::new(args, input, route::GuardType::Options) { + Ok(route) => route.into_token_stream().into(), + Err(err) => err.to_compile_error().into(), + } } /// Creates route handler with `TRACE` method guard. @@ -163,11 +158,10 @@ pub fn options(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn trace(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as syn::AttributeArgs); - let gen = match route::Route::new(args, input, route::GuardType::Trace) { - Ok(gen) => gen, - Err(err) => return err.to_compile_error().into(), - }; - gen.generate() + match Route::new(args, input, route::GuardType::Trace) { + Ok(route) => route.into_token_stream().into(), + Err(err) => err.to_compile_error().into(), + } } /// Creates route handler with `PATCH` method guard. @@ -178,9 +172,8 @@ pub fn trace(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn patch(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as syn::AttributeArgs); - let gen = match route::Route::new(args, input, route::GuardType::Patch) { - Ok(gen) => gen, - Err(err) => return err.to_compile_error().into(), - }; - gen.generate() + match Route::new(args, input, route::GuardType::Patch) { + Ok(route) => route.into_token_stream().into(), + Err(err) => err.to_compile_error().into(), + } } diff --git a/actix-web-codegen/src/route.rs b/actix-web-codegen/src/route.rs index 60b829595..341329ea2 100644 --- a/actix-web-codegen/src/route.rs +++ b/actix-web-codegen/src/route.rs @@ -2,7 +2,7 @@ extern crate proc_macro; use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; -use quote::{quote, ToTokens, TokenStreamExt}; +use quote::{format_ident, quote, ToTokens, TokenStreamExt}; use syn::{AttributeArgs, Ident, NestedMeta}; enum ResourceType { @@ -12,11 +12,7 @@ enum ResourceType { impl ToTokens for ResourceType { fn to_tokens(&self, stream: &mut TokenStream2) { - let ident = match self { - ResourceType::Async => "to", - ResourceType::Sync => "to", - }; - let ident = Ident::new(ident, Span::call_site()); + let ident = format_ident!("to"); stream.append(ident); } } @@ -52,8 +48,7 @@ impl GuardType { impl ToTokens for GuardType { fn to_tokens(&self, stream: &mut TokenStream2) { - let ident = self.as_str(); - let ident = Ident::new(ident, Span::call_site()); + let ident = Ident::new(self.as_str(), Span::call_site()); stream.append(ident); } } @@ -93,12 +88,12 @@ impl Args { } else { return Err(syn::Error::new_spanned( nv.path, - "Unknown attribute key is specified. Allowed: guard", + "Unknown attribute key is specified. Allowed: guard.", )); } } arg => { - return Err(syn::Error::new_spanned(arg, "Unknown attribute")); + return Err(syn::Error::new_spanned(arg, "Unknown attribute.")); } } } @@ -181,15 +176,18 @@ impl Route { guard, }) } +} - pub fn generate(&self) -> TokenStream { - let name = &self.name; +impl ToTokens for Route { + fn to_tokens(&self, output: &mut TokenStream2) { + let Self { + name, + guard, + ast, + args: Args { path, guards }, + resource_type, + } = self; let resource_name = name.to_string(); - let guard = &self.guard; - let ast = &self.ast; - let path = &self.args.path; - let extra_guards = &self.args.guards; - let resource_type = &self.resource_type; let stream = quote! { #[allow(non_camel_case_types, missing_docs)] pub struct #name; @@ -200,13 +198,14 @@ impl Route { let __resource = actix_web::Resource::new(#path) .name(#resource_name) .guard(actix_web::guard::#guard()) - #(.guard(actix_web::guard::fn_guard(#extra_guards)))* + #(.guard(actix_web::guard::fn_guard(#guards)))* .#resource_type(#name); actix_web::dev::HttpServiceFactory::register(__resource, __config) } } }; - stream.into() + + output.extend(stream); } } From 0d958fabd7cb8051d59fdb1c3b5b781476000fa6 Mon Sep 17 00:00:00 2001 From: Stig Johan Berggren Date: Tue, 17 Mar 2020 00:23:54 +0100 Subject: [PATCH 086/157] =?UTF-8?q?=F0=9F=93=9D=20Improve=20the=20code=20e?= =?UTF-8?q?xample=20for=20JsonConfig=20(#1418)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ๐Ÿ“ Improve the code example for JsonConfig * Remove a redundant comment --- src/types/json.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/types/json.rs b/src/types/json.rs index ead2d0c9a..df8aa4fc6 100644 --- a/src/types/json.rs +++ b/src/types/json.rs @@ -207,8 +207,10 @@ where /// Json extractor configuration /// +/// # Examples +/// /// ```rust -/// use actix_web::{error, web, App, FromRequest, HttpResponse}; +/// use actix_web::{error, web, App, FromRequest, HttpRequest, HttpResponse}; /// use serde_derive::Deserialize; /// /// #[derive(Deserialize)] @@ -221,6 +223,19 @@ where /// format!("Welcome {}!", info.username) /// } /// +/// /// Return either a 400 or 415, and include the error message from serde +/// /// in the response body +/// fn json_error_handler(err: error::JsonPayloadError, _req: &HttpRequest) -> error::Error { +/// let detail = err.to_string(); +/// let response = match &err { +/// error::JsonPayloadError::ContentType => { +/// HttpResponse::UnsupportedMediaType().content_type("text/plain").body(detail) +/// } +/// _ => HttpResponse::BadRequest().content_type("text/plain").body(detail), +/// }; +/// error::InternalError::from_response(err, response).into() +/// } +/// /// fn main() { /// let app = App::new().service( /// web::resource("/index.html") @@ -231,10 +246,7 @@ where /// .content_type(|mime| { // <- accept text/plain content type /// mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN /// }) -/// .error_handler(|err, req| { // <- create custom error response -/// error::InternalError::from_response( -/// err, HttpResponse::Conflict().finish()).into() -/// }) +/// .error_handler(json_error_handler) // Use our custom error response /// })) /// .route(web::post().to(index)) /// ); From 5548c57a098a5f68ee27ecdda1de51a2d0b54f6e Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 18 Mar 2020 05:04:30 +0900 Subject: [PATCH 087/157] Upload coverage on PRs --- .github/workflows/linux.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 7cabb8020..ec5423048 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -73,15 +73,14 @@ jobs: args: --package=awc --no-default-features --features=rustls -- --nocapture - name: Generate coverage file - if: matrix.version == 'stable' && github.ref == 'refs/heads/master' + if: matrix.version == 'stable' && (github.ref == 'refs/heads/master' || github.event_name == 'pull_request') run: | cargo install cargo-tarpaulin cargo tarpaulin --out Xml - name: Upload to Codecov - if: matrix.version == 'stable' && github.ref == 'refs/heads/master' + if: matrix.version == 'stable' && (github.ref == 'refs/heads/master' || github.event_name == 'pull_request') uses: codecov/codecov-action@v1 with: - token: ${{ secrets.CODECOV_TOKEN }} file: cobertura.xml - name: Clear the cargo caches From 146ae4da18038a5dfcc1a6d09aac356baaf0f29f Mon Sep 17 00:00:00 2001 From: Otavio Salvador Date: Tue, 17 Mar 2020 22:51:06 -0300 Subject: [PATCH 088/157] Implement `std::error::Error` for our custom errors For allowing a more ergonomic use and better integration on the ecosystem, this adds the `std::error::Error` `impl` for our custom errors. We intent to drop this hand made code once `derive_more` finishes the addition of the Error derive support[1]. Until that is available, we need to live with that. 1. https://github.com/JelteF/derive_more/issues/92 Signed-off-by: Otavio Salvador --- CHANGES.md | 8 ++++++++ actix-http/CHANGES.md | 8 ++++++++ actix-http/src/client/error.rs | 8 ++++++++ actix-http/src/error.rs | 4 ++++ actix-http/src/ws/mod.rs | 2 ++ awc/CHANGES.md | 8 ++++++++ awc/src/error.rs | 4 ++++ src/error.rs | 12 ++++++++++++ 8 files changed, 54 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 460b24f57..34908d96d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,13 @@ # Changes +## [Unreleased] + +### Changed + +* Implement `std::error::Error` for our custom errors [#1422] + +[#1422]: https://github.com/actix/actix-web/pull/1422 + ## [3.0.0-alpha.1] - 2020-03-11 ### Added diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index fb1c3a329..9d75e1a05 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,5 +1,13 @@ # Changes +## [Unreleased] + +### Changed + +* Implement `std::error::Error` for our custom errors [#1422] + +[#1422]: https://github.com/actix/actix-web/pull/1422 + ## [2.0.0-alpha.2] - 2020-03-07 ### Changed diff --git a/actix-http/src/client/error.rs b/actix-http/src/client/error.rs index 42ea47ee8..0f0a86cd1 100644 --- a/actix-http/src/client/error.rs +++ b/actix-http/src/client/error.rs @@ -55,6 +55,8 @@ pub enum ConnectError { Io(io::Error), } +impl std::error::Error for ConnectError {} + impl From for ConnectError { fn from(err: actix_connect::ConnectError) -> ConnectError { match err { @@ -86,6 +88,8 @@ pub enum InvalidUrl { HttpError(http::Error), } +impl std::error::Error for InvalidUrl {} + /// A set of errors that can occur during request sending and response reading #[derive(Debug, Display, From)] pub enum SendRequestError { @@ -115,6 +119,8 @@ pub enum SendRequestError { Body(Error), } +impl std::error::Error for SendRequestError {} + /// Convert `SendRequestError` to a server `Response` impl ResponseError for SendRequestError { fn status_code(&self) -> StatusCode { @@ -139,6 +145,8 @@ pub enum FreezeRequestError { Http(HttpError), } +impl std::error::Error for FreezeRequestError {} + impl From for SendRequestError { fn from(e: FreezeRequestError) -> Self { match e { diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 0850e18ff..7ecdc6394 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -333,6 +333,8 @@ pub enum PayloadError { Io(io::Error), } +impl std::error::Error for PayloadError {} + impl From for PayloadError { fn from(err: h2::Error) -> Self { PayloadError::Http2Payload(err) @@ -441,6 +443,8 @@ pub enum ContentTypeError { UnknownEncoding, } +impl std::error::Error for ContentTypeError {} + /// Return `BadRequest` for `ContentTypeError` impl ResponseError for ContentTypeError { fn status_code(&self) -> StatusCode { diff --git a/actix-http/src/ws/mod.rs b/actix-http/src/ws/mod.rs index ffa397979..3d83943c7 100644 --- a/actix-http/src/ws/mod.rs +++ b/actix-http/src/ws/mod.rs @@ -58,6 +58,8 @@ pub enum ProtocolError { Io(io::Error), } +impl std::error::Error for ProtocolError {} + impl ResponseError for ProtocolError {} /// Websocket handshake errors diff --git a/awc/CHANGES.md b/awc/CHANGES.md index e6f731540..d127700ac 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,5 +1,13 @@ # Changes +## [Unreleased] + +### Changed + +* Implement `std::error::Error` for our custom errors [#1422] + +[#1422]: https://github.com/actix/actix-web/pull/1422 + ## [2.0.0-alpha.1] - 2020-03-11 * Update `actix-http` dependency to 2.0.0-alpha.2 diff --git a/awc/src/error.rs b/awc/src/error.rs index 7fece74ee..d008166d9 100644 --- a/awc/src/error.rs +++ b/awc/src/error.rs @@ -42,6 +42,8 @@ pub enum WsClientError { SendRequest(SendRequestError), } +impl std::error::Error for WsClientError {} + impl From for WsClientError { fn from(err: InvalidUrl) -> Self { WsClientError::SendRequest(err.into()) @@ -68,5 +70,7 @@ pub enum JsonPayloadError { Payload(PayloadError), } +impl std::error::Error for JsonPayloadError {} + /// Return `InternalServerError` for `JsonPayloadError` impl ResponseError for JsonPayloadError {} diff --git a/src/error.rs b/src/error.rs index 31f6b9c5b..659ba05fd 100644 --- a/src/error.rs +++ b/src/error.rs @@ -21,6 +21,8 @@ pub enum UrlGenerationError { ParseError(UrlParseError), } +impl std::error::Error for UrlGenerationError {} + /// `InternalServerError` for `UrlGeneratorError` impl ResponseError for UrlGenerationError {} @@ -51,6 +53,8 @@ pub enum UrlencodedError { Payload(PayloadError), } +impl std::error::Error for UrlencodedError {} + /// Return `BadRequest` for `UrlencodedError` impl ResponseError for UrlencodedError { fn status_code(&self) -> StatusCode { @@ -79,6 +83,8 @@ pub enum JsonPayloadError { Payload(PayloadError), } +impl std::error::Error for JsonPayloadError {} + /// Return `BadRequest` for `JsonPayloadError` impl ResponseError for JsonPayloadError { fn error_response(&self) -> HttpResponse { @@ -99,6 +105,8 @@ pub enum PathError { Deserialize(serde::de::value::Error), } +impl std::error::Error for PathError {} + /// Return `BadRequest` for `PathError` impl ResponseError for PathError { fn status_code(&self) -> StatusCode { @@ -114,6 +122,8 @@ pub enum QueryPayloadError { Deserialize(serde::de::value::Error), } +impl std::error::Error for QueryPayloadError {} + /// Return `BadRequest` for `QueryPayloadError` impl ResponseError for QueryPayloadError { fn status_code(&self) -> StatusCode { @@ -139,6 +149,8 @@ pub enum ReadlinesError { ContentTypeError(ContentTypeError), } +impl std::error::Error for ReadlinesError {} + /// Return `BadRequest` for `ReadlinesError` impl ResponseError for ReadlinesError { fn status_code(&self) -> StatusCode { From 206733188478f4d18df376a72fac3a4938b99bd6 Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Fri, 20 Mar 2020 03:40:42 +0800 Subject: [PATCH 089/157] Refactor actix-codegen duplicate code (#1423) Co-authored-by: Yuki Okushi --- actix-web-codegen/src/lib.rs | 57 ++++++---------------------------- actix-web-codegen/src/route.rs | 14 ++++++++- 2 files changed, 22 insertions(+), 49 deletions(-) diff --git a/actix-web-codegen/src/lib.rs b/actix-web-codegen/src/lib.rs index affb4dfec..b724eb797 100644 --- a/actix-web-codegen/src/lib.rs +++ b/actix-web-codegen/src/lib.rs @@ -45,9 +45,6 @@ extern crate proc_macro; mod route; use proc_macro::TokenStream; -use quote::ToTokens; -use route::Route; -use syn::parse_macro_input; /// Creates route handler with `GET` method guard. /// @@ -59,11 +56,7 @@ use syn::parse_macro_input; /// - `guard="function_name"` - Registers function as guard using `actix_web::guard::fn_guard` #[proc_macro_attribute] pub fn get(args: TokenStream, input: TokenStream) -> TokenStream { - let args = parse_macro_input!(args as syn::AttributeArgs); - match Route::new(args, input, route::GuardType::Get) { - Ok(route) => route.into_token_stream().into(), - Err(err) => err.to_compile_error().into(), - } + route::generate(args, input, route::GuardType::Get) } /// Creates route handler with `POST` method guard. @@ -73,11 +66,7 @@ pub fn get(args: TokenStream, input: TokenStream) -> TokenStream { /// Attributes are the same as in [get](attr.get.html) #[proc_macro_attribute] pub fn post(args: TokenStream, input: TokenStream) -> TokenStream { - let args = parse_macro_input!(args as syn::AttributeArgs); - match Route::new(args, input, route::GuardType::Post) { - Ok(route) => route.into_token_stream().into(), - Err(err) => err.to_compile_error().into(), - } + route::generate(args, input, route::GuardType::Post) } /// Creates route handler with `PUT` method guard. @@ -87,11 +76,7 @@ pub fn post(args: TokenStream, input: TokenStream) -> TokenStream { /// Attributes are the same as in [get](attr.get.html) #[proc_macro_attribute] pub fn put(args: TokenStream, input: TokenStream) -> TokenStream { - let args = parse_macro_input!(args as syn::AttributeArgs); - match Route::new(args, input, route::GuardType::Put) { - Ok(route) => route.into_token_stream().into(), - Err(err) => err.to_compile_error().into(), - } + route::generate(args, input, route::GuardType::Put) } /// Creates route handler with `DELETE` method guard. @@ -101,11 +86,7 @@ pub fn put(args: TokenStream, input: TokenStream) -> TokenStream { /// Attributes are the same as in [get](attr.get.html) #[proc_macro_attribute] pub fn delete(args: TokenStream, input: TokenStream) -> TokenStream { - let args = parse_macro_input!(args as syn::AttributeArgs); - match Route::new(args, input, route::GuardType::Delete) { - Ok(route) => route.into_token_stream().into(), - Err(err) => err.to_compile_error().into(), - } + route::generate(args, input, route::GuardType::Delete) } /// Creates route handler with `HEAD` method guard. @@ -115,11 +96,7 @@ pub fn delete(args: TokenStream, input: TokenStream) -> TokenStream { /// Attributes are the same as in [head](attr.head.html) #[proc_macro_attribute] pub fn head(args: TokenStream, input: TokenStream) -> TokenStream { - let args = parse_macro_input!(args as syn::AttributeArgs); - match Route::new(args, input, route::GuardType::Head) { - Ok(route) => route.into_token_stream().into(), - Err(err) => err.to_compile_error().into(), - } + route::generate(args, input, route::GuardType::Head) } /// Creates route handler with `CONNECT` method guard. @@ -129,11 +106,7 @@ pub fn head(args: TokenStream, input: TokenStream) -> TokenStream { /// Attributes are the same as in [connect](attr.connect.html) #[proc_macro_attribute] pub fn connect(args: TokenStream, input: TokenStream) -> TokenStream { - let args = parse_macro_input!(args as syn::AttributeArgs); - match Route::new(args, input, route::GuardType::Connect) { - Ok(route) => route.into_token_stream().into(), - Err(err) => err.to_compile_error().into(), - } + route::generate(args, input, route::GuardType::Connect) } /// Creates route handler with `OPTIONS` method guard. @@ -143,11 +116,7 @@ pub fn connect(args: TokenStream, input: TokenStream) -> TokenStream { /// Attributes are the same as in [options](attr.options.html) #[proc_macro_attribute] pub fn options(args: TokenStream, input: TokenStream) -> TokenStream { - let args = parse_macro_input!(args as syn::AttributeArgs); - match Route::new(args, input, route::GuardType::Options) { - Ok(route) => route.into_token_stream().into(), - Err(err) => err.to_compile_error().into(), - } + route::generate(args, input, route::GuardType::Options) } /// Creates route handler with `TRACE` method guard. @@ -157,11 +126,7 @@ pub fn options(args: TokenStream, input: TokenStream) -> TokenStream { /// Attributes are the same as in [trace](attr.trace.html) #[proc_macro_attribute] pub fn trace(args: TokenStream, input: TokenStream) -> TokenStream { - let args = parse_macro_input!(args as syn::AttributeArgs); - match Route::new(args, input, route::GuardType::Trace) { - Ok(route) => route.into_token_stream().into(), - Err(err) => err.to_compile_error().into(), - } + route::generate(args, input, route::GuardType::Trace) } /// Creates route handler with `PATCH` method guard. @@ -171,9 +136,5 @@ pub fn trace(args: TokenStream, input: TokenStream) -> TokenStream { /// Attributes are the same as in [patch](attr.patch.html) #[proc_macro_attribute] pub fn patch(args: TokenStream, input: TokenStream) -> TokenStream { - let args = parse_macro_input!(args as syn::AttributeArgs); - match Route::new(args, input, route::GuardType::Patch) { - Ok(route) => route.into_token_stream().into(), - Err(err) => err.to_compile_error().into(), - } + route::generate(args, input, route::GuardType::Patch) } diff --git a/actix-web-codegen/src/route.rs b/actix-web-codegen/src/route.rs index 341329ea2..3e6f9c979 100644 --- a/actix-web-codegen/src/route.rs +++ b/actix-web-codegen/src/route.rs @@ -3,7 +3,7 @@ extern crate proc_macro; use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{format_ident, quote, ToTokens, TokenStreamExt}; -use syn::{AttributeArgs, Ident, NestedMeta}; +use syn::{AttributeArgs, Ident, NestedMeta, parse_macro_input}; enum ResourceType { Async, @@ -209,3 +209,15 @@ impl ToTokens for Route { output.extend(stream); } } + +pub(crate) fn generate( + args: TokenStream, + input: TokenStream, + guard: GuardType, +) -> TokenStream { + let args = parse_macro_input!(args as syn::AttributeArgs); + match Route::new(args, input, guard) { + Ok(route) => route.into_token_stream().into(), + Err(err) => err.to_compile_error().into(), + } +} From 7b7daa75a47b78847871d04a931dc31d16bebb2b Mon Sep 17 00:00:00 2001 From: Otavio Salvador Date: Wed, 25 Mar 2020 23:49:24 -0300 Subject: [PATCH 090/157] Add explicit feature requirements for examples and tests This allow us to build 'actix-web' without default features and run all tests. Signed-off-by: Otavio Salvador --- Cargo.toml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 6bd36279a..84f876579 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,18 @@ openssl = ["actix-tls/openssl", "awc/openssl", "open-ssl"] # rustls rustls = ["actix-tls/rustls", "awc/rustls", "rust-tls"] +[[example]] +name = "basic" +required-features = ["compress"] + +[[example]] +name = "uds" +required-features = ["compress"] + +[[test]] +name = "test_server" +required-features = ["compress"] + [dependencies] actix-codec = "0.2.0" actix-service = "1.0.2" From aaff68bf05fd69bf0ea652dc5507c31729e49050 Mon Sep 17 00:00:00 2001 From: Stephen Eckels Date: Sat, 4 Apr 2020 14:26:40 -0400 Subject: [PATCH 091/157] Change NormalizePath to append trailing slash (#1433) * Change NormalizePath to append trailing slash * add tests * Update CHANGES.md Co-authored-by: Yuki Okushi --- CHANGES.md | 1 + src/middleware/normalize.rs | 43 +++++++++++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 34908d96d..0739f280c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ ### Changed * Implement `std::error::Error` for our custom errors [#1422] +* NormalizePath middleware now appends trailing / so that routes of form /example/ respond to /example requests. [#1422]: https://github.com/actix/actix-web/pull/1422 diff --git a/src/middleware/normalize.rs b/src/middleware/normalize.rs index f6b834bfe..38b5310f7 100644 --- a/src/middleware/normalize.rs +++ b/src/middleware/normalize.rs @@ -74,9 +74,13 @@ where fn call(&mut self, mut req: ServiceRequest) -> Self::Future { let head = req.head_mut(); - let path = head.uri.path(); + + // always add trailing slash, might be an extra one + let path = head.uri.path().to_string() + "/"; let original_len = path.len(); - let path = self.merge_slash.replace_all(path, "/"); + + // normalize multiple /'s to one / + let path = self.merge_slash.replace_all(&path, "/"); if original_len != path.len() { let mut parts = head.uri.clone().into_parts(); @@ -119,6 +123,14 @@ mod tests { let req = TestRequest::with_uri("/v1//something////").to_request(); let res = call_service(&mut app, req).await; assert!(res.status().is_success()); + + let req2 = TestRequest::with_uri("//v1/something").to_request(); + let res2 = call_service(&mut app, req2).await; + assert!(res2.status().is_success()); + + let req3 = TestRequest::with_uri("//v1//////something").to_request(); + let res3 = call_service(&mut app, req3).await; + assert!(res3.status().is_success()); } #[actix_rt::test] @@ -136,6 +148,14 @@ mod tests { let req = TestRequest::with_uri("/v1//something////").to_srv_request(); let res = normalize.call(req).await.unwrap(); assert!(res.status().is_success()); + + let req2 = TestRequest::with_uri("///v1/something").to_srv_request(); + let res2 = normalize.call(req2).await.unwrap(); + assert!(res2.status().is_success()); + + let req3 = TestRequest::with_uri("//v1///something").to_srv_request(); + let res3 = normalize.call(req3).await.unwrap(); + assert!(res3.status().is_success()); } #[actix_rt::test] @@ -147,6 +167,25 @@ mod tests { ok(req.into_response(HttpResponse::Ok().finish())) }; + let mut normalize = NormalizePath + .new_transform(srv.into_service()) + .await + .unwrap(); + + let req = TestRequest::with_uri(URI).to_srv_request(); + let res = normalize.call(req).await.unwrap(); + assert!(res.status().is_success()); + } + + #[actix_rt::test] + async fn should_normalize_nothing_notrail() { + const URI: &str = "/v1/something"; + + let srv = |req: ServiceRequest| { + assert_eq!(URI, req.path()); + ok(req.into_response(HttpResponse::Ok().finish())) + }; + let mut normalize = NormalizePath .new_transform(srv.into_service()) .await From 0ad02ee0e02c235b49a181bd164cc945fde18b18 Mon Sep 17 00:00:00 2001 From: Tore Pettersen Date: Sun, 5 Apr 2020 21:12:44 +0200 Subject: [PATCH 092/157] Add convenience functions for testing (#1401) * Add convenience functions for testing * Fix remarks from PR and add tests * Add unpin to read_json_body * Update changelog --- CHANGES.md | 1 + src/test.rs | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 0739f280c..4a7862543 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,6 +14,7 @@ ### Added * Add helper function for creating routes with `TRACE` method guard `web::trace()` +* Add convenience functions `test::read_body_json()` and `test::TestRequest::send_request()` for testing. ### Changed diff --git a/src/test.rs b/src/test.rs index 15e66a230..19ea8bbef 100644 --- a/src/test.rs +++ b/src/test.rs @@ -203,6 +203,54 @@ where bytes.freeze() } +/// Helper function that returns a deserialized response body of a ServiceResponse. +/// +/// ```rust +/// use actix_web::{App, test, web, HttpResponse, http::header}; +/// use serde::{Serialize, Deserialize}; +/// +/// #[derive(Serialize, Deserialize)] +/// pub struct Person { +/// id: String, +/// name: String, +/// } +/// +/// #[actix_rt::test] +/// async fn test_post_person() { +/// let mut app = test::init_service( +/// App::new().service( +/// web::resource("/people") +/// .route(web::post().to(|person: web::Json| async { +/// HttpResponse::Ok() +/// .json(person.into_inner())}) +/// )) +/// ).await; +/// +/// let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes(); +/// +/// let resp = test::TestRequest::post() +/// .uri("/people") +/// .header(header::CONTENT_TYPE, "application/json") +/// .set_payload(payload) +/// .send_request(&mut app) +/// .await; +/// +/// assert!(resp.status().is_success()); +/// +/// let result: Person = test::read_body_json(resp).await; +/// } +/// ``` +pub async fn read_body_json(res: ServiceResponse) -> T +where + B: MessageBody + Unpin, + T: DeserializeOwned, +{ + let body = read_body(res).await; + + serde_json::from_slice(&body) + .unwrap_or_else(|_| panic!("read_response_json failed during deserialization")) +} + pub async fn load_stream(mut stream: S) -> Result where S: Stream> + Unpin, @@ -527,6 +575,16 @@ impl TestRequest { (req, payload) } + + /// Complete request creation, calls service and waits for response future completion. + pub async fn send_request(self, app: &mut S) -> S::Response + where + S: Service, Error = E>, + E: std::fmt::Debug + { + let req = self.to_request(); + call_service(app, req).await + } } /// Start test server with default configuration @@ -1041,6 +1099,23 @@ mod tests { assert_eq!(result, Bytes::from_static(b"welcome!")); } + #[actix_rt::test] + async fn test_send_request() { + let mut app = + init_service(App::new().service(web::resource("/index.html").route( + web::get().to(|| async { HttpResponse::Ok().body("welcome!") }), + ))) + .await; + + let resp = TestRequest::get() + .uri("/index.html") + .send_request(&mut app) + .await; + + let result = read_body(resp).await; + assert_eq!(result, Bytes::from_static(b"welcome!")); + } + #[derive(Serialize, Deserialize)] pub struct Person { id: String, @@ -1068,6 +1143,28 @@ mod tests { assert_eq!(&result.id, "12345"); } + #[actix_rt::test] + async fn test_body_json() { + let mut app = init_service(App::new().service(web::resource("/people").route( + web::post().to(|person: web::Json| { + async { HttpResponse::Ok().json(person.into_inner()) } + }), + ))) + .await; + + let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes(); + + let resp = TestRequest::post() + .uri("/people") + .header(header::CONTENT_TYPE, "application/json") + .set_payload(payload) + .send_request(&mut app) + .await; + + let result: Person = read_body_json(resp).await; + assert_eq!(&result.name, "User name"); + } + #[actix_rt::test] async fn test_request_response_form() { let mut app = init_service(App::new().service(web::resource("/people").route( From dfaa330a94bdd3b09f381f5509f16902ec3beffb Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 8 Apr 2020 04:42:38 +0900 Subject: [PATCH 093/157] Deploy all the workspace crates' docs --- .github/workflows/upload-doc.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/upload-doc.yml b/.github/workflows/upload-doc.yml index 388ae3704..75d534b28 100644 --- a/.github/workflows/upload-doc.yml +++ b/.github/workflows/upload-doc.yml @@ -24,7 +24,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: doc - args: --no-deps --all-features + args: --no-deps --workspace --all-features - name: Tweak HTML run: echo "" > target/doc/index.html @@ -32,4 +32,4 @@ jobs: - name: Upload documentation run: | git clone https://github.com/davisp/ghp-import.git - ./ghp-import/ghp_import.py -n -p -f -m "Documentation upload" -r https://${{ secrets.GITHUB_TOKEN }}@github.com/"${{ github.repository }}.git" target/doc \ No newline at end of file + ./ghp-import/ghp_import.py -n -p -f -m "Documentation upload" -r https://${{ secrets.GITHUB_TOKEN }}@github.com/"${{ github.repository }}.git" target/doc From 74ddc852c8494eb7c7c3e948fcf46ab4e40188e9 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 8 Apr 2020 04:48:01 +0900 Subject: [PATCH 094/157] Tweak README --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b55c5bae..5dc8d376f 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,12 @@ Actix web is a simple, pragmatic and extremely fast web framework for Rust. * Includes an asynchronous [HTTP client](https://actix.rs/actix-web/actix_web/client/index.html) * Supports [Actix actor framework](https://github.com/actix/actix) +## Docs + +* [API documentation (master)](https://actix.rs/actix-web/actix_web) +* [API documentation (docs.rs)](https://docs.rs/actix-web) +* [User guide](https://actix.rs) + ## Example Dependencies: @@ -74,7 +80,7 @@ async fn main() -> std::io::Result<()> { * [Stateful](https://github.com/actix/examples/tree/master/state/) * [Multipart streams](https://github.com/actix/examples/tree/master/multipart/) * [Simple websocket](https://github.com/actix/examples/tree/master/websocket/) -* [Tera](https://github.com/actix/examples/tree/master/template_tera/) / +* [Tera](https://github.com/actix/examples/tree/master/template_tera/) * [Askama](https://github.com/actix/examples/tree/master/template_askama/) templates * [Diesel integration](https://github.com/actix/examples/tree/master/diesel/) * [r2d2](https://github.com/actix/examples/tree/master/r2d2/) From e7ba871bbf52411c5948c272ad8c5c2ac7246720 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 13 Apr 2020 03:42:44 +0900 Subject: [PATCH 095/157] Remove cache config from GHA workflows --- .github/workflows/bench.yml | 25 ------------------------- .github/workflows/linux.yml | 25 ------------------------- .github/workflows/macos.yml | 25 ------------------------- 3 files changed, 75 deletions(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 08bb81d48..04fe83f66 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -16,32 +16,7 @@ jobs: profile: minimal override: true - - name: Generate Cargo.lock - uses: actions-rs/cargo@v1 - with: - command: generate-lockfile - - name: Cache cargo registry - uses: actions/cache@v1 - with: - path: ~/.cargo/registry - key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-registry-trimmed-${{ hashFiles('**/Cargo.lock') }} - - name: Cache cargo index - uses: actions/cache@v1 - with: - path: ~/.cargo/git - key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }} - - name: Cache cargo build - uses: actions/cache@v1 - with: - path: target - key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-build-trimmed-${{ hashFiles('**/Cargo.lock') }} - - name: Check benchmark uses: actions-rs/cargo@v1 with: command: bench - - - name: Clear the cargo caches - run: | - cargo install cargo-cache --no-default-features --features ci-autoclean - cargo-cache diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index ec5423048..f94d5f811 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -25,26 +25,6 @@ jobs: profile: minimal override: true - - name: Generate Cargo.lock - uses: actions-rs/cargo@v1 - with: - command: generate-lockfile - - name: Cache cargo registry - uses: actions/cache@v1 - with: - path: ~/.cargo/registry - key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }} - - name: Cache cargo index - uses: actions/cache@v1 - with: - path: ~/.cargo/git - key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }} - - name: Cache cargo build - uses: actions/cache@v1 - with: - path: target - key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-build-trimmed-${{ hashFiles('**/Cargo.lock') }} - - name: check build uses: actions-rs/cargo@v1 with: @@ -82,8 +62,3 @@ jobs: uses: codecov/codecov-action@v1 with: file: cobertura.xml - - - name: Clear the cargo caches - run: | - cargo install cargo-cache --no-default-features --features ci-autoclean - cargo-cache diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 397236a29..6c360bacc 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -24,26 +24,6 @@ jobs: profile: minimal override: true - - name: Generate Cargo.lock - uses: actions-rs/cargo@v1 - with: - command: generate-lockfile - - name: Cache cargo registry - uses: actions/cache@v1 - with: - path: ~/.cargo/registry - key: ${{ matrix.version }}-x86_64-apple-darwin-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }} - - name: Cache cargo index - uses: actions/cache@v1 - with: - path: ~/.cargo/git - key: ${{ matrix.version }}-x86_64-apple-darwin-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }} - - name: Cache cargo build - uses: actions/cache@v1 - with: - path: target - key: ${{ matrix.version }}-x86_64-apple-darwin-cargo-build-trimmed-${{ hashFiles('**/Cargo.lock') }} - - name: check build uses: actions-rs/cargo@v1 with: @@ -57,8 +37,3 @@ jobs: args: --all --all-features --no-fail-fast -- --nocapture --skip=test_h2_content_length --skip=test_reading_deflate_encoding_large_random_rustls - - - name: Clear the cargo caches - run: | - cargo install cargo-cache --no-default-features --features ci-autoclean - cargo-cache From 45e2e401401fb62d69fa90424fda45962b1d4e5d Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 14 Apr 2020 02:33:19 +0100 Subject: [PATCH 096/157] set data container on default service calls closes #1450 --- CHANGES.md | 2 ++ src/resource.rs | 30 ++++++++++++++++---- src/scope.rs | 74 +++++++++++++++++++++++++++---------------------- 3 files changed, 68 insertions(+), 38 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4a7862543..db2c8e8f5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,10 +4,12 @@ ### Changed +* `{Resource,Scope}::default_service(f)` handlers now support app data extraction. [#1452] * Implement `std::error::Error` for our custom errors [#1422] * NormalizePath middleware now appends trailing / so that routes of form /example/ respond to /example requests. [#1422]: https://github.com/actix/actix-web/pull/1422 +[#1452]: https://github.com/actix/actix-web/pull/1452 ## [3.0.0-alpha.1] - 2020-03-11 diff --git a/src/resource.rs b/src/resource.rs index d03024a07..a4ab19052 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -542,6 +542,9 @@ impl Service for ResourceService { } } if let Some(ref mut default) = self.default { + if let Some(ref data) = self.data { + req.set_data_container(data.clone()); + } Either::Right(default.call(req)) } else { let req = req.into_parts().0; @@ -649,11 +652,9 @@ mod tests { #[actix_rt::test] async fn test_to() { let mut srv = - init_service(App::new().service(web::resource("/test").to(|| { - async { - delay_for(Duration::from_millis(100)).await; - Ok::<_, Error>(HttpResponse::Ok()) - } + init_service(App::new().service(web::resource("/test").to(|| async { + delay_for(Duration::from_millis(100)).await; + Ok::<_, Error>(HttpResponse::Ok()) }))) .await; let req = TestRequest::with_uri("/test").to_request(); @@ -793,4 +794,23 @@ mod tests { let resp = call_service(&mut srv, req).await; assert_eq!(resp.status(), StatusCode::OK); } + + #[actix_rt::test] + async fn test_data_default_service() { + let mut srv = init_service( + App::new().data(1usize).service( + web::resource("/test") + .data(10usize) + .default_service(web::to(|data: web::Data| { + assert_eq!(**data, 10); + HttpResponse::Ok() + })), + ), + ) + .await; + + let req = TestRequest::get().uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + } } diff --git a/src/scope.rs b/src/scope.rs index 18e775e61..7ead71787 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -54,7 +54,7 @@ type BoxedResponse = LocalBoxFuture<'static, Result>; /// ``` /// /// In the above example three routes get registered: -/// * /{project_id}/path1 - reponds to all http method +/// * /{project_id}/path1 - responds to all http method /// * /{project_id}/path2 - `GET` requests /// * /{project_id}/path3 - `HEAD` requests /// @@ -626,6 +626,9 @@ impl Service for ScopeService { } Either::Left(srv.call(req)) } else if let Some(ref mut default) = self.default { + if let Some(ref data) = self.data { + req.set_data_container(data.clone()); + } Either::Left(default.call(req)) } else { let req = req.into_parts().0; @@ -825,11 +828,9 @@ mod tests { async fn test_scope_variable_segment() { let mut srv = init_service(App::new().service(web::scope("/ab-{project}").service( - web::resource("/path1").to(|r: HttpRequest| { - async move { - HttpResponse::Ok() - .body(format!("project: {}", &r.match_info()["project"])) - } + web::resource("/path1").to(|r: HttpRequest| async move { + HttpResponse::Ok() + .body(format!("project: {}", &r.match_info()["project"])) }), ))) .await; @@ -937,11 +938,9 @@ mod tests { async fn test_nested_scope_with_variable_segment() { let mut srv = init_service(App::new().service(web::scope("/app").service( web::scope("/{project_id}").service(web::resource("/path1").to( - |r: HttpRequest| { - async move { - HttpResponse::Created() - .body(format!("project: {}", &r.match_info()["project_id"])) - } + |r: HttpRequest| async move { + HttpResponse::Created() + .body(format!("project: {}", &r.match_info()["project_id"])) }, )), ))) @@ -964,14 +963,12 @@ mod tests { async fn test_nested2_scope_with_variable_segment() { let mut srv = init_service(App::new().service(web::scope("/app").service( web::scope("/{project}").service(web::scope("/{id}").service( - web::resource("/path1").to(|r: HttpRequest| { - async move { - HttpResponse::Created().body(format!( - "project: {} - {}", - &r.match_info()["project"], - &r.match_info()["id"], - )) - } + web::resource("/path1").to(|r: HttpRequest| async move { + HttpResponse::Created().body(format!( + "project: {} - {}", + &r.match_info()["project"], + &r.match_info()["id"], + )) }), )), ))) @@ -1119,6 +1116,23 @@ mod tests { assert_eq!(resp.status(), StatusCode::OK); } + #[actix_rt::test] + async fn test_override_data_default_service() { + let mut srv = init_service(App::new().data(1usize).service( + web::scope("app").data(10usize).default_service(web::to( + |data: web::Data| { + assert_eq!(**data, 10); + HttpResponse::Ok() + }, + )), + )) + .await; + + let req = TestRequest::with_uri("/app/t").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + } + #[actix_rt::test] async fn test_override_app_data() { let mut srv = init_service(App::new().app_data(web::Data::new(1usize)).service( @@ -1177,15 +1191,11 @@ mod tests { ); s.route( "/", - web::get().to(|req: HttpRequest| { - async move { - HttpResponse::Ok().body(format!( - "{}", - req.url_for("youtube", &["xxxxxx"]) - .unwrap() - .as_str() - )) - } + web::get().to(|req: HttpRequest| async move { + HttpResponse::Ok().body(format!( + "{}", + req.url_for("youtube", &["xxxxxx"]).unwrap().as_str() + )) }), ); })); @@ -1203,11 +1213,9 @@ mod tests { async fn test_url_for_nested() { let mut srv = init_service(App::new().service(web::scope("/a").service( web::scope("/b").service(web::resource("/c/{stuff}").name("c").route( - web::get().to(|req: HttpRequest| { - async move { - HttpResponse::Ok() - .body(format!("{}", req.url_for("c", &["12345"]).unwrap())) - } + web::get().to(|req: HttpRequest| async move { + HttpResponse::Ok() + .body(format!("{}", req.url_for("c", &["12345"]).unwrap())) }), )), ))) From 54619cb6802439a123d922fefb3578b68c8580cc Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 16 Apr 2020 06:54:34 +0900 Subject: [PATCH 097/157] actix-http: Remove `failure` support (#1449) --- Cargo.toml | 4 +--- actix-http/CHANGES.md | 2 ++ actix-http/Cargo.toml | 8 +------- actix-http/src/error.rs | 6 +----- test-server/Cargo.toml | 2 +- 5 files changed, 6 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 84f876579..7a0bef858 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ members = [ ] [features] -default = ["compress", "failure"] +default = ["compress"] # content-encoding support compress = ["actix-http/compress", "awc/compress"] @@ -50,8 +50,6 @@ compress = ["actix-http/compress", "awc/compress"] # sessions feature, session require "ring" crate and c compiler secure-cookies = ["actix-http/secure-cookies"] -failure = ["actix-http/failure"] - # openssl openssl = ["actix-tls/openssl", "awc/openssl", "open-ssl"] diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 9d75e1a05..140d78e95 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -5,6 +5,8 @@ ### Changed * Implement `std::error::Error` for our custom errors [#1422] +* Remove `failure` support for `ResponseError` since that crate + will be deprecated in the near future. [#1422]: https://github.com/actix/actix-web/pull/1422 diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index e78c74624..fcb05dd37 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -15,7 +15,7 @@ license = "MIT/Apache-2.0" edition = "2018" [package.metadata.docs.rs] -features = ["openssl", "rustls", "failure", "compress", "secure-cookies","actors"] +features = ["openssl", "rustls", "compress", "secure-cookies", "actors"] [lib] name = "actix_http" @@ -33,9 +33,6 @@ rustls = ["actix-tls/rustls", "actix-connect/rustls"] # enable compressison support compress = ["flate2", "brotli2"] -# failure integration. actix does not use failure anymore -failure = ["fail-ure"] - # support for secure cookies secure-cookies = ["ring"] @@ -89,9 +86,6 @@ ring = { version = "0.16.9", optional = true } brotli2 = { version="0.3.2", optional = true } flate2 = { version = "1.0.13", optional = true } -# optional deps -fail-ure = { version = "0.1.5", package="failure", optional = true } - [dev-dependencies] actix-server = "1.0.1" actix-connect = { version = "2.0.0-alpha.2", features=["openssl"] } diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 7ecdc6394..70cfa053f 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -34,7 +34,7 @@ pub type Result = result::Result; /// General purpose actix web error. /// -/// An actix web error is used to carry errors from `failure` or `std::error` +/// 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()`. /// @@ -950,10 +950,6 @@ where InternalError::new(err, StatusCode::NETWORK_AUTHENTICATION_REQUIRED).into() } -#[cfg(feature = "failure")] -/// Compatibility for `failure::Error` -impl ResponseError for fail_ure::Error {} - #[cfg(feature = "actors")] /// `InternalServerError` for `actix::MailboxError` /// This is supported on feature=`actors` only diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 29896898e..97581585d 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -52,7 +52,7 @@ sha1 = "0.6" slab = "0.4" serde_urlencoded = "0.6.1" time = { version = "0.2.7", default-features = false, features = ["std"] } -open-ssl = { version="0.10", package="openssl", optional = true } +open-ssl = { version="0.10", package = "openssl", optional = true } [dev-dependencies] actix-web = "3.0.0-alpha.1" From 5b0f7fff699f82aeb427cb5b5760af963b1ce591 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 21 Apr 2020 04:09:35 +0100 Subject: [PATCH 098/157] fix spelling errors in doc comments --- actix-files/src/lib.rs | 2 +- actix-framed/src/lib.rs | 2 +- actix-framed/src/request.rs | 4 ++-- actix-http/src/config.rs | 2 +- actix-http/src/cookie/jar.rs | 2 +- actix-http/src/cookie/mod.rs | 2 +- actix-http/src/cookie/secure/key.rs | 4 ++-- actix-http/src/error.rs | 4 ++-- actix-http/src/h1/decoder.rs | 2 +- actix-http/src/h1/dispatcher.rs | 6 ++--- .../src/header/common/content_disposition.rs | 22 +++++++++---------- actix-http/src/header/common/range.rs | 2 +- actix-http/src/header/map.rs | 2 +- actix-http/src/message.rs | 4 ++-- actix-http/src/ws/mod.rs | 4 ++-- actix-http/src/ws/proto.rs | 2 +- src/app.rs | 4 ++-- src/config.rs | 2 +- src/guard.rs | 4 ++-- src/middleware/condition.rs | 2 +- src/request.rs | 2 +- src/resource.rs | 2 +- src/scope.rs | 4 ++-- src/service.rs | 2 +- 24 files changed, 44 insertions(+), 44 deletions(-) diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index d910b7d5f..6d2da6c73 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -521,7 +521,7 @@ impl Service for FilesService { Err(e) => return Either::Left(ok(req.error_response(e))), }; - // full filepath + // full file path let path = match self.directory.join(&real_path.0).canonicalize() { Ok(path) => path, Err(e) => return self.handle_err(e, req), diff --git a/actix-framed/src/lib.rs b/actix-framed/src/lib.rs index 250533f39..9d2c71250 100644 --- a/actix-framed/src/lib.rs +++ b/actix-framed/src/lib.rs @@ -7,7 +7,7 @@ mod service; mod state; pub mod test; -// re-export for convinience +// re-export for convenience pub use actix_http::{http, Error, HttpMessage, Response, ResponseError}; pub use self::app::{FramedApp, FramedAppService}; diff --git a/actix-framed/src/request.rs b/actix-framed/src/request.rs index 1872dcc25..9a6cc8e39 100644 --- a/actix-framed/src/request.rs +++ b/actix-framed/src/request.rs @@ -42,7 +42,7 @@ impl FramedRequest { self.req.head() } - /// This method returns muttable reference to the request head. + /// This method returns mutable reference to the request head. /// panics if multiple references of http request exists. #[inline] pub fn head_mut(&mut self) -> &mut RequestHead { @@ -131,7 +131,7 @@ mod tests { use super::*; #[test] - fn test_reqest() { + fn test_request() { let buf = TestBuffer::empty(); let framed = Framed::new(buf, Codec::default()); let req = TestRequest::with_uri("/index.html?q=1") diff --git a/actix-http/src/config.rs b/actix-http/src/config.rs index 899046231..3221b9b8a 100644 --- a/actix-http/src/config.rs +++ b/actix-http/src/config.rs @@ -114,7 +114,7 @@ impl ServiceConfig { } #[inline] - /// Return state of connection keep-alive funcitonality + /// Return state of connection keep-alive functionality pub fn keep_alive_enabled(&self) -> bool { self.0.ka_enabled } diff --git a/actix-http/src/cookie/jar.rs b/actix-http/src/cookie/jar.rs index dd4ec477e..9fa6bdc7d 100644 --- a/actix-http/src/cookie/jar.rs +++ b/actix-http/src/cookie/jar.rs @@ -13,7 +13,7 @@ use super::secure::{Key, PrivateJar, SignedJar}; /// /// A `CookieJar` provides storage for any number of cookies. Any changes made /// to the jar are tracked; the changes can be retrieved via the -/// [delta](#method.delta) method which returns an interator over the changes. +/// [delta](#method.delta) method which returns an iterator over the changes. /// /// # Usage /// diff --git a/actix-http/src/cookie/mod.rs b/actix-http/src/cookie/mod.rs index 7f74abc95..b8ea6f4af 100644 --- a/actix-http/src/cookie/mod.rs +++ b/actix-http/src/cookie/mod.rs @@ -103,7 +103,7 @@ enum CookieStr { impl CookieStr { /// Retrieves the string `self` corresponds to. If `self` is derived from - /// indexes, the corresponding subslice of `string` is returned. Otherwise, + /// indexes, the corresponding sub-slice of `string` is returned. Otherwise, /// the concrete string is returned. /// /// # Panics diff --git a/actix-http/src/cookie/secure/key.rs b/actix-http/src/cookie/secure/key.rs index 779c16b75..41413921f 100644 --- a/actix-http/src/cookie/secure/key.rs +++ b/actix-http/src/cookie/secure/key.rs @@ -84,7 +84,7 @@ impl Key { } /// Generates signing/encryption keys from a secure, random source. Keys are - /// generated nondeterministically. + /// generated non-deterministically. /// /// # Panics /// @@ -103,7 +103,7 @@ impl Key { } /// Attempts to generate signing/encryption keys from a secure, random - /// source. Keys are generated nondeterministically. If randomness cannot be + /// source. Keys are generated non-deterministically. If randomness cannot be /// retrieved from the underlying operating system, returns `None`. /// /// # Example diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 70cfa053f..f0a4b70bc 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -18,7 +18,7 @@ use serde::de::value::Error as DeError; use serde_json::error::Error as JsonError; use serde_urlencoded::ser::Error as FormError; -// re-export for convinience +// re-export for convenience use crate::body::Body; pub use crate::cookie::ParseError as CookieParseError; use crate::helpers::Writer; @@ -432,7 +432,7 @@ pub enum DispatchError { Unknown, } -/// A set of error that can occure during parsing content type +/// A set of error that can occur during parsing content type #[derive(PartialEq, Debug, Display)] pub enum ContentTypeError { /// Can not parse content type diff --git a/actix-http/src/h1/decoder.rs b/actix-http/src/h1/decoder.rs index d3ccd8e5a..c9d3edf33 100644 --- a/actix-http/src/h1/decoder.rs +++ b/actix-http/src/h1/decoder.rs @@ -18,7 +18,7 @@ use crate::request::Request; const MAX_BUFFER_SIZE: usize = 131_072; const MAX_HEADERS: usize = 96; -/// Incoming messagd decoder +/// Incoming message decoder pub(crate) struct MessageDecoder(PhantomData); #[derive(Debug)] diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index ec01261e5..88f11c7c7 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -297,8 +297,8 @@ where /// Flush stream /// - /// true - got whouldblock - /// false - didnt get whouldblock + /// true - got WouldBlock + /// false - didn't get WouldBlock #[pin_project::project] fn poll_flush( self: Pin<&mut Self>, @@ -812,7 +812,7 @@ where return self.poll(cx); } - // we didnt get WouldBlock from write operation, + // we didn't get WouldBlock from write operation, // so data get written to kernel completely (OSX) // and we have to write again otherwise response can get stuck if inner.as_mut().poll_flush(cx)? || !drain { diff --git a/actix-http/src/header/common/content_disposition.rs b/actix-http/src/header/common/content_disposition.rs index aa2e00ec0..d65933901 100644 --- a/actix-http/src/header/common/content_disposition.rs +++ b/actix-http/src/header/common/content_disposition.rs @@ -90,40 +90,40 @@ pub enum DispositionParam { /// [RFC6266](https://tools.ietf.org/html/rfc6266) as *token "=" value*. Recipients should /// ignore unrecognizable parameters. Unknown(String, String), - /// An unrecognized extended paramater as defined in + /// An unrecognized extended parameter as defined in /// [RFC5987](https://tools.ietf.org/html/rfc5987) as *ext-parameter*, in /// [RFC6266](https://tools.ietf.org/html/rfc6266) as *ext-token "=" ext-value*. The single - /// trailling asterisk is not included. Recipients should ignore unrecognizable parameters. + /// trailing asterisk is not included. Recipients should ignore unrecognizable parameters. UnknownExt(String, ExtendedValue), } impl DispositionParam { - /// Returns `true` if the paramater is [`Name`](DispositionParam::Name). + /// Returns `true` if the parameter is [`Name`](DispositionParam::Name). #[inline] pub fn is_name(&self) -> bool { self.as_name().is_some() } - /// Returns `true` if the paramater is [`Filename`](DispositionParam::Filename). + /// Returns `true` if the parameter is [`Filename`](DispositionParam::Filename). #[inline] pub fn is_filename(&self) -> bool { self.as_filename().is_some() } - /// Returns `true` if the paramater is [`FilenameExt`](DispositionParam::FilenameExt). + /// Returns `true` if the parameter is [`FilenameExt`](DispositionParam::FilenameExt). #[inline] pub fn is_filename_ext(&self) -> bool { self.as_filename_ext().is_some() } - /// Returns `true` if the paramater is [`Unknown`](DispositionParam::Unknown) and the `name` + /// Returns `true` if the parameter is [`Unknown`](DispositionParam::Unknown) and the `name` #[inline] /// matches. pub fn is_unknown>(&self, name: T) -> bool { self.as_unknown(name).is_some() } - /// Returns `true` if the paramater is [`UnknownExt`](DispositionParam::UnknownExt) and the + /// Returns `true` if the parameter is [`UnknownExt`](DispositionParam::UnknownExt) and the /// `name` matches. #[inline] pub fn is_unknown_ext>(&self, name: T) -> bool { @@ -373,7 +373,7 @@ impl ContentDisposition { let param = if param_name.eq_ignore_ascii_case("name") { DispositionParam::Name(value) } else if param_name.eq_ignore_ascii_case("filename") { - // See also comments in test_from_raw_uncessary_percent_decode. + // See also comments in test_from_raw_unnecessary_percent_decode. DispositionParam::Filename(value) } else { DispositionParam::Unknown(param_name.to_owned(), value) @@ -529,7 +529,7 @@ impl fmt::Display for DispositionParam { // ; to use within parameter values // // - // See also comments in test_from_raw_uncessary_percent_decode. + // See also comments in test_from_raw_unnecessary_percent_decode. lazy_static! { static ref RE: Regex = Regex::new("[\x00-\x08\x10-\x1F\x7F\"\\\\]").unwrap(); } @@ -676,7 +676,7 @@ mod tests { fn test_from_raw_unordered() { let a = HeaderValue::from_static( "form-data; dummy=3; filename=\"sample.png\" ; name=upload;", - // Actually, a trailling semolocon is not compliant. But it is fine to accept. + // Actually, a trailing semicolon is not compliant. But it is fine to accept. ); let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap(); let b = ContentDisposition { @@ -833,7 +833,7 @@ mod tests { } #[test] - fn test_from_raw_uncessary_percent_decode() { + fn test_from_raw_unnecessary_percent_decode() { // In fact, RFC7578 (multipart/form-data) Section 2 and 4.2 suggests that filename with // non-ASCII characters MAY be percent-encoded. // On the contrary, RFC6266 or other RFCs related to Content-Disposition response header diff --git a/actix-http/src/header/common/range.rs b/actix-http/src/header/common/range.rs index 71718fc7a..fc1bc8159 100644 --- a/actix-http/src/header/common/range.rs +++ b/actix-http/src/header/common/range.rs @@ -7,7 +7,7 @@ use header::{Header, Raw}; /// `Range` header, defined in [RFC7233](https://tools.ietf.org/html/rfc7233#section-3.1) /// /// The "Range" header field on a GET request modifies the method -/// semantics to request transfer of only one or more subranges of the +/// semantics to request transfer of only one or more sub-ranges of the /// selected representation data, rather than the entire selected /// representation data. /// diff --git a/actix-http/src/header/map.rs b/actix-http/src/header/map.rs index 132087b9e..36c050b8f 100644 --- a/actix-http/src/header/map.rs +++ b/actix-http/src/header/map.rs @@ -7,7 +7,7 @@ use http::header::{HeaderName, HeaderValue}; /// A set of HTTP headers /// -/// `HeaderMap` is an multimap of [`HeaderName`] to values. +/// `HeaderMap` is an multi-map of [`HeaderName`] to values. /// /// [`HeaderName`]: struct.HeaderName.html #[derive(Debug, Clone)] diff --git a/actix-http/src/message.rs b/actix-http/src/message.rs index d005ad04a..5e53f73b6 100644 --- a/actix-http/src/message.rs +++ b/actix-http/src/message.rs @@ -99,13 +99,13 @@ impl RequestHead { } /// Is to uppercase headers with Camel-Case. - /// Befault is `false` + /// Default is `false` #[inline] pub fn camel_case_headers(&self) -> bool { self.flags.contains(Flags::CAMEL_CASE) } - /// Set `true` to send headers which are uppercased with Camel-Case. + /// Set `true` to send headers which are formatted as Camel-Case. #[inline] pub fn set_camel_case_headers(&mut self, val: bool) { if val { diff --git a/actix-http/src/ws/mod.rs b/actix-http/src/ws/mod.rs index 3d83943c7..6ffdecc35 100644 --- a/actix-http/src/ws/mod.rs +++ b/actix-http/src/ws/mod.rs @@ -110,7 +110,7 @@ impl ResponseError for HandshakeError { } } -/// Verify `WebSocket` handshake request and create handshake reponse. +/// Verify `WebSocket` handshake request and create handshake response. // /// `protocols` is a sequence of known protocols. On successful handshake, // /// the returned response headers contain the first protocol in this list // /// which the server also knows. @@ -170,7 +170,7 @@ pub fn verify_handshake(req: &RequestHead) -> Result<(), HandshakeError> { Ok(()) } -/// Create websocket's handshake response +/// Create websocket handshake response /// /// This function returns handshake `Response`, ready to send to peer. pub fn handshake_response(req: &RequestHead) -> ResponseBuilder { diff --git a/actix-http/src/ws/proto.rs b/actix-http/src/ws/proto.rs index 7b55cbf1a..dd3078a6c 100644 --- a/actix-http/src/ws/proto.rs +++ b/actix-http/src/ws/proto.rs @@ -203,7 +203,7 @@ impl> From<(CloseCode, T)> for CloseReason { static WS_GUID: &str = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; -// TODO: hash is always same size, we dont need String +// TODO: hash is always same size, we don't need String pub fn hash_key(key: &[u8]) -> String { use sha1::Digest; let mut hasher = sha1::Sha1::new(); diff --git a/src/app.rs b/src/app.rs index a060eb53e..ed2aff8e6 100644 --- a/src/app.rs +++ b/src/app.rs @@ -320,7 +320,7 @@ where /// Registers middleware, in the form of a middleware component (type), /// that runs during inbound and/or outbound processing in the request - /// lifecycle (request -> response), modifying request/response as + /// life-cycle (request -> response), modifying request/response as /// necessary, across all requests managed by the *Application*. /// /// Use middleware when you need to read or modify *every* request or @@ -385,7 +385,7 @@ where } /// Registers middleware, in the form of a closure, that runs during inbound - /// and/or outbound processing in the request lifecycle (request -> response), + /// and/or outbound processing in the request life-cycle (request -> response), /// modifying request/response as necessary, across all requests managed by /// the *Application*. /// diff --git a/src/config.rs b/src/config.rs index 6ce96767d..6db378c7b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -50,7 +50,7 @@ impl AppService { } } - /// Check if root is beeing configured + /// Check if root is being configured pub fn is_root(&self) -> bool { self.root } diff --git a/src/guard.rs b/src/guard.rs index e6303e9c7..25284236a 100644 --- a/src/guard.rs +++ b/src/guard.rs @@ -6,7 +6,7 @@ //! It is possible to add guards to *scopes*, *resources* //! and *routes*. Actix provide several guards by default, like various //! http methods, header, etc. To become a guard, type must implement `Guard` -//! trait. Simple functions coulds guards as well. +//! trait. Simple functions could be guards as well. //! //! Guards can not modify the request object. But it is possible //! to store extra attributes on a request by using the `Extensions` container. @@ -100,7 +100,7 @@ pub fn Any(guard: F) -> AnyGuard { AnyGuard(vec![Box::new(guard)]) } -/// Matches if any of supplied guards matche. +/// Matches any of supplied guards. pub struct AnyGuard(Vec>); impl AnyGuard { diff --git a/src/middleware/condition.rs b/src/middleware/condition.rs index 68d06837e..8c6909134 100644 --- a/src/middleware/condition.rs +++ b/src/middleware/condition.rs @@ -5,7 +5,7 @@ use actix_service::{Service, Transform}; use futures::future::{ok, Either, FutureExt, LocalBoxFuture}; /// `Middleware` for conditionally enables another middleware. -/// The controled middleware must not change the `Service` interfaces. +/// The controlled middleware must not change the `Service` interfaces. /// This means you cannot control such middlewares like `Logger` or `Compress`. /// /// ## Usage diff --git a/src/request.rs b/src/request.rs index cd9c72313..51a1c54ff 100644 --- a/src/request.rs +++ b/src/request.rs @@ -57,7 +57,7 @@ impl HttpRequest { &self.0.head } - /// This method returns muttable reference to the request head. + /// This method returns mutable reference to the request head. /// panics if multiple references of http request exists. #[inline] pub(crate) fn head_mut(&mut self) -> &mut RequestHead { diff --git a/src/resource.rs b/src/resource.rs index a4ab19052..dba32b43c 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -46,7 +46,7 @@ type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Err /// ``` /// /// If no matching route could be found, *405* response code get returned. -/// Default behavior could be overriden with `default_resource()` method. +/// Default behavior could be overridden with `default_resource()` method. pub struct Resource { endpoint: T, rdef: Vec, diff --git a/src/scope.rs b/src/scope.rs index 7ead71787..1569e90b0 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -303,7 +303,7 @@ where /// Registers middleware, in the form of a middleware component (type), /// that runs during inbound processing in the request - /// lifecycle (request -> response), modifying request as + /// life-cycle (request -> response), modifying request as /// necessary, across all requests managed by the *Scope*. Scope-level /// middleware is more limited in what it can modify, relative to Route or /// Application level middleware, in that Scope-level middleware can not modify @@ -344,7 +344,7 @@ where } /// Registers middleware, in the form of a closure, that runs during inbound - /// processing in the request lifecycle (request -> response), modifying + /// processing in the request life-cycle (request -> response), modifying /// request as necessary, across all requests managed by the *Scope*. /// Scope-level middleware is more limited in what it can modify, relative /// to Route or Application level middleware, in that Scope-level middleware diff --git a/src/service.rs b/src/service.rs index e51be9964..b783fd720 100644 --- a/src/service.rs +++ b/src/service.rs @@ -69,7 +69,7 @@ impl ServiceRequest { /// Construct request from parts. /// - /// `ServiceRequest` can be re-constructed only if `req` hasnt been cloned. + /// `ServiceRequest` can be re-constructed only if `req` hasn't been cloned. pub fn from_parts( mut req: HttpRequest, pl: Payload, From ce24630d31fc85e301bbb401111df56573964286 Mon Sep 17 00:00:00 2001 From: Huy Date: Wed, 22 Apr 2020 11:39:09 -0700 Subject: [PATCH 099/157] Fix typos in MIGRATION.md (#1470) * Fix typos in MIGRATION.md Those are `crate` not `create` * Update MIGRATION.md --- MIGRATION.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 86721e0eb..672aa253e 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -360,7 +360,7 @@ * `actix_web::server` module has been removed. To start http server use `actix_web::HttpServer` type -* StaticFiles and NamedFile has been move to separate create. +* StaticFiles and NamedFile have been moved to a separate crate. instead of `use actix_web::fs::StaticFile` @@ -370,7 +370,7 @@ use `use actix_files::NamedFile` -* Multipart has been move to separate create. +* Multipart has been moved to a separate crate. instead of `use actix_web::multipart::Multipart` From b047413b39fa98daf6a94e999512d295200d80ef Mon Sep 17 00:00:00 2001 From: Huston Bokinsky Date: Tue, 28 Apr 2020 19:13:09 -0700 Subject: [PATCH 100/157] Small ws codec fix (#1465) * Small ws codec fix * Update actix-http/Changes.md Co-authored-by: Huston Bokinsky --- actix-http/CHANGES.md | 2 ++ actix-http/src/ws/codec.rs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 140d78e95..43f189afc 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -7,6 +7,8 @@ * Implement `std::error::Error` for our custom errors [#1422] * Remove `failure` support for `ResponseError` since that crate will be deprecated in the near future. +* Fix a mistake in the encoding of websocket continuation messages wherein + Item::FirstText and Item::FirstBinary are each encoded as the other. [#1422]: https://github.com/actix/actix-web/pull/1422 diff --git a/actix-http/src/ws/codec.rs b/actix-http/src/ws/codec.rs index a37208a2b..733976a78 100644 --- a/actix-http/src/ws/codec.rs +++ b/actix-http/src/ws/codec.rs @@ -137,7 +137,7 @@ impl Encoder for Codec { Parser::write_message( dst, &data[..], - OpCode::Binary, + OpCode::Text, false, !self.flags.contains(Flags::SERVER), ) @@ -151,7 +151,7 @@ impl Encoder for Codec { Parser::write_message( dst, &data[..], - OpCode::Text, + OpCode::Binary, false, !self.flags.contains(Flags::SERVER), ) From bb17280f512927cbeed95a6ce99dd1d42e463add Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 29 Apr 2020 07:38:53 +0100 Subject: [PATCH 101/157] simplify data factory future polling (#1473) Co-authored-by: Yuki Okushi --- src/app.rs | 15 +++++- src/app_service.rs | 51 ++++++++++++-------- src/test.rs | 118 ++++++++++++++++++++++++++------------------- 3 files changed, 113 insertions(+), 71 deletions(-) diff --git a/src/app.rs b/src/app.rs index ed2aff8e6..c611f2657 100644 --- a/src/app.rs +++ b/src/app.rs @@ -476,13 +476,13 @@ where mod tests { use actix_service::Service; use bytes::Bytes; - use futures::future::ok; + use futures::future::{ok, err}; use super::*; use crate::http::{header, HeaderValue, Method, StatusCode}; use crate::middleware::DefaultHeaders; use crate::service::ServiceRequest; - use crate::test::{call_service, init_service, read_body, TestRequest}; + use crate::test::{call_service, init_service, try_init_service, read_body, TestRequest}; use crate::{web, HttpRequest, HttpResponse}; #[actix_rt::test] @@ -551,6 +551,17 @@ mod tests { assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); } + #[actix_rt::test] + async fn test_data_factory_errors() { + let srv = + try_init_service(App::new().data_factory(|| err::(())).service( + web::resource("/").to(|_: web::Data| HttpResponse::Ok()), + )) + .await; + + assert!(srv.is_err()); + } + #[actix_rt::test] async fn test_extension() { let mut srv = init_service(App::new().app_data(10usize).service( diff --git a/src/app_service.rs b/src/app_service.rs index ccfefbc68..67fa4dc2c 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -9,7 +9,7 @@ use actix_http::{Extensions, Request, Response}; use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url}; use actix_service::boxed::{self, BoxService, BoxServiceFactory}; use actix_service::{fn_service, Service, ServiceFactory}; -use futures::future::{ok, FutureExt, LocalBoxFuture}; +use futures::future::{join_all, ok, FutureExt, LocalBoxFuture}; use crate::config::{AppConfig, AppService}; use crate::data::DataFactory; @@ -109,12 +109,15 @@ where let rmap = Rc::new(rmap); rmap.finish(rmap.clone()); + // start all data factory futures + let factory_futs = join_all(self.data_factories.iter().map(|f| f())); + AppInitResult { endpoint: None, endpoint_fut: self.endpoint.new_service(()), data: self.data.clone(), - data_factories: Vec::new(), - data_factories_fut: self.data_factories.iter().map(|f| f()).collect(), + data_factories: None, + data_factories_fut: factory_futs.boxed_local(), extensions: Some( self.extensions .borrow_mut() @@ -133,15 +136,21 @@ pub struct AppInitResult where T: ServiceFactory, { - endpoint: Option, #[pin] endpoint_fut: T::Future, + // a Some signals completion of endpoint creation + endpoint: Option, + + #[pin] + data_factories_fut: LocalBoxFuture<'static, Vec, ()>>>, + // a Some signals completion of factory futures + data_factories: Option>>, + rmap: Rc, config: AppConfig, data: Rc>>, - data_factories: Vec>, - data_factories_fut: Vec, ()>>>, extensions: Option, + _t: PhantomData, } @@ -161,44 +170,46 @@ where let this = self.project(); // async data factories - let mut idx = 0; - while idx < this.data_factories_fut.len() { - match Pin::new(&mut this.data_factories_fut[idx]).poll(cx)? { - Poll::Ready(f) => { - this.data_factories.push(f); - let _ = this.data_factories_fut.remove(idx); - } - Poll::Pending => idx += 1, + if let Poll::Ready(factories) = this.data_factories_fut.poll(cx) { + let factories: Result, ()> = factories.into_iter().collect(); + + if let Ok(factories) = factories { + this.data_factories.replace(factories); + } else { + return Poll::Ready(Err(())); } } + // app service and middleware if this.endpoint.is_none() { if let Poll::Ready(srv) = this.endpoint_fut.poll(cx)? { *this.endpoint = Some(srv); } } - if this.endpoint.is_some() && this.data_factories_fut.is_empty() { + // not using if let so condition only needs shared ref + if this.endpoint.is_some() && this.data_factories.is_some() { // create app data container let mut data = this.extensions.take().unwrap(); + for f in this.data.iter() { f.create(&mut data); } - for f in this.data_factories.iter() { + for f in this.data_factories.take().unwrap().iter() { f.create(&mut data); } - Poll::Ready(Ok(AppInitService { + return Poll::Ready(Ok(AppInitService { service: this.endpoint.take().unwrap(), rmap: this.rmap.clone(), config: this.config.clone(), data: Rc::new(data), pool: HttpRequestPool::create(), - })) - } else { - Poll::Pending + })); } + + Poll::Pending } } diff --git a/src/test.rs b/src/test.rs index 19ea8bbef..c8a738d83 100644 --- a/src/test.rs +++ b/src/test.rs @@ -78,6 +78,26 @@ pub fn default_service( pub async fn init_service( app: R, ) -> impl Service, Error = E> +where + R: IntoServiceFactory, + S: ServiceFactory< + Config = AppConfig, + Request = Request, + Response = ServiceResponse, + Error = E, + >, + S::InitError: std::fmt::Debug, +{ + try_init_service(app).await.expect("service initilization failed") +} + +/// Fallible version of init_service that allows testing data factory errors. +pub(crate) async fn try_init_service( + app: R, +) -> Result< + impl Service, Error = E>, + S::InitError, +> where R: IntoServiceFactory, S: ServiceFactory< @@ -89,7 +109,7 @@ where S::InitError: std::fmt::Debug, { let srv = app.into_factory(); - srv.new_service(AppConfig::default()).await.unwrap() + srv.new_service(AppConfig::default()).await } /// Calls service and waits for response future completion. @@ -580,7 +600,7 @@ impl TestRequest { pub async fn send_request(self, app: &mut S) -> S::Response where S: Service, Error = E>, - E: std::fmt::Debug + E: std::fmt::Debug, { let req = self.to_request(); call_service(app, req).await @@ -1125,8 +1145,8 @@ mod tests { #[actix_rt::test] async fn test_response_json() { let mut app = init_service(App::new().service(web::resource("/people").route( - web::post().to(|person: web::Json| { - async { HttpResponse::Ok().json(person.into_inner()) } + web::post().to(|person: web::Json| async { + HttpResponse::Ok().json(person.into_inner()) }), ))) .await; @@ -1146,8 +1166,8 @@ mod tests { #[actix_rt::test] async fn test_body_json() { let mut app = init_service(App::new().service(web::resource("/people").route( - web::post().to(|person: web::Json| { - async { HttpResponse::Ok().json(person.into_inner()) } + web::post().to(|person: web::Json| async { + HttpResponse::Ok().json(person.into_inner()) }), ))) .await; @@ -1168,8 +1188,8 @@ mod tests { #[actix_rt::test] async fn test_request_response_form() { let mut app = init_service(App::new().service(web::resource("/people").route( - web::post().to(|person: web::Form| { - async { HttpResponse::Ok().json(person.into_inner()) } + web::post().to(|person: web::Form| async { + HttpResponse::Ok().json(person.into_inner()) }), ))) .await; @@ -1194,8 +1214,8 @@ mod tests { #[actix_rt::test] async fn test_request_response_json() { let mut app = init_service(App::new().service(web::resource("/people").route( - web::post().to(|person: web::Json| { - async { HttpResponse::Ok().json(person.into_inner()) } + web::post().to(|person: web::Json| async { + HttpResponse::Ok().json(person.into_inner()) }), ))) .await; @@ -1259,53 +1279,53 @@ mod tests { assert!(res.status().is_success()); } -/* + /* - Comment out until actix decoupled of actix-http: - https://github.com/actix/actix/issues/321 + Comment out until actix decoupled of actix-http: + https://github.com/actix/actix/issues/321 - use futures::FutureExt; + use futures::FutureExt; - #[actix_rt::test] - async fn test_actor() { - use actix::Actor; + #[actix_rt::test] + async fn test_actor() { + use actix::Actor; - struct MyActor; + struct MyActor; - struct Num(usize); - impl actix::Message for Num { - type Result = usize; - } - impl actix::Actor for MyActor { - type Context = actix::Context; - } - impl actix::Handler for MyActor { - type Result = usize; - fn handle(&mut self, msg: Num, _: &mut Self::Context) -> Self::Result { - msg.0 + struct Num(usize); + impl actix::Message for Num { + type Result = usize; + } + impl actix::Actor for MyActor { + type Context = actix::Context; + } + impl actix::Handler for MyActor { + type Result = usize; + fn handle(&mut self, msg: Num, _: &mut Self::Context) -> Self::Result { + msg.0 + } } - } - let mut app = init_service(App::new().service(web::resource("/index.html").to( - move || { - addr.send(Num(1)).map(|res| match res { - Ok(res) => { - if res == 1 { - Ok(HttpResponse::Ok()) - } else { - Ok(HttpResponse::BadRequest()) + let mut app = init_service(App::new().service(web::resource("/index.html").to( + move || { + addr.send(Num(1)).map(|res| match res { + Ok(res) => { + if res == 1 { + Ok(HttpResponse::Ok()) + } else { + Ok(HttpResponse::BadRequest()) + } } - } - Err(err) => Err(err), - }) - }, - ))) - .await; + Err(err) => Err(err), + }) + }, + ))) + .await; - let req = TestRequest::post().uri("/index.html").to_request(); - let res = app.call(req).await.unwrap(); - assert!(res.status().is_success()); - } -*/ + let req = TestRequest::post().uri("/index.html").to_request(); + let res = app.call(req).await.unwrap(); + assert!(res.status().is_success()); + } + */ } From c27d3fad8ece1e1440153f0abb47b96927c66f97 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 29 Apr 2020 18:20:47 +0100 Subject: [PATCH 102/157] clarify resource/scope app data overriding (#1476) * relocate FnDataFactory * clarify app data overriding in Scope and Resource Co-authored-by: Yuki Okushi --- actix-http/src/extensions.rs | 193 ++++++++++++++++++----------------- src/app.rs | 6 +- src/app_service.rs | 4 +- src/data.rs | 5 +- src/resource.rs | 85 ++++++++++++++- src/scope.rs | 6 +- 6 files changed, 191 insertions(+), 108 deletions(-) diff --git a/actix-http/src/extensions.rs b/actix-http/src/extensions.rs index 5114ce140..6a4a034a4 100644 --- a/actix-http/src/extensions.rs +++ b/actix-http/src/extensions.rs @@ -67,108 +67,113 @@ impl fmt::Debug for Extensions { } } -#[test] -fn test_remove() { - let mut map = Extensions::new(); +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_remove() { + let mut map = Extensions::new(); - map.insert::(123); - assert!(map.get::().is_some()); + map.insert::(123); + assert!(map.get::().is_some()); - map.remove::(); - assert!(map.get::().is_none()); -} - -#[test] -fn test_clear() { - let mut map = Extensions::new(); - - map.insert::(8); - map.insert::(16); - map.insert::(32); - - assert!(map.contains::()); - assert!(map.contains::()); - assert!(map.contains::()); - - map.clear(); - - assert!(!map.contains::()); - assert!(!map.contains::()); - assert!(!map.contains::()); - - map.insert::(10); - assert_eq!(*map.get::().unwrap(), 10); -} - -#[test] -fn test_integers() { - let mut map = Extensions::new(); - - map.insert::(8); - map.insert::(16); - map.insert::(32); - map.insert::(64); - map.insert::(128); - map.insert::(8); - map.insert::(16); - map.insert::(32); - map.insert::(64); - map.insert::(128); - assert!(map.get::().is_some()); - assert!(map.get::().is_some()); - assert!(map.get::().is_some()); - assert!(map.get::().is_some()); - assert!(map.get::().is_some()); - assert!(map.get::().is_some()); - assert!(map.get::().is_some()); - assert!(map.get::().is_some()); - assert!(map.get::().is_some()); - assert!(map.get::().is_some()); -} - -#[test] -fn test_composition() { - struct Magi(pub T); - - struct Madoka { - pub god: bool, + map.remove::(); + assert!(map.get::().is_none()); } - struct Homura { - pub attempts: usize, + #[test] + fn test_clear() { + let mut map = Extensions::new(); + + map.insert::(8); + map.insert::(16); + map.insert::(32); + + assert!(map.contains::()); + assert!(map.contains::()); + assert!(map.contains::()); + + map.clear(); + + assert!(!map.contains::()); + assert!(!map.contains::()); + assert!(!map.contains::()); + + map.insert::(10); + assert_eq!(*map.get::().unwrap(), 10); } - struct Mami { - pub guns: usize, + #[test] + fn test_integers() { + let mut map = Extensions::new(); + + map.insert::(8); + map.insert::(16); + map.insert::(32); + map.insert::(64); + map.insert::(128); + map.insert::(8); + map.insert::(16); + map.insert::(32); + map.insert::(64); + map.insert::(128); + assert!(map.get::().is_some()); + assert!(map.get::().is_some()); + assert!(map.get::().is_some()); + assert!(map.get::().is_some()); + assert!(map.get::().is_some()); + assert!(map.get::().is_some()); + assert!(map.get::().is_some()); + assert!(map.get::().is_some()); + assert!(map.get::().is_some()); + assert!(map.get::().is_some()); } - let mut map = Extensions::new(); + #[test] + fn test_composition() { + struct Magi(pub T); - map.insert(Magi(Madoka { god: false })); - map.insert(Magi(Homura { attempts: 0 })); - map.insert(Magi(Mami { guns: 999 })); + struct Madoka { + pub god: bool, + } - assert!(!map.get::>().unwrap().0.god); - assert_eq!(0, map.get::>().unwrap().0.attempts); - assert_eq!(999, map.get::>().unwrap().0.guns); -} - -#[test] -fn test_extensions() { - #[derive(Debug, PartialEq)] - struct MyType(i32); - - let mut extensions = Extensions::new(); - - extensions.insert(5i32); - extensions.insert(MyType(10)); - - assert_eq!(extensions.get(), Some(&5i32)); - assert_eq!(extensions.get_mut(), Some(&mut 5i32)); - - assert_eq!(extensions.remove::(), Some(5i32)); - assert!(extensions.get::().is_none()); - - assert_eq!(extensions.get::(), None); - assert_eq!(extensions.get(), Some(&MyType(10))); + struct Homura { + pub attempts: usize, + } + + struct Mami { + pub guns: usize, + } + + let mut map = Extensions::new(); + + map.insert(Magi(Madoka { god: false })); + map.insert(Magi(Homura { attempts: 0 })); + map.insert(Magi(Mami { guns: 999 })); + + assert!(!map.get::>().unwrap().0.god); + assert_eq!(0, map.get::>().unwrap().0.attempts); + assert_eq!(999, map.get::>().unwrap().0.guns); + } + + #[test] + fn test_extensions() { + #[derive(Debug, PartialEq)] + struct MyType(i32); + + let mut extensions = Extensions::new(); + + extensions.insert(5i32); + extensions.insert(MyType(10)); + + assert_eq!(extensions.get(), Some(&5i32)); + assert_eq!(extensions.get_mut(), Some(&mut 5i32)); + + assert_eq!(extensions.remove::(), Some(5i32)); + assert!(extensions.get::().is_none()); + + assert_eq!(extensions.get::(), None); + assert_eq!(extensions.get(), Some(&MyType(10))); + } } diff --git a/src/app.rs b/src/app.rs index c611f2657..7d3100db7 100644 --- a/src/app.rs +++ b/src/app.rs @@ -10,11 +10,11 @@ use actix_service::boxed::{self, BoxServiceFactory}; use actix_service::{ apply, apply_fn_factory, IntoServiceFactory, ServiceFactory, Transform, }; -use futures::future::{FutureExt, LocalBoxFuture}; +use futures::future::FutureExt; use crate::app_service::{AppEntry, AppInit, AppRoutingFactory}; use crate::config::ServiceConfig; -use crate::data::{Data, DataFactory}; +use crate::data::{Data, DataFactory, FnDataFactory}; use crate::dev::ResourceDef; use crate::error::Error; use crate::resource::Resource; @@ -25,8 +25,6 @@ use crate::service::{ }; type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>; -type FnDataFactory = - Box LocalBoxFuture<'static, Result, ()>>>; /// Application builder - structure that follows the builder pattern /// for building application instances. diff --git a/src/app_service.rs b/src/app_service.rs index 67fa4dc2c..808592e58 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -12,7 +12,7 @@ use actix_service::{fn_service, Service, ServiceFactory}; use futures::future::{join_all, ok, FutureExt, LocalBoxFuture}; use crate::config::{AppConfig, AppService}; -use crate::data::DataFactory; +use crate::data::{FnDataFactory, DataFactory}; use crate::error::Error; use crate::guard::Guard; use crate::request::{HttpRequest, HttpRequestPool}; @@ -23,8 +23,6 @@ type Guards = Vec>; type HttpService = BoxService; type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>; type BoxResponse = LocalBoxFuture<'static, Result>; -type FnDataFactory = - Box LocalBoxFuture<'static, Result, ()>>>; /// Service factory to convert `Request` to a `ServiceRequest`. /// It also executes data factories. diff --git a/src/data.rs b/src/data.rs index c36418942..0c04e1d90 100644 --- a/src/data.rs +++ b/src/data.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use actix_http::error::{Error, ErrorInternalServerError}; use actix_http::Extensions; -use futures::future::{err, ok, Ready}; +use futures::future::{err, ok, LocalBoxFuture, Ready}; use crate::dev::Payload; use crate::extract::FromRequest; @@ -14,6 +14,9 @@ pub(crate) trait DataFactory { fn create(&self, extensions: &mut Extensions) -> bool; } +pub(crate) type FnDataFactory = + Box LocalBoxFuture<'static, Result, ()>>>; + /// Application data. /// /// Application data is an arbitrary data attached to the app. diff --git a/src/resource.rs b/src/resource.rs index dba32b43c..634294cc2 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -196,9 +196,11 @@ where self.app_data(Data::new(data)) } - /// Set or override application data. + /// Add resource data. /// - /// This method overrides data stored with [`App::app_data()`](#method.app_data) + /// If used, this method will create a new data context used for extracting + /// from requests. Data added here is *not* merged with data added on App + /// or containing scopes. pub fn app_data(mut self, data: U) -> Self { if self.data.is_none() { self.data = Some(Extensions::new()); @@ -393,6 +395,7 @@ where if let Some(ref mut ext) = self.data { config.set_service_data(ext); } + config.register_service(rdef, guards, self, None) } } @@ -587,13 +590,14 @@ mod tests { use actix_rt::time::delay_for; use actix_service::Service; + use bytes::Bytes; use futures::future::ok; use crate::http::{header, HeaderValue, Method, StatusCode}; use crate::middleware::DefaultHeaders; use crate::service::ServiceRequest; - use crate::test::{call_service, init_service, TestRequest}; - use crate::{guard, web, App, Error, HttpResponse}; + use crate::test::{call_service, init_service, read_body, TestRequest}; + use crate::{guard, web, App, Error, HttpRequest, HttpResponse}; #[actix_rt::test] async fn test_middleware() { @@ -619,6 +623,79 @@ mod tests { ); } + #[actix_rt::test] + async fn test_overwritten_data() { + #[allow(dead_code)] + fn echo_usize(req: HttpRequest) -> HttpResponse { + let num = req.app_data::().unwrap(); + HttpResponse::Ok().body(format!("{}", num)) + } + + #[allow(dead_code)] + fn echo_u32(req: HttpRequest) -> HttpResponse { + let num = req.app_data::().unwrap(); + HttpResponse::Ok().body(format!("{}", num)) + } + + #[allow(dead_code)] + fn echo_both(req: HttpRequest) -> HttpResponse { + let num = req.app_data::().unwrap(); + let num2 = req.app_data::().unwrap(); + HttpResponse::Ok().body(format!("{}-{}", num, num2)) + } + + let mut srv = init_service( + App::new() + .app_data(88usize) + .service(web::resource("/").route(web::get().to(echo_usize))) + .service( + web::resource("/one") + .app_data(1usize) + .route(web::get().to(echo_usize)), + ) + .service( + web::resource("/two") + .app_data(2usize) + .route(web::get().to(echo_usize)), + ) + .service( + web::resource("/three") + .app_data(3u32) + // this doesnt work because app_data "overrides" the + // entire data field potentially passed down + // .route(web::get().to(echo_both)), + .route(web::get().to(echo_u32)), + ) + .service(web::resource("/eight").route(web::get().to(echo_usize))), + ) + .await; + + let req = TestRequest::get().uri("/").to_request(); + let resp = srv.call(req).await.unwrap(); + let body = read_body(resp).await; + assert_eq!(body, Bytes::from_static(b"88")); + + let req = TestRequest::get().uri("/one").to_request(); + let resp = srv.call(req).await.unwrap(); + let body = read_body(resp).await; + assert_eq!(body, Bytes::from_static(b"1")); + + let req = TestRequest::get().uri("/two").to_request(); + let resp = srv.call(req).await.unwrap(); + let body = read_body(resp).await; + assert_eq!(body, Bytes::from_static(b"2")); + + // let req = TestRequest::get().uri("/three").to_request(); + // let resp = srv.call(req).await.unwrap(); + // let body = read_body(resp).await; + // assert_eq!(body, Bytes::from_static(b"88-3")); + + let req = TestRequest::get().uri("/eight").to_request(); + let resp = srv.call(req).await.unwrap(); + let body = read_body(resp).await; + assert_eq!(body, Bytes::from_static(b"88")); + } + #[actix_rt::test] async fn test_middleware_fn() { let mut srv = init_service( diff --git a/src/scope.rs b/src/scope.rs index 1569e90b0..2016c6f1c 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -151,9 +151,11 @@ where self.app_data(Data::new(data)) } - /// Set or override application data. + /// Add scope data. /// - /// This method overrides data stored with [`App::app_data()`](#method.app_data) + /// If used, this method will create a new data context used for extracting + /// from requests. Data added here is *not* merged with data added on App + /// or containing scopes. pub fn app_data(mut self, data: U) -> Self { if self.data.is_none() { self.data = Some(Extensions::new()); From d5ceae20740837bddef85053c3354452b437877a Mon Sep 17 00:00:00 2001 From: Mikail Bagishov Date: Sat, 2 May 2020 12:14:50 +0300 Subject: [PATCH 103/157] Replace deprecated now with now_utc (#1481) * Replace deprecated now with now_utc * Update doctest --- actix-http/src/config.rs | 2 +- actix-http/src/cookie/builder.rs | 2 +- actix-http/src/cookie/jar.rs | 2 +- actix-http/src/cookie/mod.rs | 2 +- actix-http/src/time_parser.rs | 2 +- src/middleware/logger.rs | 16 ++++++++-------- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/actix-http/src/config.rs b/actix-http/src/config.rs index 3221b9b8a..abf3d8ff9 100644 --- a/actix-http/src/config.rs +++ b/actix-http/src/config.rs @@ -214,7 +214,7 @@ impl Date { write!( self, "{}", - OffsetDateTime::now().format("%a, %d %b %Y %H:%M:%S GMT") + OffsetDateTime::now_utc().format("%a, %d %b %Y %H:%M:%S GMT") ) .unwrap(); } diff --git a/actix-http/src/cookie/builder.rs b/actix-http/src/cookie/builder.rs index 80e7ee71f..b64352e35 100644 --- a/actix-http/src/cookie/builder.rs +++ b/actix-http/src/cookie/builder.rs @@ -63,7 +63,7 @@ impl CookieBuilder { /// use actix_http::cookie::Cookie; /// /// let c = Cookie::build("foo", "bar") - /// .expires(time::OffsetDateTime::now()) + /// .expires(time::OffsetDateTime::now_utc()) /// .finish(); /// /// assert!(c.expires().is_some()); diff --git a/actix-http/src/cookie/jar.rs b/actix-http/src/cookie/jar.rs index 9fa6bdc7d..0c76c1cfe 100644 --- a/actix-http/src/cookie/jar.rs +++ b/actix-http/src/cookie/jar.rs @@ -221,7 +221,7 @@ impl CookieJar { if self.original_cookies.contains(cookie.name()) { cookie.set_value(""); cookie.set_max_age(Duration::zero()); - cookie.set_expires(OffsetDateTime::now() - Duration::days(365)); + cookie.set_expires(OffsetDateTime::now_utc() - Duration::days(365)); self.delta_cookies.replace(DeltaCookie::removed(cookie)); } else { self.delta_cookies.remove(cookie.name()); diff --git a/actix-http/src/cookie/mod.rs b/actix-http/src/cookie/mod.rs index b8ea6f4af..b94e0fe0f 100644 --- a/actix-http/src/cookie/mod.rs +++ b/actix-http/src/cookie/mod.rs @@ -733,7 +733,7 @@ impl<'c> Cookie<'c> { pub fn make_permanent(&mut self) { let twenty_years = Duration::days(365 * 20); self.set_max_age(twenty_years); - self.set_expires(OffsetDateTime::now() + twenty_years); + self.set_expires(OffsetDateTime::now_utc() + twenty_years); } fn fmt_parameters(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/actix-http/src/time_parser.rs b/actix-http/src/time_parser.rs index b5b07ccba..0d06a5867 100644 --- a/actix-http/src/time_parser.rs +++ b/actix-http/src/time_parser.rs @@ -19,7 +19,7 @@ fn try_parse_rfc_850(time: &str) -> Option { // If the `time` string contains a two-digit year, then as per RFC 2616 ยง 19.3, // we consider the year as part of this century if it's within the next 50 years, // otherwise we consider as part of the previous century. - let now = OffsetDateTime::now(); + let now = OffsetDateTime::now_utc(); let century_start_year = (now.year() / 100) * 100; let mut expanded_year = century_start_year + dt.year(); diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index e40fe648a..7d1577c96 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -163,11 +163,11 @@ where LoggerResponse { fut: self.service.call(req), format: None, - time: OffsetDateTime::now(), + time: OffsetDateTime::now_utc(), _t: PhantomData, } } else { - let now = OffsetDateTime::now(); + let now = OffsetDateTime::now_utc(); let mut format = self.inner.format.clone(); for unit in &mut format.0 { @@ -380,12 +380,12 @@ impl FormatText { FormatText::Percent => "%".fmt(fmt), FormatText::ResponseSize => size.fmt(fmt), FormatText::Time => { - let rt = OffsetDateTime::now() - entry_time; + let rt = OffsetDateTime::now_utc() - entry_time; let rt = rt.as_seconds_f64(); fmt.write_fmt(format_args!("{:.6}", rt)) } FormatText::TimeMillis => { - let rt = OffsetDateTime::now() - entry_time; + let rt = OffsetDateTime::now_utc() - entry_time; let rt = (rt.whole_nanoseconds() as f64) / 1_000_000.0; fmt.write_fmt(format_args!("{:.6}", rt)) } @@ -520,7 +520,7 @@ mod tests { .uri("/test/route/yeah") .to_srv_request(); - let now = OffsetDateTime::now(); + let now = OffsetDateTime::now_utc(); for unit in &mut format.0 { unit.render_request(now, &req); } @@ -551,7 +551,7 @@ mod tests { ) .to_srv_request(); - let now = OffsetDateTime::now(); + let now = OffsetDateTime::now_utc(); for unit in &mut format.0 { unit.render_request(now, &req); } @@ -561,7 +561,7 @@ mod tests { unit.render_response(&resp); } - let entry_time = OffsetDateTime::now(); + let entry_time = OffsetDateTime::now_utc(); let render = |fmt: &mut Formatter<'_>| { for unit in &format.0 { unit.render(fmt, 1024, entry_time)?; @@ -579,7 +579,7 @@ mod tests { let mut format = Format::new("%t"); let req = TestRequest::default().to_srv_request(); - let now = OffsetDateTime::now(); + let now = OffsetDateTime::now_utc(); for unit in &mut format.0 { unit.render_request(now, &req); } From f37cb6dd0b484708bad5ff75cf7c0f15baa19b70 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 3 May 2020 09:37:40 +0100 Subject: [PATCH 104/157] refactor h1 status line helper to remove unsafe usage (#1484) Co-authored-by: Yuki Okushi --- actix-http/Cargo.toml | 4 + actix-http/benches/status-line.rs | 222 ++++++++++++++++++++++++++++++ actix-http/src/helpers.rs | 101 ++++++-------- 3 files changed, 269 insertions(+), 58 deletions(-) create mode 100644 actix-http/benches/status-line.rs diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index fcb05dd37..b2c6b8e0a 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -101,3 +101,7 @@ rust-tls = { version="0.17", package = "rustls" } [[bench]] name = "content-length" harness = false + +[[bench]] +name = "status-line" +harness = false diff --git a/actix-http/benches/status-line.rs b/actix-http/benches/status-line.rs new file mode 100644 index 000000000..51f840f89 --- /dev/null +++ b/actix-http/benches/status-line.rs @@ -0,0 +1,222 @@ +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; + +use bytes::BytesMut; +use http::Version; + +const CODES: &[u16] = &[201, 303, 404, 515]; + +fn bench_write_status_line_11(c: &mut Criterion) { + let mut group = c.benchmark_group("write_status_line v1.1"); + + let version = Version::HTTP_11; + + for i in CODES.iter() { + group.bench_with_input(BenchmarkId::new("Original (unsafe)", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _original::write_status_line(version, i, &mut b); + }) + }); + + group.bench_with_input(BenchmarkId::new("New (safe)", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _new::write_status_line(version, i, &mut b); + }) + }); + + group.bench_with_input(BenchmarkId::new("Naive", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _naive::write_status_line(version, i, &mut b); + }) + }); + } + + group.finish(); +} + +fn bench_write_status_line_10(c: &mut Criterion) { + let mut group = c.benchmark_group("write_status_line v1.0"); + + let version = Version::HTTP_10; + + for i in CODES.iter() { + group.bench_with_input(BenchmarkId::new("Original (unsafe)", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _original::write_status_line(version, i, &mut b); + }) + }); + + group.bench_with_input(BenchmarkId::new("New (safe)", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _new::write_status_line(version, i, &mut b); + }) + }); + + group.bench_with_input(BenchmarkId::new("Naive", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _naive::write_status_line(version, i, &mut b); + }) + }); + } + + group.finish(); +} + +fn bench_write_status_line_09(c: &mut Criterion) { + let mut group = c.benchmark_group("write_status_line v0.9"); + + let version = Version::HTTP_09; + + for i in CODES.iter() { + group.bench_with_input(BenchmarkId::new("Original (unsafe)", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _original::write_status_line(version, i, &mut b); + }) + }); + + group.bench_with_input(BenchmarkId::new("New (safe)", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _new::write_status_line(version, i, &mut b); + }) + }); + + group.bench_with_input(BenchmarkId::new("Naive", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _naive::write_status_line(version, i, &mut b); + }) + }); + } + + group.finish(); +} + +criterion_group!( + benches, + bench_write_status_line_11, + bench_write_status_line_10, + bench_write_status_line_09 +); +criterion_main!(benches); + +mod _naive { + use bytes::{BufMut, BytesMut}; + use http::Version; + + pub(crate) fn write_status_line(version: Version, n: u16, bytes: &mut BytesMut) { + match version { + Version::HTTP_11 => bytes.put_slice(b"HTTP/1.1 "), + Version::HTTP_10 => bytes.put_slice(b"HTTP/1.0 "), + Version::HTTP_09 => bytes.put_slice(b"HTTP/0.9 "), + _ => { + // other HTTP version handlers do not use this method + } + } + + bytes.put_slice(n.to_string().as_bytes()); + } +} + +mod _new { + use bytes::{BufMut, BytesMut}; + use http::Version; + + const DIGITS_START: u8 = b'0'; + + pub(crate) fn write_status_line(version: Version, n: u16, bytes: &mut BytesMut) { + match version { + Version::HTTP_11 => bytes.put_slice(b"HTTP/1.1 "), + Version::HTTP_10 => bytes.put_slice(b"HTTP/1.0 "), + Version::HTTP_09 => bytes.put_slice(b"HTTP/0.9 "), + _ => { + // other HTTP version handlers do not use this method + } + } + + let d100 = (n / 100) as u8; + let d10 = ((n / 10) % 10) as u8; + let d1 = (n % 10) as u8; + + bytes.put_u8(DIGITS_START + d100); + bytes.put_u8(DIGITS_START + d10); + bytes.put_u8(DIGITS_START + d1); + + bytes.put_u8(b' '); + } +} + +mod _original { + use std::ptr; + + use bytes::{BufMut, BytesMut}; + use http::Version; + + const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\ + 2021222324252627282930313233343536373839\ + 4041424344454647484950515253545556575859\ + 6061626364656667686970717273747576777879\ + 8081828384858687888990919293949596979899"; + + pub(crate) const STATUS_LINE_BUF_SIZE: usize = 13; + + pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesMut) { + let mut buf: [u8; STATUS_LINE_BUF_SIZE] = *b"HTTP/1.1 "; + + match version { + Version::HTTP_2 => buf[5] = b'2', + Version::HTTP_10 => buf[7] = b'0', + Version::HTTP_09 => { + buf[5] = b'0'; + buf[7] = b'9'; + } + _ => (), + } + + let mut curr: isize = 12; + let buf_ptr = buf.as_mut_ptr(); + let lut_ptr = DEC_DIGITS_LUT.as_ptr(); + let four = n > 999; + + // decode 2 more chars, if > 2 chars + let d1 = (n % 100) << 1; + n /= 100; + curr -= 2; + unsafe { + ptr::copy_nonoverlapping( + lut_ptr.offset(d1 as isize), + buf_ptr.offset(curr), + 2, + ); + } + + // decode last 1 or 2 chars + if n < 10 { + curr -= 1; + unsafe { + *buf_ptr.offset(curr) = (n as u8) + b'0'; + } + } else { + let d1 = n << 1; + curr -= 2; + unsafe { + ptr::copy_nonoverlapping( + lut_ptr.offset(d1 as isize), + buf_ptr.offset(curr), + 2, + ); + } + } + + bytes.put_slice(&buf); + if four { + bytes.put_u8(b' '); + } + } +} diff --git a/actix-http/src/helpers.rs b/actix-http/src/helpers.rs index 86f8250b6..ff647e72b 100644 --- a/actix-http/src/helpers.rs +++ b/actix-http/src/helpers.rs @@ -1,69 +1,34 @@ -use std::{io, ptr}; +use std::io; use bytes::{BufMut, BytesMut}; use http::Version; use crate::extensions::Extensions; -const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\ - 2021222324252627282930313233343536373839\ - 4041424344454647484950515253545556575859\ - 6061626364656667686970717273747576777879\ - 8081828384858687888990919293949596979899"; - -pub(crate) const STATUS_LINE_BUF_SIZE: usize = 13; - -pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesMut) { - let mut buf: [u8; STATUS_LINE_BUF_SIZE] = *b"HTTP/1.1 "; - match version { - Version::HTTP_2 => buf[5] = b'2', - Version::HTTP_10 => buf[7] = b'0', - Version::HTTP_09 => { - buf[5] = b'0'; - buf[7] = b'9'; - } - _ => (), - } - - let mut curr: isize = 12; - let buf_ptr = buf.as_mut_ptr(); - let lut_ptr = DEC_DIGITS_LUT.as_ptr(); - let four = n > 999; - - // decode 2 more chars, if > 2 chars - let d1 = (n % 100) << 1; - n /= 100; - curr -= 2; - unsafe { - ptr::copy_nonoverlapping(lut_ptr.offset(d1 as isize), buf_ptr.offset(curr), 2); - } - - // decode last 1 or 2 chars - if n < 10 { - curr -= 1; - unsafe { - *buf_ptr.offset(curr) = (n as u8) + b'0'; - } - } else { - let d1 = n << 1; - curr -= 2; - unsafe { - ptr::copy_nonoverlapping( - lut_ptr.offset(d1 as isize), - buf_ptr.offset(curr), - 2, - ); - } - } - - bytes.put_slice(&buf); - if four { - bytes.put_u8(b' '); - } -} - const DIGITS_START: u8 = b'0'; +pub(crate) fn write_status_line(version: Version, n: u16, bytes: &mut BytesMut) { + match version { + Version::HTTP_11 => bytes.put_slice(b"HTTP/1.1 "), + Version::HTTP_10 => bytes.put_slice(b"HTTP/1.0 "), + Version::HTTP_09 => bytes.put_slice(b"HTTP/0.9 "), + _ => { + // other HTTP version handlers do not use this method + } + } + + let d100 = (n / 100) as u8; + let d10 = ((n / 10) % 10) as u8; + let d1 = (n % 10) as u8; + + bytes.put_u8(DIGITS_START + d100); + bytes.put_u8(DIGITS_START + d10); + bytes.put_u8(DIGITS_START + d1); + + // trailing space before reason + bytes.put_u8(b' '); +} + /// NOTE: bytes object has to contain enough space pub fn write_content_length(n: usize, bytes: &mut BytesMut) { bytes.put_slice(b"\r\ncontent-length: "); @@ -189,8 +154,28 @@ impl DataFactory for Data { #[cfg(test)] mod tests { + use std::str::from_utf8; + use super::*; + #[test] + fn test_status_line() { + let mut bytes = BytesMut::new(); + bytes.reserve(50); + write_status_line(Version::HTTP_11, 200, &mut bytes); + assert_eq!(from_utf8(&bytes.split().freeze()).unwrap(), "HTTP/1.1 200 "); + + let mut bytes = BytesMut::new(); + bytes.reserve(50); + write_status_line(Version::HTTP_09, 404, &mut bytes); + assert_eq!(from_utf8(&bytes.split().freeze()).unwrap(), "HTTP/0.9 404 "); + + let mut bytes = BytesMut::new(); + bytes.reserve(50); + write_status_line(Version::HTTP_09, 515, &mut bytes); + assert_eq!(from_utf8(&bytes.split().freeze()).unwrap(), "HTTP/0.9 515 "); + } + #[test] fn test_write_content_length() { let mut bytes = BytesMut::new(); From b521e9b2218a23b3c2e288611ec123042b287053 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 3 May 2020 14:33:29 +0100 Subject: [PATCH 105/157] conditional test compilation [range, charset] (#1483) * conditionally compile range and charset tests * remove deprecated try macros Co-authored-by: Yuki Okushi --- actix-http/src/extensions.rs | 2 + actix-http/src/header/common/range.rs | 302 +++++++++++------------- actix-http/src/header/shared/charset.rs | 29 ++- src/config.rs | 1 + 4 files changed, 159 insertions(+), 175 deletions(-) diff --git a/actix-http/src/extensions.rs b/actix-http/src/extensions.rs index 6a4a034a4..4e3918537 100644 --- a/actix-http/src/extensions.rs +++ b/actix-http/src/extensions.rs @@ -6,6 +6,8 @@ use fxhash::FxHashMap; #[derive(Default)] /// A type map of request extensions. pub struct Extensions { + /// Use FxHasher with a std HashMap with for faster + /// lookups on the small `TypeId` (u64 equivalent) keys. map: FxHashMap>, } diff --git a/actix-http/src/header/common/range.rs b/actix-http/src/header/common/range.rs index fc1bc8159..f9e203bb2 100644 --- a/actix-http/src/header/common/range.rs +++ b/actix-http/src/header/common/range.rs @@ -183,13 +183,13 @@ impl fmt::Display for Range { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Range::Bytes(ref ranges) => { - try!(write!(f, "bytes=")); + write!(f, "bytes=")?; for (i, range) in ranges.iter().enumerate() { if i != 0 { - try!(f.write_str(",")); + f.write_str(",")?; } - try!(Display::fmt(range, f)); + Display::fmt(range, f)?; } Ok(()) } @@ -214,9 +214,9 @@ impl FromStr for Range { } Ok(Range::Bytes(ranges)) } - (Some(unit), Some(range_str)) if unit != "" && range_str != "" => Ok( - Range::Unregistered(unit.to_owned(), range_str.to_owned()), - ), + (Some(unit), Some(range_str)) if unit != "" && range_str != "" => { + Ok(Range::Unregistered(unit.to_owned(), range_str.to_owned())) + } _ => Err(::Error::Header), } } @@ -229,7 +229,8 @@ impl FromStr for ByteRangeSpec { let mut parts = s.splitn(2, '-'); match (parts.next(), parts.next()) { - (Some(""), Some(end)) => end.parse() + (Some(""), Some(end)) => end + .parse() .or(Err(::Error::Header)) .map(ByteRangeSpec::Last), (Some(start), Some("")) => start @@ -272,163 +273,138 @@ impl Header for Range { } } -#[test] -fn test_parse_bytes_range_valid() { - let r: Range = Header::parse_header(&"bytes=1-100".into()).unwrap(); - let r2: Range = Header::parse_header(&"bytes=1-100,-".into()).unwrap(); - let r3 = Range::bytes(1, 100); - assert_eq!(r, r2); - assert_eq!(r2, r3); +#[cfg(test)] +mod tests { + use super::*; - let r: Range = Header::parse_header(&"bytes=1-100,200-".into()).unwrap(); - let r2: Range = - Header::parse_header(&"bytes= 1-100 , 101-xxx, 200- ".into()).unwrap(); - let r3 = Range::Bytes(vec![ - ByteRangeSpec::FromTo(1, 100), - ByteRangeSpec::AllFrom(200), - ]); - assert_eq!(r, r2); - assert_eq!(r2, r3); + #[test] + fn test_parse_bytes_range_valid() { + let r: Range = Header::parse_header(&"bytes=1-100".into()).unwrap(); + let r2: Range = Header::parse_header(&"bytes=1-100,-".into()).unwrap(); + let r3 = Range::bytes(1, 100); + assert_eq!(r, r2); + assert_eq!(r2, r3); - let r: Range = Header::parse_header(&"bytes=1-100,-100".into()).unwrap(); - let r2: Range = Header::parse_header(&"bytes=1-100, ,,-100".into()).unwrap(); - let r3 = Range::Bytes(vec![ - ByteRangeSpec::FromTo(1, 100), - ByteRangeSpec::Last(100), - ]); - assert_eq!(r, r2); - assert_eq!(r2, r3); + let r: Range = Header::parse_header(&"bytes=1-100,200-".into()).unwrap(); + let r2: Range = + Header::parse_header(&"bytes= 1-100 , 101-xxx, 200- ".into()).unwrap(); + let r3 = Range::Bytes(vec![ + ByteRangeSpec::FromTo(1, 100), + ByteRangeSpec::AllFrom(200), + ]); + assert_eq!(r, r2); + assert_eq!(r2, r3); - let r: Range = Header::parse_header(&"custom=1-100,-100".into()).unwrap(); - let r2 = Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned()); - assert_eq!(r, r2); -} - -#[test] -fn test_parse_unregistered_range_valid() { - let r: Range = Header::parse_header(&"custom=1-100,-100".into()).unwrap(); - let r2 = Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned()); - assert_eq!(r, r2); - - let r: Range = Header::parse_header(&"custom=abcd".into()).unwrap(); - let r2 = Range::Unregistered("custom".to_owned(), "abcd".to_owned()); - assert_eq!(r, r2); - - let r: Range = Header::parse_header(&"custom=xxx-yyy".into()).unwrap(); - let r2 = Range::Unregistered("custom".to_owned(), "xxx-yyy".to_owned()); - assert_eq!(r, r2); -} - -#[test] -fn test_parse_invalid() { - let r: ::Result = Header::parse_header(&"bytes=1-a,-".into()); - assert_eq!(r.ok(), None); - - let r: ::Result = Header::parse_header(&"bytes=1-2-3".into()); - assert_eq!(r.ok(), None); - - let r: ::Result = Header::parse_header(&"abc".into()); - assert_eq!(r.ok(), None); - - let r: ::Result = Header::parse_header(&"bytes=1-100=".into()); - assert_eq!(r.ok(), None); - - let r: ::Result = Header::parse_header(&"bytes=".into()); - assert_eq!(r.ok(), None); - - let r: ::Result = Header::parse_header(&"custom=".into()); - assert_eq!(r.ok(), None); - - let r: ::Result = Header::parse_header(&"=1-100".into()); - assert_eq!(r.ok(), None); -} - -#[test] -fn test_fmt() { - use header::Headers; - - let mut headers = Headers::new(); - - headers.set(Range::Bytes(vec![ - ByteRangeSpec::FromTo(0, 1000), - ByteRangeSpec::AllFrom(2000), - ])); - assert_eq!(&headers.to_string(), "Range: bytes=0-1000,2000-\r\n"); - - headers.clear(); - headers.set(Range::Bytes(vec![])); - - assert_eq!(&headers.to_string(), "Range: bytes=\r\n"); - - headers.clear(); - headers.set(Range::Unregistered( - "custom".to_owned(), - "1-xxx".to_owned(), - )); - - assert_eq!(&headers.to_string(), "Range: custom=1-xxx\r\n"); -} - -#[test] -fn test_byte_range_spec_to_satisfiable_range() { - assert_eq!( - Some((0, 0)), - ByteRangeSpec::FromTo(0, 0).to_satisfiable_range(3) - ); - assert_eq!( - Some((1, 2)), - ByteRangeSpec::FromTo(1, 2).to_satisfiable_range(3) - ); - assert_eq!( - Some((1, 2)), - ByteRangeSpec::FromTo(1, 5).to_satisfiable_range(3) - ); - assert_eq!( - None, - ByteRangeSpec::FromTo(3, 3).to_satisfiable_range(3) - ); - assert_eq!( - None, - ByteRangeSpec::FromTo(2, 1).to_satisfiable_range(3) - ); - assert_eq!( - None, - ByteRangeSpec::FromTo(0, 0).to_satisfiable_range(0) - ); - - assert_eq!( - Some((0, 2)), - ByteRangeSpec::AllFrom(0).to_satisfiable_range(3) - ); - assert_eq!( - Some((2, 2)), - ByteRangeSpec::AllFrom(2).to_satisfiable_range(3) - ); - assert_eq!( - None, - ByteRangeSpec::AllFrom(3).to_satisfiable_range(3) - ); - assert_eq!( - None, - ByteRangeSpec::AllFrom(5).to_satisfiable_range(3) - ); - assert_eq!( - None, - ByteRangeSpec::AllFrom(0).to_satisfiable_range(0) - ); - - assert_eq!( - Some((1, 2)), - ByteRangeSpec::Last(2).to_satisfiable_range(3) - ); - assert_eq!( - Some((2, 2)), - ByteRangeSpec::Last(1).to_satisfiable_range(3) - ); - assert_eq!( - Some((0, 2)), - ByteRangeSpec::Last(5).to_satisfiable_range(3) - ); - assert_eq!(None, ByteRangeSpec::Last(0).to_satisfiable_range(3)); - assert_eq!(None, ByteRangeSpec::Last(2).to_satisfiable_range(0)); + let r: Range = Header::parse_header(&"bytes=1-100,-100".into()).unwrap(); + let r2: Range = Header::parse_header(&"bytes=1-100, ,,-100".into()).unwrap(); + let r3 = Range::Bytes(vec![ + ByteRangeSpec::FromTo(1, 100), + ByteRangeSpec::Last(100), + ]); + assert_eq!(r, r2); + assert_eq!(r2, r3); + + let r: Range = Header::parse_header(&"custom=1-100,-100".into()).unwrap(); + let r2 = Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned()); + assert_eq!(r, r2); + } + + #[test] + fn test_parse_unregistered_range_valid() { + let r: Range = Header::parse_header(&"custom=1-100,-100".into()).unwrap(); + let r2 = Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned()); + assert_eq!(r, r2); + + let r: Range = Header::parse_header(&"custom=abcd".into()).unwrap(); + let r2 = Range::Unregistered("custom".to_owned(), "abcd".to_owned()); + assert_eq!(r, r2); + + let r: Range = Header::parse_header(&"custom=xxx-yyy".into()).unwrap(); + let r2 = Range::Unregistered("custom".to_owned(), "xxx-yyy".to_owned()); + assert_eq!(r, r2); + } + + #[test] + fn test_parse_invalid() { + let r: ::Result = Header::parse_header(&"bytes=1-a,-".into()); + assert_eq!(r.ok(), None); + + let r: ::Result = Header::parse_header(&"bytes=1-2-3".into()); + assert_eq!(r.ok(), None); + + let r: ::Result = Header::parse_header(&"abc".into()); + assert_eq!(r.ok(), None); + + let r: ::Result = Header::parse_header(&"bytes=1-100=".into()); + assert_eq!(r.ok(), None); + + let r: ::Result = Header::parse_header(&"bytes=".into()); + assert_eq!(r.ok(), None); + + let r: ::Result = Header::parse_header(&"custom=".into()); + assert_eq!(r.ok(), None); + + let r: ::Result = Header::parse_header(&"=1-100".into()); + assert_eq!(r.ok(), None); + } + + #[test] + fn test_fmt() { + use header::Headers; + + let mut headers = Headers::new(); + + headers.set(Range::Bytes(vec![ + ByteRangeSpec::FromTo(0, 1000), + ByteRangeSpec::AllFrom(2000), + ])); + assert_eq!(&headers.to_string(), "Range: bytes=0-1000,2000-\r\n"); + + headers.clear(); + headers.set(Range::Bytes(vec![])); + + assert_eq!(&headers.to_string(), "Range: bytes=\r\n"); + + headers.clear(); + headers.set(Range::Unregistered("custom".to_owned(), "1-xxx".to_owned())); + + assert_eq!(&headers.to_string(), "Range: custom=1-xxx\r\n"); + } + + #[test] + fn test_byte_range_spec_to_satisfiable_range() { + assert_eq!( + Some((0, 0)), + ByteRangeSpec::FromTo(0, 0).to_satisfiable_range(3) + ); + assert_eq!( + Some((1, 2)), + ByteRangeSpec::FromTo(1, 2).to_satisfiable_range(3) + ); + assert_eq!( + Some((1, 2)), + ByteRangeSpec::FromTo(1, 5).to_satisfiable_range(3) + ); + assert_eq!(None, ByteRangeSpec::FromTo(3, 3).to_satisfiable_range(3)); + assert_eq!(None, ByteRangeSpec::FromTo(2, 1).to_satisfiable_range(3)); + assert_eq!(None, ByteRangeSpec::FromTo(0, 0).to_satisfiable_range(0)); + + assert_eq!( + Some((0, 2)), + ByteRangeSpec::AllFrom(0).to_satisfiable_range(3) + ); + assert_eq!( + Some((2, 2)), + ByteRangeSpec::AllFrom(2).to_satisfiable_range(3) + ); + assert_eq!(None, ByteRangeSpec::AllFrom(3).to_satisfiable_range(3)); + assert_eq!(None, ByteRangeSpec::AllFrom(5).to_satisfiable_range(3)); + assert_eq!(None, ByteRangeSpec::AllFrom(0).to_satisfiable_range(0)); + + assert_eq!(Some((1, 2)), ByteRangeSpec::Last(2).to_satisfiable_range(3)); + assert_eq!(Some((2, 2)), ByteRangeSpec::Last(1).to_satisfiable_range(3)); + assert_eq!(Some((0, 2)), ByteRangeSpec::Last(5).to_satisfiable_range(3)); + assert_eq!(None, ByteRangeSpec::Last(0).to_satisfiable_range(3)); + assert_eq!(None, ByteRangeSpec::Last(2).to_satisfiable_range(0)); + } } diff --git a/actix-http/src/header/shared/charset.rs b/actix-http/src/header/shared/charset.rs index 6ddfa03ea..00e7309d4 100644 --- a/actix-http/src/header/shared/charset.rs +++ b/actix-http/src/header/shared/charset.rs @@ -137,17 +137,22 @@ impl FromStr for Charset { } } -#[test] -fn test_parse() { - assert_eq!(Us_Ascii, "us-ascii".parse().unwrap()); - assert_eq!(Us_Ascii, "US-Ascii".parse().unwrap()); - assert_eq!(Us_Ascii, "US-ASCII".parse().unwrap()); - assert_eq!(Shift_Jis, "Shift-JIS".parse().unwrap()); - assert_eq!(Ext("ABCD".to_owned()), "abcd".parse().unwrap()); -} +#[cfg(test)] +mod tests { + use super::*; -#[test] -fn test_display() { - assert_eq!("US-ASCII", format!("{}", Us_Ascii)); - assert_eq!("ABCD", format!("{}", Ext("ABCD".to_owned()))); + #[test] + fn test_parse() { + assert_eq!(Us_Ascii, "us-ascii".parse().unwrap()); + assert_eq!(Us_Ascii, "US-Ascii".parse().unwrap()); + assert_eq!(Us_Ascii, "US-ASCII".parse().unwrap()); + assert_eq!(Shift_Jis, "Shift-JIS".parse().unwrap()); + assert_eq!(Ext("ABCD".to_owned()), "abcd".parse().unwrap()); + } + + #[test] + fn test_display() { + assert_eq!("US-ASCII", format!("{}", Us_Ascii)); + assert_eq!("ABCD", format!("{}", Ext("ABCD".to_owned()))); + } } diff --git a/src/config.rs b/src/config.rs index 6db378c7b..19a5ccc7b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -123,6 +123,7 @@ impl AppService { } } +/// Application connection config #[derive(Clone)] pub struct AppConfig(Rc); From 9164ed1f0c2916f4820fd89088ba12faa9598658 Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Thu, 7 May 2020 11:31:12 +0200 Subject: [PATCH 106/157] add resource middleware on actix-web-codegen (#1467) Co-authored-by: Yuki Okushi --- actix-web-codegen/src/lib.rs | 2 + actix-web-codegen/src/route.rs | 17 +++++- actix-web-codegen/tests/test_macro.rs | 78 ++++++++++++++++++++++++++- 3 files changed, 94 insertions(+), 3 deletions(-) diff --git a/actix-web-codegen/src/lib.rs b/actix-web-codegen/src/lib.rs index b724eb797..39a8a6464 100644 --- a/actix-web-codegen/src/lib.rs +++ b/actix-web-codegen/src/lib.rs @@ -21,6 +21,7 @@ //! //! - `"path"` - Raw literal string with path for which to register handle. Mandatory. //! - `guard="function_name"` - Registers function as guard using `actix_web::guard::fn_guard` +//! - `wrap="Middleware"` - Registers a resource middleware. //! //! ## Notes //! @@ -54,6 +55,7 @@ use proc_macro::TokenStream; /// /// - `"path"` - Raw literal string with path for which to register handler. Mandatory. /// - `guard="function_name"` - Registers function as guard using `actix_web::guard::fn_guard` +/// - `wrap="Middleware"` - Registers a resource middleware. #[proc_macro_attribute] pub fn get(args: TokenStream, input: TokenStream) -> TokenStream { route::generate(args, input, route::GuardType::Get) diff --git a/actix-web-codegen/src/route.rs b/actix-web-codegen/src/route.rs index 3e6f9c979..7e3d43f1d 100644 --- a/actix-web-codegen/src/route.rs +++ b/actix-web-codegen/src/route.rs @@ -56,12 +56,14 @@ impl ToTokens for GuardType { struct Args { path: syn::LitStr, guards: Vec, + wrappers: Vec, } impl Args { fn new(args: AttributeArgs) -> syn::Result { let mut path = None; let mut guards = Vec::new(); + let mut wrappers = Vec::new(); for arg in args { match arg { NestedMeta::Lit(syn::Lit::Str(lit)) => match path { @@ -85,10 +87,19 @@ impl Args { "Attribute guard expects literal string!", )); } + } else if nv.path.is_ident("wrap") { + if let syn::Lit::Str(lit) = nv.lit { + wrappers.push(lit.parse()?); + } else { + return Err(syn::Error::new_spanned( + nv.lit, + "Attribute wrap expects type", + )); + } } else { return Err(syn::Error::new_spanned( nv.path, - "Unknown attribute key is specified. Allowed: guard.", + "Unknown attribute key is specified. Allowed: guard and wrap", )); } } @@ -100,6 +111,7 @@ impl Args { Ok(Args { path: path.unwrap(), guards, + wrappers, }) } } @@ -184,7 +196,7 @@ impl ToTokens for Route { name, guard, ast, - args: Args { path, guards }, + args: Args { path, guards, wrappers }, resource_type, } = self; let resource_name = name.to_string(); @@ -199,6 +211,7 @@ impl ToTokens for Route { .name(#resource_name) .guard(actix_web::guard::#guard()) #(.guard(actix_web::guard::fn_guard(#guards)))* + #(.wrap(#wrappers))* .#resource_type(#name); actix_web::dev::HttpServiceFactory::register(__resource, __config) diff --git a/actix-web-codegen/tests/test_macro.rs b/actix-web-codegen/tests/test_macro.rs index ffb50c11e..8264a7fd7 100644 --- a/actix-web-codegen/tests/test_macro.rs +++ b/actix-web-codegen/tests/test_macro.rs @@ -1,6 +1,11 @@ -use actix_web::{http, test, web::Path, App, HttpResponse, Responder}; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use actix_web::{http, test, web::Path, App, HttpResponse, Responder, Error}; +use actix_web::dev::{Service, Transform, ServiceRequest, ServiceResponse}; use actix_web_codegen::{connect, delete, get, head, options, patch, post, put, trace}; use futures::{future, Future}; +use actix_web::http::header::{HeaderName, HeaderValue}; // Make sure that we can name function as 'config' #[get("/config")] @@ -73,6 +78,65 @@ async fn get_param_test(_: Path) -> impl Responder { HttpResponse::Ok() } +pub struct ChangeStatusCode; + +impl Transform for ChangeStatusCode +where + S: Service, Error = Error>, + S::Future: 'static, + B: 'static, +{ + type Request = ServiceRequest; + type Response = ServiceResponse; + type Error = Error; + type InitError = (); + type Transform = ChangeStatusCodeMiddleware; + type Future = future::Ready>; + + fn new_transform(&self, service: S) -> Self::Future { + future::ok(ChangeStatusCodeMiddleware { service }) + } +} + +pub struct ChangeStatusCodeMiddleware { + service: S, +} + +impl Service for ChangeStatusCodeMiddleware +where + S: Service, Error = Error>, + S::Future: 'static, + B: 'static, +{ + type Request = ServiceRequest; + type Response = ServiceResponse; + type Error = Error; + type Future = Pin>>>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.service.poll_ready(cx) + } + + fn call(&mut self, req: ServiceRequest) -> Self::Future { + + let fut = self.service.call(req); + + Box::pin(async move { + let mut res = fut.await?; + let headers = res.headers_mut(); + let header_name = HeaderName::from_lowercase(b"custom-header").unwrap(); + let header_value = HeaderValue::from_str("hello").unwrap(); + headers.insert(header_name, header_value); + Ok(res) + }) + } +} + +#[get("/test/wrap", wrap = "ChangeStatusCode")] +async fn get_wrap(_: Path) -> impl Responder { + HttpResponse::Ok() +} + #[actix_rt::test] async fn test_params() { let srv = test::start(|| { @@ -155,3 +219,15 @@ async fn test_auto_async() { let response = request.send().await.unwrap(); assert!(response.status().is_success()); } + +#[actix_rt::test] +async fn test_wrap() { + let srv = test::start(|| { + App::new() + .service(get_wrap) + }); + + let request = srv.request(http::Method::GET, srv.url("/test/wrap")); + let response = request.send().await.unwrap(); + assert!(response.headers().contains_key("custom-header")); +} From 9d94fb91b26b5de44083af89545dfaede7e0b2ed Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 7 May 2020 18:26:48 +0100 Subject: [PATCH 107/157] correct spelling of ConnectError::Unresolved (#1487) Co-authored-by: Yuki Okushi --- actix-http/src/client/error.rs | 4 ++-- actix-http/src/client/pool.rs | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/actix-http/src/client/error.rs b/actix-http/src/client/error.rs index 0f0a86cd1..e4653a31a 100644 --- a/actix-http/src/client/error.rs +++ b/actix-http/src/client/error.rs @@ -48,7 +48,7 @@ pub enum ConnectError { /// Unresolved host name #[display(fmt = "Connector received `Connect` method with unresolved host")] - Unresolverd, + Unresolved, /// Connection io error #[display(fmt = "{}", _0)] @@ -63,7 +63,7 @@ impl From for ConnectError { actix_connect::ConnectError::Resolver(e) => ConnectError::Resolver(e), actix_connect::ConnectError::NoRecords => ConnectError::NoRecords, actix_connect::ConnectError::InvalidInput => panic!(), - actix_connect::ConnectError::Unresolverd => ConnectError::Unresolverd, + actix_connect::ConnectError::Unresolved => ConnectError::Unresolved, actix_connect::ConnectError::Io(e) => ConnectError::Io(e), } } diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index 983396f92..5a10725b0 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -105,7 +105,7 @@ where let key = if let Some(authority) = req.uri.authority() { authority.clone().into() } else { - return Err(ConnectError::Unresolverd); + return Err(ConnectError::Unresolved); }; // acquire connection @@ -195,7 +195,7 @@ where if let Some(i) = self.inner.take() { let mut inner = i.as_ref().borrow_mut(); inner.release_waiter(&self.key, self.token); - inner.check_availibility(); + inner.check_availability(); } } } @@ -232,7 +232,7 @@ where if let Some(i) = self.inner.take() { let mut inner = i.as_ref().borrow_mut(); inner.release(); - inner.check_availibility(); + inner.check_availability(); } } } @@ -359,7 +359,7 @@ where created, used: Instant::now(), }); - self.check_availibility(); + self.check_availability(); } fn release_close(&mut self, io: ConnectionType) { @@ -369,10 +369,10 @@ where actix_rt::spawn(CloseConnection::new(io, timeout)) } } - self.check_availibility(); + self.check_availability(); } - fn check_availibility(&self) { + fn check_availability(&self) { if !self.waiters_queue.is_empty() && self.acquired < self.config.limit { self.waker.wake(); } @@ -534,7 +534,7 @@ where if let Some(inner) = self.project().inner.take() { let mut inner = inner.as_ref().borrow_mut(); inner.release(); - inner.check_availibility(); + inner.check_availability(); } } } From 0bc4a5e7032b798114fa10dc4caf261495dcff22 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 8 May 2020 02:33:33 +0900 Subject: [PATCH 108/157] http: Bump up to 2.0.0-alpha.3 --- actix-http/CHANGES.md | 11 ++++++++--- actix-http/Cargo.toml | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 43f189afc..92302a666 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,16 +1,21 @@ # Changes -## [Unreleased] +## [2.0.0-alpha.3] - 2020-05-08 + +### Fixed + +* Correct spelling of ConnectError::Unresolved [#1487] +* Fix a mistake in the encoding of websocket continuation messages wherein + Item::FirstText and Item::FirstBinary are each encoded as the other. ### Changed * Implement `std::error::Error` for our custom errors [#1422] * Remove `failure` support for `ResponseError` since that crate will be deprecated in the near future. -* Fix a mistake in the encoding of websocket continuation messages wherein - Item::FirstText and Item::FirstBinary are each encoded as the other. [#1422]: https://github.com/actix/actix-web/pull/1422 +[#1487]: https://github.com/actix/actix-web/pull/1487 ## [2.0.0-alpha.2] - 2020-03-07 diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index b2c6b8e0a..7e398f6fa 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "2.0.0-alpha.2" +version = "2.0.0-alpha.3" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" @@ -42,7 +42,7 @@ actors = ["actix"] [dependencies] actix-service = "1.0.5" actix-codec = "0.2.0" -actix-connect = "2.0.0-alpha.2" +actix-connect = "2.0.0-alpha.3" actix-utils = "1.0.6" actix-rt = "1.0.0" actix-threadpool = "0.3.1" From 9d0c80b6ce13d92d8b3ec35a9d61c580134f6a77 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 8 May 2020 02:33:55 +0900 Subject: [PATCH 109/157] Update actix-http deps --- Cargo.toml | 2 +- actix-files/Cargo.toml | 2 +- actix-framed/Cargo.toml | 2 +- actix-multipart/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- awc/Cargo.toml | 4 ++-- test-server/Cargo.toml | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7a0bef858..da836d178 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,7 +81,7 @@ actix-threadpool = "0.3.1" actix-tls = "2.0.0-alpha.1" actix-web-codegen = "0.2.0" -actix-http = "2.0.0-alpha.2" +actix-http = "2.0.0-alpha.3" awc = { version = "2.0.0-alpha.1", default-features = false } bytes = "0.5.3" diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index ed887e323..4ddc89f3d 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -19,7 +19,7 @@ path = "src/lib.rs" [dependencies] actix-web = { version = "3.0.0-alpha.1", default-features = false } -actix-http = "2.0.0-alpha.2" +actix-http = "2.0.0-alpha.3" actix-service = "1.0.1" bitflags = "1" bytes = "0.5.3" diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index adcda1422..94009db6f 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -23,7 +23,7 @@ actix-codec = "0.2.0" actix-service = "1.0.1" actix-router = "0.2.1" actix-rt = "1.0.0" -actix-http = "2.0.0-alpha.2" +actix-http = "2.0.0-alpha.3" bytes = "0.5.3" futures = "0.3.1" diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index e81b07cb7..6e5696e58 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -29,4 +29,4 @@ twoway = "0.2" [dev-dependencies] actix-rt = "1.0.0" -actix-http = "2.0.0-alpha.2" +actix-http = "2.0.0-alpha.3" diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 7e24d8c99..ccb41abb2 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -18,7 +18,7 @@ path = "src/lib.rs" [dependencies] actix = "0.10.0-alpha.1" actix-web = "3.0.0-alpha.1" -actix-http = "2.0.0-alpha.2" +actix-http = "2.0.0-alpha.3" actix-codec = "0.2.0" bytes = "0.5.2" futures = "0.3.1" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index fde136eb0..782f6f00a 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -36,7 +36,7 @@ compress = ["actix-http/compress"] [dependencies] actix-codec = "0.2.0" actix-service = "1.0.1" -actix-http = "2.0.0-alpha.2" +actix-http = "2.0.0-alpha.3" actix-rt = "1.0.0" base64 = "0.11" @@ -56,7 +56,7 @@ rust-tls = { version = "0.17.0", package="rustls", optional = true, features = [ [dev-dependencies] actix-connect = { version = "2.0.0-alpha.2", features=["openssl"] } actix-web = { version = "3.0.0-alpha.1", features=["openssl"] } -actix-http = { version = "2.0.0-alpha.2", features=["openssl"] } +actix-http = { version = "2.0.0-alpha.3", features=["openssl"] } actix-http-test = { version = "1.0.0", features=["openssl"] } actix-utils = "1.0.3" actix-server = "1.0.0" diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 97581585d..60b4127a4 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -56,4 +56,4 @@ open-ssl = { version="0.10", package = "openssl", optional = true } [dev-dependencies] actix-web = "3.0.0-alpha.1" -actix-http = "2.0.0-alpha.2" +actix-http = "2.0.0-alpha.3" From 54abf356d47c7a01fde87acfb0328d730229434f Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 8 May 2020 03:33:29 +0900 Subject: [PATCH 110/157] actors: Bump up to 3.0.0-alpha.1 --- actix-web-actors/CHANGES.md | 6 ++++++ actix-web-actors/Cargo.toml | 6 +++--- actix-web-actors/src/context.rs | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/actix-web-actors/CHANGES.md b/actix-web-actors/CHANGES.md index 66ff7ed6c..68947569a 100644 --- a/actix-web-actors/CHANGES.md +++ b/actix-web-actors/CHANGES.md @@ -1,5 +1,11 @@ # Changes +# [3.0.0-alpha.1] - 2020-05-08 + +* Update the actix-web dependency to 3.0.0-alpha.1 +* Update the actix dependency to 0.10.0-alpha.2 +* Update the actix-http dependency to 2.0.0-alpha.3 + ## [2.0.0] - 2019-12-20 * Release diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index ccb41abb2..42c602102 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web-actors" -version = "2.0.0" +version = "3.0.0-alpha.1" authors = ["Nikolay Kim "] description = "Actix actors support for actix web framework." readme = "README.md" @@ -16,7 +16,7 @@ name = "actix_web_actors" path = "src/lib.rs" [dependencies] -actix = "0.10.0-alpha.1" +actix = "0.10.0-alpha.2" actix-web = "3.0.0-alpha.1" actix-http = "2.0.0-alpha.3" actix-codec = "0.2.0" @@ -26,4 +26,4 @@ pin-project = "0.4.6" [dev-dependencies] actix-rt = "1.0.0" -env_logger = "0.6" +env_logger = "0.7" diff --git a/actix-web-actors/src/context.rs b/actix-web-actors/src/context.rs index 6a403de12..c889092d2 100644 --- a/actix-web-actors/src/context.rs +++ b/actix-web-actors/src/context.rs @@ -174,7 +174,7 @@ where // frames if let Some(data) = self.fut.ctx().stream.pop_front() { - Poll::Ready(data.map(|b| Ok(b))) + Poll::Ready(data.map(Ok)) } else if self.fut.alive() { Poll::Pending } else { From b6b3481c6f9bed70c8150b439aa3626bff33eed6 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 8 May 2020 06:46:13 +0900 Subject: [PATCH 111/157] web: Bump up to 3.0.0-alpha.2 --- CHANGES.md | 3 ++- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index db2c8e8f5..2ed9c20b7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,12 +1,13 @@ # Changes -## [Unreleased] +## [3.0.0-alpha.2] - 2020-05-08 ### Changed * `{Resource,Scope}::default_service(f)` handlers now support app data extraction. [#1452] * Implement `std::error::Error` for our custom errors [#1422] * NormalizePath middleware now appends trailing / so that routes of form /example/ respond to /example requests. +* Remove the `failure` feature and support. [#1422]: https://github.com/actix/actix-web/pull/1422 [#1452]: https://github.com/actix/actix-web/pull/1452 diff --git a/Cargo.toml b/Cargo.toml index da836d178..823893962 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "3.0.0-alpha.1" +version = "3.0.0-alpha.2" authors = ["Nikolay Kim "] description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." readme = "README.md" From b66c3083a54234bababcf18708498b266d9be50b Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 8 May 2020 06:46:42 +0900 Subject: [PATCH 112/157] Update the `actix-web` dependency to 3.0.0-alpha.2 --- actix-files/Cargo.toml | 4 ++-- actix-multipart/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- actix-web-codegen/Cargo.toml | 2 +- awc/Cargo.toml | 14 +++++++------- test-server/Cargo.toml | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 4ddc89f3d..60f92c5d9 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -18,7 +18,7 @@ name = "actix_files" path = "src/lib.rs" [dependencies] -actix-web = { version = "3.0.0-alpha.1", default-features = false } +actix-web = { version = "3.0.0-alpha.2", default-features = false } actix-http = "2.0.0-alpha.3" actix-service = "1.0.1" bitflags = "1" @@ -33,4 +33,4 @@ v_htmlescape = "0.4" [dev-dependencies] actix-rt = "1.0.0" -actix-web = { version = "3.0.0-alpha.1", features=["openssl"] } +actix-web = { version = "3.0.0-alpha.2", features = ["openssl"] } diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 6e5696e58..d8421d0eb 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -16,7 +16,7 @@ name = "actix_multipart" path = "src/lib.rs" [dependencies] -actix-web = { version = "3.0.0-alpha.1", default-features = false } +actix-web = { version = "3.0.0-alpha.2", default-features = false } actix-service = "1.0.1" actix-utils = "1.0.3" bytes = "0.5.3" diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 42c602102..f9bcc65de 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -17,7 +17,7 @@ path = "src/lib.rs" [dependencies] actix = "0.10.0-alpha.2" -actix-web = "3.0.0-alpha.1" +actix-web = "3.0.0-alpha.2" actix-http = "2.0.0-alpha.3" actix-codec = "0.2.0" bytes = "0.5.2" diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 42befc0dd..835c9fcc9 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -18,5 +18,5 @@ proc-macro2 = "^1" [dev-dependencies] actix-rt = "1.0.0" -actix-web = "3.0.0-alpha.1" +actix-web = "3.0.0-alpha.2" futures = "0.3.1" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 782f6f00a..5ccf31654 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -50,17 +50,17 @@ rand = "0.7" serde = "1.0" serde_json = "1.0" serde_urlencoded = "0.6.1" -open-ssl = { version="0.10", package="openssl", optional = true } -rust-tls = { version = "0.17.0", package="rustls", optional = true, features = ["dangerous_configuration"] } +open-ssl = { version = "0.10", package = "openssl", optional = true } +rust-tls = { version = "0.17.0", package = "rustls", optional = true, features = ["dangerous_configuration"] } [dev-dependencies] -actix-connect = { version = "2.0.0-alpha.2", features=["openssl"] } -actix-web = { version = "3.0.0-alpha.1", features=["openssl"] } -actix-http = { version = "2.0.0-alpha.3", features=["openssl"] } -actix-http-test = { version = "1.0.0", features=["openssl"] } +actix-connect = { version = "2.0.0-alpha.2", features = ["openssl"] } +actix-web = { version = "3.0.0-alpha.2", features = ["openssl"] } +actix-http = { version = "2.0.0-alpha.3", features = ["openssl"] } +actix-http-test = { version = "1.0.0", features = ["openssl"] } actix-utils = "1.0.3" actix-server = "1.0.0" -actix-tls = { version = "2.0.0-alpha.1", features=["openssl", "rustls"] } +actix-tls = { version = "2.0.0-alpha.1", features = ["openssl", "rustls"] } brotli2 = "0.3.2" flate2 = "1.0.13" futures = "0.3.1" diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 60b4127a4..f44ecc5fd 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -52,8 +52,8 @@ sha1 = "0.6" slab = "0.4" serde_urlencoded = "0.6.1" time = { version = "0.2.7", default-features = false, features = ["std"] } -open-ssl = { version="0.10", package = "openssl", optional = true } +open-ssl = { version = "0.10", package = "openssl", optional = true } [dev-dependencies] -actix-web = "3.0.0-alpha.1" +actix-web = "3.0.0-alpha.2" actix-http = "2.0.0-alpha.3" From 879cad94226a398523f8c258e3699eb864ba006a Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 9 May 2020 00:31:26 +0100 Subject: [PATCH 113/157] allow parent data containers to be accessed from child scopes --- CHANGES.md | 10 +++++- Cargo.toml | 1 + src/app_service.rs | 2 +- src/request.rs | 85 +++++++++++++++++++++++++++++++++++++++++---- src/resource.rs | 86 +++------------------------------------------- src/scope.rs | 8 ++--- src/service.rs | 20 ++++++----- 7 files changed, 109 insertions(+), 103 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 2ed9c20b7..97b7ca63a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,16 +1,24 @@ # Changes +## [Unreleased] + +### Changed + +* Resources and Scopes can now access non-overridden data types set on App (or containing scopes) when setting their own data. [#1486] + ## [3.0.0-alpha.2] - 2020-05-08 ### Changed * `{Resource,Scope}::default_service(f)` handlers now support app data extraction. [#1452] * Implement `std::error::Error` for our custom errors [#1422] -* NormalizePath middleware now appends trailing / so that routes of form /example/ respond to /example requests. +* NormalizePath middleware now appends trailing / so that routes of form /example/ respond to /example requests. [#1433] * Remove the `failure` feature and support. [#1422]: https://github.com/actix/actix-web/pull/1422 +[#1433]: https://github.com/actix/actix-web/pull/1433 [#1452]: https://github.com/actix/actix-web/pull/1452 +[#1486]: https://github.com/actix/actix-web/pull/1486 ## [3.0.0-alpha.1] - 2020-03-11 diff --git a/Cargo.toml b/Cargo.toml index 823893962..b24cc89d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -101,6 +101,7 @@ time = { version = "0.2.7", default-features = false, features = ["std"] } url = "2.1" open-ssl = { version="0.10", package = "openssl", optional = true } rust-tls = { version = "0.17.0", package = "rustls", optional = true } +tinyvec = { version = "0.3", features = ["alloc"] } [dev-dependencies] actix = "0.10.0-alpha.1" diff --git a/src/app_service.rs b/src/app_service.rs index 808592e58..2d64bed3e 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -245,7 +245,7 @@ where inner.path.reset(); inner.head = head; inner.payload = payload; - inner.app_data = self.data.clone(); + inner.app_data.push(self.data.clone()); req } else { HttpRequest::new( diff --git a/src/request.rs b/src/request.rs index 51a1c54ff..72fea1faf 100644 --- a/src/request.rs +++ b/src/request.rs @@ -6,6 +6,7 @@ use actix_http::http::{HeaderMap, Method, Uri, Version}; use actix_http::{Error, Extensions, HttpMessage, Message, Payload, RequestHead}; use actix_router::{Path, Url}; use futures::future::{ok, Ready}; +use tinyvec::TinyVec; use crate::config::AppConfig; use crate::error::UrlGenerationError; @@ -21,7 +22,7 @@ pub(crate) struct HttpRequestInner { pub(crate) head: Message, pub(crate) path: Path, pub(crate) payload: Payload, - pub(crate) app_data: Rc, + pub(crate) app_data: TinyVec<[Rc; 4]>, rmap: Rc, config: AppConfig, pool: &'static HttpRequestPool, @@ -38,13 +39,16 @@ impl HttpRequest { app_data: Rc, pool: &'static HttpRequestPool, ) -> HttpRequest { + let mut data = TinyVec::<[Rc; 4]>::new(); + data.push(app_data); + HttpRequest(Rc::new(HttpRequestInner { head, path, payload, rmap, config, - app_data, + app_data: data, pool, })) } @@ -215,11 +219,13 @@ impl HttpRequest { /// let opt_t = req.app_data::>(); /// ``` pub fn app_data(&self) -> Option<&T> { - if let Some(st) = self.0.app_data.get::() { - Some(&st) - } else { - None + for container in self.0.app_data.iter().rev() { + if let Some(data) = container.get::() { + return Some(data); + } } + + None } } @@ -342,10 +348,13 @@ impl HttpRequestPool { #[cfg(test)] mod tests { + use actix_service::Service; + use bytes::Bytes; + use super::*; use crate::dev::{ResourceDef, ResourceMap}; use crate::http::{header, StatusCode}; - use crate::test::{call_service, init_service, TestRequest}; + use crate::test::{call_service, init_service, read_body, TestRequest}; use crate::{web, App, HttpResponse}; #[test] @@ -494,6 +503,68 @@ mod tests { assert_eq!(resp.status(), StatusCode::BAD_REQUEST); } + #[actix_rt::test] + async fn test_cascading_data() { + #[allow(dead_code)] + fn echo_usize(req: HttpRequest) -> HttpResponse { + let num = req.app_data::().unwrap(); + HttpResponse::Ok().body(num.to_string()) + } + + let mut srv = init_service( + App::new() + .app_data(88usize) + .service(web::resource("/").route(web::get().to(echo_usize))) + .service( + web::resource("/one") + .app_data(1u32) + .route(web::get().to(echo_usize)), + ), + ) + .await; + + let req = TestRequest::get().uri("/").to_request(); + let resp = srv.call(req).await.unwrap(); + let body = read_body(resp).await; + assert_eq!(body, Bytes::from_static(b"88")); + + let req = TestRequest::get().uri("/one").to_request(); + let resp = srv.call(req).await.unwrap(); + let body = read_body(resp).await; + assert_eq!(body, Bytes::from_static(b"88")); + } + + #[actix_rt::test] + async fn test_overwrite_data() { + #[allow(dead_code)] + fn echo_usize(req: HttpRequest) -> HttpResponse { + let num = req.app_data::().unwrap(); + HttpResponse::Ok().body(num.to_string()) + } + + let mut srv = init_service( + App::new() + .app_data(88usize) + .service(web::resource("/").route(web::get().to(echo_usize))) + .service( + web::resource("/one") + .app_data(1usize) + .route(web::get().to(echo_usize)), + ), + ) + .await; + + let req = TestRequest::get().uri("/").to_request(); + let resp = srv.call(req).await.unwrap(); + let body = read_body(resp).await; + assert_eq!(body, Bytes::from_static(b"88")); + + let req = TestRequest::get().uri("/one").to_request(); + let resp = srv.call(req).await.unwrap(); + let body = read_body(resp).await; + assert_eq!(body, Bytes::from_static(b"1")); + } + #[actix_rt::test] async fn test_extensions_dropped() { struct Tracker { diff --git a/src/resource.rs b/src/resource.rs index 634294cc2..477f0bfba 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -198,9 +198,7 @@ where /// Add resource data. /// - /// If used, this method will create a new data context used for extracting - /// from requests. Data added here is *not* merged with data added on App - /// or containing scopes. + /// Data of different types from parent contexts will still be accessible. pub fn app_data(mut self, data: U) -> Self { if self.data.is_none() { self.data = Some(Extensions::new()); @@ -539,14 +537,14 @@ impl Service for ResourceService { for route in self.routes.iter_mut() { if route.check(&mut req) { if let Some(ref data) = self.data { - req.set_data_container(data.clone()); + req.add_data_container(data.clone()); } return Either::Right(route.call(req)); } } if let Some(ref mut default) = self.default { if let Some(ref data) = self.data { - req.set_data_container(data.clone()); + req.add_data_container(data.clone()); } Either::Right(default.call(req)) } else { @@ -590,14 +588,13 @@ mod tests { use actix_rt::time::delay_for; use actix_service::Service; - use bytes::Bytes; use futures::future::ok; use crate::http::{header, HeaderValue, Method, StatusCode}; use crate::middleware::DefaultHeaders; use crate::service::ServiceRequest; - use crate::test::{call_service, init_service, read_body, TestRequest}; - use crate::{guard, web, App, Error, HttpRequest, HttpResponse}; + use crate::test::{call_service, init_service, TestRequest}; + use crate::{guard, web, App, Error, HttpResponse}; #[actix_rt::test] async fn test_middleware() { @@ -623,79 +620,6 @@ mod tests { ); } - #[actix_rt::test] - async fn test_overwritten_data() { - #[allow(dead_code)] - fn echo_usize(req: HttpRequest) -> HttpResponse { - let num = req.app_data::().unwrap(); - HttpResponse::Ok().body(format!("{}", num)) - } - - #[allow(dead_code)] - fn echo_u32(req: HttpRequest) -> HttpResponse { - let num = req.app_data::().unwrap(); - HttpResponse::Ok().body(format!("{}", num)) - } - - #[allow(dead_code)] - fn echo_both(req: HttpRequest) -> HttpResponse { - let num = req.app_data::().unwrap(); - let num2 = req.app_data::().unwrap(); - HttpResponse::Ok().body(format!("{}-{}", num, num2)) - } - - let mut srv = init_service( - App::new() - .app_data(88usize) - .service(web::resource("/").route(web::get().to(echo_usize))) - .service( - web::resource("/one") - .app_data(1usize) - .route(web::get().to(echo_usize)), - ) - .service( - web::resource("/two") - .app_data(2usize) - .route(web::get().to(echo_usize)), - ) - .service( - web::resource("/three") - .app_data(3u32) - // this doesnt work because app_data "overrides" the - // entire data field potentially passed down - // .route(web::get().to(echo_both)), - .route(web::get().to(echo_u32)), - ) - .service(web::resource("/eight").route(web::get().to(echo_usize))), - ) - .await; - - let req = TestRequest::get().uri("/").to_request(); - let resp = srv.call(req).await.unwrap(); - let body = read_body(resp).await; - assert_eq!(body, Bytes::from_static(b"88")); - - let req = TestRequest::get().uri("/one").to_request(); - let resp = srv.call(req).await.unwrap(); - let body = read_body(resp).await; - assert_eq!(body, Bytes::from_static(b"1")); - - let req = TestRequest::get().uri("/two").to_request(); - let resp = srv.call(req).await.unwrap(); - let body = read_body(resp).await; - assert_eq!(body, Bytes::from_static(b"2")); - - // let req = TestRequest::get().uri("/three").to_request(); - // let resp = srv.call(req).await.unwrap(); - // let body = read_body(resp).await; - // assert_eq!(body, Bytes::from_static(b"88-3")); - - let req = TestRequest::get().uri("/eight").to_request(); - let resp = srv.call(req).await.unwrap(); - let body = read_body(resp).await; - assert_eq!(body, Bytes::from_static(b"88")); - } - #[actix_rt::test] async fn test_middleware_fn() { let mut srv = init_service( diff --git a/src/scope.rs b/src/scope.rs index 2016c6f1c..407d4946d 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -153,9 +153,7 @@ where /// Add scope data. /// - /// If used, this method will create a new data context used for extracting - /// from requests. Data added here is *not* merged with data added on App - /// or containing scopes. + /// Data of different types from parent contexts will still be accessible. pub fn app_data(mut self, data: U) -> Self { if self.data.is_none() { self.data = Some(Extensions::new()); @@ -624,12 +622,12 @@ impl Service for ScopeService { if let Some((srv, _info)) = res { if let Some(ref data) = self.data { - req.set_data_container(data.clone()); + req.add_data_container(data.clone()); } Either::Left(srv.call(req)) } else if let Some(ref mut default) = self.default { if let Some(ref data) = self.data { - req.set_data_container(data.clone()); + req.add_data_container(data.clone()); } Either::Left(default.call(req)) } else { diff --git a/src/service.rs b/src/service.rs index b783fd720..8dc9fa93d 100644 --- a/src/service.rs +++ b/src/service.rs @@ -217,11 +217,13 @@ impl ServiceRequest { /// Get an application data stored with `App::data()` method during /// application configuration. pub fn app_data(&self) -> Option> { - if let Some(st) = (self.0).0.app_data.get::>() { - Some(st.clone()) - } else { - None + for container in (self.0).0.app_data.iter().rev() { + if let Some(data) = container.get::>() { + return Some(Data::clone(&data)); + } } + + None } /// Set request payload. @@ -230,9 +232,12 @@ impl ServiceRequest { } #[doc(hidden)] - /// Set new app data container - pub fn set_data_container(&mut self, extensions: Rc) { - Rc::get_mut(&mut (self.0).0).unwrap().app_data = extensions; + /// Add app data container to request's resolution set. + pub fn add_data_container(&mut self, extensions: Rc) { + Rc::get_mut(&mut (self.0).0) + .unwrap() + .app_data + .push(extensions); } } @@ -578,7 +583,6 @@ mod tests { let resp = srv.call(req).await.unwrap(); assert_eq!(resp.status(), http::StatusCode::NOT_FOUND); } - #[test] fn test_fmt_debug() { let req = TestRequest::get() From 63864ecf9e3e6b8dbf883d3030267a525fb50a18 Mon Sep 17 00:00:00 2001 From: Takeru Sato Date: Wed, 13 May 2020 01:48:35 +0900 Subject: [PATCH 114/157] support parsing of SameSite=None (#1503) --- actix-http/CHANGES.md | 6 ++++++ actix-http/src/cookie/parse.rs | 35 ++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 92302a666..56cd9e58c 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,5 +1,11 @@ # Changes +## [Unreleased] + +### Fixed + +* Support parsing of `SameSite=None` [#1503] + ## [2.0.0-alpha.3] - 2020-05-08 ### Fixed diff --git a/actix-http/src/cookie/parse.rs b/actix-http/src/cookie/parse.rs index ce261c758..d472b32b6 100644 --- a/actix-http/src/cookie/parse.rs +++ b/actix-http/src/cookie/parse.rs @@ -172,6 +172,8 @@ fn parse_inner<'c>(s: &str, decode: bool) -> Result, ParseError> { cookie.same_site = Some(SameSite::Strict); } else if v.eq_ignore_ascii_case("lax") { cookie.same_site = Some(SameSite::Lax); + } else if v.eq_ignore_ascii_case("none") { + cookie.same_site = Some(SameSite::None); } else { // We do nothing here, for now. When/if the `SameSite` // attribute becomes standard, the spec says that we should @@ -261,6 +263,16 @@ mod tests { assert_eq_parse!("foo=bar; SameSite=strict", expected); assert_eq_parse!("foo=bar; SameSite=STrICT", expected); assert_eq_parse!("foo=bar; SameSite=STRICT", expected); + + let expected = Cookie::build("foo", "bar") + .same_site(SameSite::None) + .finish(); + + assert_eq_parse!("foo=bar; SameSite=None", expected); + assert_eq_parse!("foo=bar; SameSITE=None", expected); + assert_eq_parse!("foo=bar; SameSite=nOne", expected); + assert_eq_parse!("foo=bar; SameSite=NoNE", expected); + assert_eq_parse!("foo=bar; SameSite=NONE", expected); } #[test] @@ -396,6 +408,29 @@ mod tests { Domain=foo.com; Expires=Wed, 21 Oct 2015 07:28:00 GMT", unexpected ); + + expected.set_expires(expires); + expected.set_same_site(SameSite::Lax); + assert_eq_parse!( + " foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \ + Domain=foo.com; Expires=Wed, 21 Oct 2015 07:28:00 GMT; \ + SameSite=Lax", + expected + ); + expected.set_same_site(SameSite::Strict); + assert_eq_parse!( + " foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \ + Domain=foo.com; Expires=Wed, 21 Oct 2015 07:28:00 GMT; \ + SameSite=Strict", + expected + ); + expected.set_same_site(SameSite::None); + assert_eq_parse!( + " foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \ + Domain=foo.com; Expires=Wed, 21 Oct 2015 07:28:00 GMT; \ + SameSite=None", + expected + ); } #[test] From 996f1d7eaef70491bbda61fce69d96eed3ba6873 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 13 May 2020 01:57:37 +0100 Subject: [PATCH 115/157] bump msrv in ci and readme --- .github/workflows/linux.yml | 2 +- CHANGES.md | 1 + README.md | 3 ++- actix-files/CHANGES.md | 4 ++++ actix-files/README.md | 2 +- actix-framed/README.md | 2 +- actix-framed/changes.md | 4 ++++ actix-http/CHANGES.md | 4 ++++ actix-http/README.md | 2 +- actix-multipart/CHANGES.md | 4 ++++ actix-multipart/README.md | 2 +- actix-web-actors/CHANGES.md | 6 +++++- actix-web-actors/README.md | 2 +- actix-web-codegen/CHANGES.md | 4 ++++ actix-web-codegen/README.md | 7 +++++++ awc/CHANGES.md | 1 + awc/README.md | 2 +- src/lib.rs | 2 +- test-server/CHANGES.md | 1 + test-server/README.md | 2 +- 20 files changed, 46 insertions(+), 11 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index f94d5f811..ae804cc53 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -8,7 +8,7 @@ jobs: fail-fast: false matrix: version: - - 1.39.0 # MSRV + - 1.40.0 # MSRV - stable - nightly diff --git a/CHANGES.md b/CHANGES.md index 97b7ca63a..09b8f6a60 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ ### Changed * Resources and Scopes can now access non-overridden data types set on App (or containing scopes) when setting their own data. [#1486] +* Bump minimum supported Rust version to 1.40 ## [3.0.0-alpha.2] - 2020-05-08 diff --git a/README.md b/README.md index 5dc8d376f..97e3ceeae 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Documentation](https://docs.rs/actix-web/badge.svg)](https://docs.rs/actix-web) [![Download](https://img.shields.io/crates/d/actix-web.svg)](https://crates.io/crates/actix-web) -[![Version](https://img.shields.io/badge/rustc-1.39+-lightgray.svg)](https://blog.rust-lang.org/2019/11/07/Rust-1.39.0.html) +[![Version](https://img.shields.io/badge/rustc-1.40+-lightgray.svg)](https://blog.rust-lang.org/2019/12/19/Rust-1.40.0.html) ![License](https://img.shields.io/crates/l/actix-web.svg)

@@ -38,6 +38,7 @@ Actix web is a simple, pragmatic and extremely fast web framework for Rust. * Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/)) * Includes an asynchronous [HTTP client](https://actix.rs/actix-web/actix_web/client/index.html) * Supports [Actix actor framework](https://github.com/actix/actix) +* Supports Rust 1.40+ ## Docs diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index c4918b56d..7901a392b 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [Unreleased] - 2020-xx-xx + +* Bump minimum supported Rust version to 1.40 + ## [0.2.1] - 2019-12-22 * Use the same format for file URLs regardless of platforms diff --git a/actix-files/README.md b/actix-files/README.md index 9585e67a8..5a5a62083 100644 --- a/actix-files/README.md +++ b/actix-files/README.md @@ -6,4 +6,4 @@ * [API Documentation](https://docs.rs/actix-files/) * [Chat on gitter](https://gitter.im/actix/actix) * Cargo package: [actix-files](https://crates.io/crates/actix-files) -* Minimum supported Rust version: 1.33 or later +* Minimum supported Rust version: 1.40 or later diff --git a/actix-framed/README.md b/actix-framed/README.md index 1714b3640..3a5ea0596 100644 --- a/actix-framed/README.md +++ b/actix-framed/README.md @@ -5,4 +5,4 @@ * [API Documentation](https://docs.rs/actix-framed/) * [Chat on gitter](https://gitter.im/actix/actix) * Cargo package: [actix-framed](https://crates.io/crates/actix-framed) -* Minimum supported Rust version: 1.33 or later +* Minimum supported Rust version: 1.40 or later diff --git a/actix-framed/changes.md b/actix-framed/changes.md index 41c7aed0e..1c5d31fa2 100644 --- a/actix-framed/changes.md +++ b/actix-framed/changes.md @@ -1,5 +1,9 @@ # Changes +## [Unreleased] - 2020-xx-xx + +* Bump minimum supported Rust version to 1.40 + ## [0.3.0] - 2019-12-25 * Migrate to actix-http 1.0 diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 56cd9e58c..e96b0451d 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Changed + +* Bump minimum supported Rust version to 1.40 + ### Fixed * Support parsing of `SameSite=None` [#1503] diff --git a/actix-http/README.md b/actix-http/README.md index 9acad3e6d..d4c96f2a7 100644 --- a/actix-http/README.md +++ b/actix-http/README.md @@ -8,7 +8,7 @@ Actix http * [API Documentation](https://docs.rs/actix-http/) * [Chat on gitter](https://gitter.im/actix/actix) * Cargo package: [actix-http](https://crates.io/crates/actix-http) -* Minimum supported Rust version: 1.31 or later +* Minimum supported Rust version: 1.40 or later ## Example diff --git a/actix-multipart/CHANGES.md b/actix-multipart/CHANGES.md index ed5c8ad3f..e7bea62c0 100644 --- a/actix-multipart/CHANGES.md +++ b/actix-multipart/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [Unreleased] - 2020-xx-xx + +* Bump minimum supported Rust version to 1.40 + ## [0.2.1] - 2020-01-xx * Remove the unused `time` dependency diff --git a/actix-multipart/README.md b/actix-multipart/README.md index a453f489e..edb2e0020 100644 --- a/actix-multipart/README.md +++ b/actix-multipart/README.md @@ -5,4 +5,4 @@ * [API Documentation](https://docs.rs/actix-multipart/) * [Chat on gitter](https://gitter.im/actix/actix) * Cargo package: [actix-multipart](https://crates.io/crates/actix-multipart) -* Minimum supported Rust version: 1.39 or later +* Minimum supported Rust version: 1.40 or later diff --git a/actix-web-actors/CHANGES.md b/actix-web-actors/CHANGES.md index 68947569a..8fd48f77c 100644 --- a/actix-web-actors/CHANGES.md +++ b/actix-web-actors/CHANGES.md @@ -1,6 +1,10 @@ # Changes -# [3.0.0-alpha.1] - 2020-05-08 +## [Unreleased] - 2020-xx-xx + +* Bump minimum supported Rust version to 1.40 + +## [3.0.0-alpha.1] - 2020-05-08 * Update the actix-web dependency to 3.0.0-alpha.1 * Update the actix dependency to 0.10.0-alpha.2 diff --git a/actix-web-actors/README.md b/actix-web-actors/README.md index 6ff7ac67c..fb8c3a621 100644 --- a/actix-web-actors/README.md +++ b/actix-web-actors/README.md @@ -5,4 +5,4 @@ Actix actors support for actix web framework [![Build Status](https://travis-ci. * [API Documentation](https://docs.rs/actix-web-actors/) * [Chat on gitter](https://gitter.im/actix/actix) * Cargo package: [actix-web-actors](https://crates.io/crates/actix-web-actors) -* Minimum supported Rust version: 1.33 or later +* Minimum supported Rust version: 1.40 or later diff --git a/actix-web-codegen/CHANGES.md b/actix-web-codegen/CHANGES.md index 941cd36de..da2faee38 100644 --- a/actix-web-codegen/CHANGES.md +++ b/actix-web-codegen/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [Unreleased] - 2020-xx-xx + +* Bump minimum supported Rust version to 1.40 + ## [0.2.1] - 2020-02-25 * Add `#[allow(missing_docs)]` attribute to generated structs [#1368] diff --git a/actix-web-codegen/README.md b/actix-web-codegen/README.md index c44a5fc7f..c482a6b36 100644 --- a/actix-web-codegen/README.md +++ b/actix-web-codegen/README.md @@ -1 +1,8 @@ # Macros for actix-web framework [![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) [![crates.io](https://meritbadge.herokuapp.com/actix-web-codegen)](https://crates.io/crates/actix-web-codegen) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +## Documentation & Resources + +* [API Documentation](https://docs.rs/actix-web-codegen/) +* [Chat on gitter](https://gitter.im/actix/actix) +* Cargo package: [actix-web-codegen](https://crates.io/crates/actix-web-codegen) +* Minimum supported Rust version: 1.40 or later diff --git a/awc/CHANGES.md b/awc/CHANGES.md index d127700ac..67bbc38c5 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -5,6 +5,7 @@ ### Changed * Implement `std::error::Error` for our custom errors [#1422] +* Bump minimum supported Rust version to 1.40 [#1422]: https://github.com/actix/actix-web/pull/1422 diff --git a/awc/README.md b/awc/README.md index 3b0034d76..2b6309c1d 100644 --- a/awc/README.md +++ b/awc/README.md @@ -8,7 +8,7 @@ An HTTP Client * [API Documentation](https://docs.rs/awc/) * [Chat on gitter](https://gitter.im/actix/actix) * Cargo package: [awc](https://crates.io/crates/awc) -* Minimum supported Rust version: 1.33 or later +* Minimum supported Rust version: 1.40 or later ## Example diff --git a/src/lib.rs b/src/lib.rs index d7cb45074..cff4acf27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,7 +72,7 @@ //! * SSL support with OpenSSL or `native-tls` //! * Middlewares (`Logger`, `Session`, `CORS`, `DefaultHeaders`) //! * Supports [Actix actor framework](https://github.com/actix/actix) -//! * Supported Rust version: 1.39 or later +//! * Supported Rust version: 1.40 or later //! //! ## Package feature //! diff --git a/test-server/CHANGES.md b/test-server/CHANGES.md index 35552030c..6a92d581c 100644 --- a/test-server/CHANGES.md +++ b/test-server/CHANGES.md @@ -5,6 +5,7 @@ * Update the `time` dependency to 0.2.7 * Update `actix-connect` dependency to 2.0.0-alpha.2 * Make `test_server` `async` fn. +* Bump minimum supported Rust version to 1.40 ## [1.0.0] - 2019-12-13 diff --git a/test-server/README.md b/test-server/README.md index e40650124..db0791db7 100644 --- a/test-server/README.md +++ b/test-server/README.md @@ -6,4 +6,4 @@ * [API Documentation](https://docs.rs/actix-http-test/) * [Chat on gitter](https://gitter.im/actix/actix) * Cargo package: [actix-http-test](https://crates.io/crates/actix-http-test) -* Minimum supported Rust version: 1.33 or later +* Minimum supported Rust version: 1.40 or later From 4fc99d4a6facba1c534fa8ae7d81d486c6f550f8 Mon Sep 17 00:00:00 2001 From: pando85 Date: Fri, 15 May 2020 02:07:27 +0200 Subject: [PATCH 116/157] Fix audit issue logging by default peer address (#1485) * Fix audit issue logging by default peer address By default log format include remote address that is taken from headers. This is very easy to replace making log untrusted. Changing default log format value `%a` to peer address we are getting this trusted data always. Also, remote address option is maintianed and relegated to `%{r}a` value. Related kanidm/kanidm#191. * Rename peer/remote to remote_addr/realip_remote_addr Change names to avoid naming confusions. I choose this accord to Nginx variables and [ngx_http_realip_module](https://nginx.org/en/docs/http/ngx_http_realip_module.html). Add more specific documentation about security concerns of using Real IP in logger. * Rename security advertise header in doc * Add fix audit issue logging by default peer adress to changelog Co-authored-by: Rob Ede --- CHANGES.md | 7 +++++ src/info.rs | 57 +++++++++++++++++++++---------------- src/middleware/logger.rs | 61 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 98 insertions(+), 27 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 09b8f6a60..1a44caee8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,8 +5,14 @@ ### Changed * Resources and Scopes can now access non-overridden data types set on App (or containing scopes) when setting their own data. [#1486] + +* Fix audit issue logging by default peer address [#1485] + * Bump minimum supported Rust version to 1.40 +[#1485]: https://github.com/actix/actix-web/pull/1485 + + ## [3.0.0-alpha.2] - 2020-05-08 ### Changed @@ -21,6 +27,7 @@ [#1452]: https://github.com/actix/actix-web/pull/1452 [#1486]: https://github.com/actix/actix-web/pull/1486 + ## [3.0.0-alpha.1] - 2020-03-11 ### Added diff --git a/src/info.rs b/src/info.rs index c9a642b36..5b506d85a 100644 --- a/src/info.rs +++ b/src/info.rs @@ -12,8 +12,8 @@ const X_FORWARDED_PROTO: &[u8] = b"x-forwarded-proto"; pub struct ConnectionInfo { scheme: String, host: String, - remote: Option, - peer: Option, + realip_remote_addr: Option, + remote_addr: Option, } impl ConnectionInfo { @@ -29,8 +29,7 @@ impl ConnectionInfo { fn new(req: &RequestHead, cfg: &AppConfig) -> ConnectionInfo { let mut host = None; let mut scheme = None; - let mut remote = None; - let mut peer = None; + let mut realip_remote_addr = None; // load forwarded header for hdr in req.headers.get_all(&header::FORWARDED) { @@ -42,8 +41,8 @@ impl ConnectionInfo { if let Some(val) = items.next() { match &name.to_lowercase() as &str { "for" => { - if remote.is_none() { - remote = Some(val.trim()); + if realip_remote_addr.is_none() { + realip_remote_addr = Some(val.trim()); } } "proto" => { @@ -106,27 +105,25 @@ impl ConnectionInfo { } } - // remote addr - if remote.is_none() { + // get remote_addraddr from socketaddr + let remote_addr = req.peer_addr.map(|addr| format!("{}", addr)); + + if realip_remote_addr.is_none() { if let Some(h) = req .headers .get(&HeaderName::from_lowercase(X_FORWARDED_FOR).unwrap()) { if let Ok(h) = h.to_str() { - remote = h.split(',').next().map(|v| v.trim()); + realip_remote_addr = h.split(',').next().map(|v| v.trim()); } } - if remote.is_none() { - // get peeraddr from socketaddr - peer = req.peer_addr.map(|addr| format!("{}", addr)); - } } ConnectionInfo { - peer, + remote_addr, scheme: scheme.unwrap_or("http").to_owned(), host: host.unwrap_or("localhost").to_owned(), - remote: remote.map(|s| s.to_owned()), + realip_remote_addr: realip_remote_addr.map(|s| s.to_owned()), } } @@ -155,13 +152,23 @@ impl ConnectionInfo { &self.host } - /// Remote socket addr of client initiated HTTP request. + /// remote_addr address of the request. + /// + /// Get remote_addr address from socket address + pub fn remote_addr(&self) -> Option<&str> { + if let Some(ref remote_addr) = self.remote_addr { + Some(remote_addr) + } else { + None + } + } + /// Real ip remote addr of client initiated HTTP request. /// /// The addr is resolved through the following headers, in this order: /// /// - Forwarded /// - X-Forwarded-For - /// - peer name of opened socket + /// - remote_addr name of opened socket /// /// # Security /// Do not use this function for security purposes, unless you can ensure the Forwarded and @@ -169,11 +176,11 @@ impl ConnectionInfo { /// address explicitly, use /// [`HttpRequest::peer_addr()`](../web/struct.HttpRequest.html#method.peer_addr) instead. #[inline] - pub fn remote(&self) -> Option<&str> { - if let Some(ref r) = self.remote { + pub fn realip_remote_addr(&self) -> Option<&str> { + if let Some(ref r) = self.realip_remote_addr { Some(r) - } else if let Some(ref peer) = self.peer { - Some(peer) + } else if let Some(ref remote_addr) = self.remote_addr { + Some(remote_addr) } else { None } @@ -202,7 +209,7 @@ mod tests { let info = req.connection_info(); assert_eq!(info.scheme(), "https"); assert_eq!(info.host(), "rust-lang.org"); - assert_eq!(info.remote(), Some("192.0.2.60")); + assert_eq!(info.realip_remote_addr(), Some("192.0.2.60")); let req = TestRequest::default() .header(header::HOST, "rust-lang.org") @@ -211,20 +218,20 @@ mod tests { let info = req.connection_info(); assert_eq!(info.scheme(), "http"); assert_eq!(info.host(), "rust-lang.org"); - assert_eq!(info.remote(), None); + assert_eq!(info.realip_remote_addr(), None); let req = TestRequest::default() .header(X_FORWARDED_FOR, "192.0.2.60") .to_http_request(); let info = req.connection_info(); - assert_eq!(info.remote(), Some("192.0.2.60")); + assert_eq!(info.realip_remote_addr(), Some("192.0.2.60")); let req = TestRequest::default() .header(X_FORWARDED_HOST, "192.0.2.60") .to_http_request(); let info = req.connection_info(); assert_eq!(info.host(), "192.0.2.60"); - assert_eq!(info.remote(), None); + assert_eq!(info.realip_remote_addr(), None); let req = TestRequest::default() .header(X_FORWARDED_PROTO, "https") diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index 7d1577c96..d6b931bb4 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -72,12 +72,21 @@ use crate::HttpResponse; /// /// `%U` Request URL /// +/// `%{r}a` Real IP remote address **\*** +/// /// `%{FOO}i` request.headers['FOO'] /// /// `%{FOO}o` response.headers['FOO'] /// /// `%{FOO}e` os.environ['FOO'] /// +/// # Security +/// **\*** It is calculated using +/// [`ConnectionInfo::realip_remote_addr()`](../dev/struct.ConnectionInfo.html#method.realip_remote_addr) +/// +/// If you use this value ensure that all requests come from trusted hosts, since it is trivial +/// for the remote client to simulate been another client. +/// pub struct Logger(Rc); struct Inner { @@ -301,7 +310,7 @@ impl Format { /// Returns `None` if the format string syntax is incorrect. pub fn new(s: &str) -> Format { log::trace!("Access log format: {}", s); - let fmt = Regex::new(r"%(\{([A-Za-z0-9\-_]+)\}([ioe])|[atPrUsbTD]?)").unwrap(); + let fmt = Regex::new(r"%(\{([A-Za-z0-9\-_]+)\}([aioe])|[atPrUsbTD]?)").unwrap(); let mut idx = 0; let mut results = Vec::new(); @@ -315,6 +324,11 @@ impl Format { if let Some(key) = cap.get(2) { results.push(match cap.get(3).unwrap().as_str() { + "a" => if key.as_str() == "r" { + FormatText::RealIPRemoteAddr + } else { + unreachable!() + }, "i" => FormatText::RequestHeader( HeaderName::try_from(key.as_str()).unwrap(), ), @@ -362,6 +376,7 @@ pub enum FormatText { Time, TimeMillis, RemoteAddr, + RealIPRemoteAddr, UrlPath, RequestHeader(HeaderName), ResponseHeader(HeaderName), @@ -458,7 +473,15 @@ impl FormatText { *self = FormatText::Str(s.to_string()); } FormatText::RemoteAddr => { - let s = if let Some(remote) = req.connection_info().remote() { + let s = if let Some(ref peer) = req.connection_info().remote_addr() { + FormatText::Str(peer.to_string()) + } else { + FormatText::Str("-".to_string()) + }; + *self = s; + } + FormatText::RealIPRemoteAddr => { + let s = if let Some(remote) = req.connection_info().realip_remote_addr() { FormatText::Str(remote.to_string()) } else { FormatText::Str("-".to_string()) @@ -549,6 +572,7 @@ mod tests { header::USER_AGENT, header::HeaderValue::from_static("ACTIX-WEB"), ) + .peer_addr("127.0.0.1:8081".parse().unwrap()) .to_srv_request(); let now = OffsetDateTime::now_utc(); @@ -570,6 +594,7 @@ mod tests { }; let s = format!("{}", FormatDisplay(&render)); assert!(s.contains("GET / HTTP/1.1")); + assert!(s.contains("127.0.0.1")); assert!(s.contains("200 1024")); assert!(s.contains("ACTIX-WEB")); } @@ -598,4 +623,36 @@ mod tests { let s = format!("{}", FormatDisplay(&render)); assert!(s.contains(&format!("{}", now.format("%Y-%m-%dT%H:%M:%S")))); } + + #[actix_rt::test] + async fn test_remote_addr_format() { + let mut format = Format::new("%{r}a"); + + let req = TestRequest::with_header( + header::FORWARDED, + header::HeaderValue::from_static("for=192.0.2.60;proto=http;by=203.0.113.43"), + ) + .to_srv_request(); + + let now = OffsetDateTime::now_utc(); + for unit in &mut format.0 { + unit.render_request(now, &req); + } + + let resp = HttpResponse::build(StatusCode::OK).force_close().finish(); + for unit in &mut format.0 { + unit.render_response(&resp); + } + + let entry_time = OffsetDateTime::now_utc(); + let render = |fmt: &mut Formatter<'_>| { + for unit in &format.0 { + unit.render(fmt, 1024, entry_time)?; + } + Ok(()) + }; + let s = format!("{}", FormatDisplay(&render)); + println!("{}", s); + assert!(s.contains("192.0.2.60")); + } } From 201090d7a26f17ceabb6135daaf9a10223915510 Mon Sep 17 00:00:00 2001 From: Sven Allers Date: Sat, 16 May 2020 01:27:03 +0200 Subject: [PATCH 117/157] Provide impl From> for Data (#1509) --- CHANGES.md | 6 +++++- src/data.rs | 13 +++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 1a44caee8..ab660808e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Added + +* Add option to create `Data` from `Arc` [#1509] + ### Changed * Resources and Scopes can now access non-overridden data types set on App (or containing scopes) when setting their own data. [#1486] @@ -11,7 +15,7 @@ * Bump minimum supported Rust version to 1.40 [#1485]: https://github.com/actix/actix-web/pull/1485 - +[#1509]: https://github.com/actix/actix-web/pull/1509 ## [3.0.0-alpha.2] - 2020-05-08 diff --git a/src/data.rs b/src/data.rs index 0c04e1d90..e657d8b7b 100644 --- a/src/data.rs +++ b/src/data.rs @@ -103,6 +103,12 @@ impl Clone for Data { } } +impl From> for Data { + fn from(arc: Arc) -> Self { + Data(arc) + } +} + impl FromRequest for Data { type Config = (); type Error = Error; @@ -281,4 +287,11 @@ mod tests { assert_eq!(num.load(Ordering::SeqCst), 0); } + + #[actix_rt::test] + async fn test_data_from_arc() { + let data_new = Data::new(String::from("test-123")); + let data_from_arc = Data::from(Arc::new(String::from("test-123"))); + assert_eq!(data_new.0, data_from_arc.0) + } } From f3b023347776b9ea727b632135d777313efdaea6 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 17 May 2020 02:54:42 +0100 Subject: [PATCH 118/157] use mem::take where possible (#1507) --- actix-http/src/body.rs | 17 ++++++----------- actix-http/src/cookie/jar.rs | 4 ++-- actix-http/src/encoding/encoder.rs | 2 +- actix-http/src/h1/dispatcher.rs | 12 +++--------- actix-session/src/lib.rs | 3 +-- src/app_service.rs | 6 +++--- src/resource.rs | 2 +- src/route.rs | 2 +- src/scope.rs | 2 +- src/service.rs | 2 +- 10 files changed, 20 insertions(+), 32 deletions(-) diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index c581db604..f887b53bb 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -189,7 +189,7 @@ impl MessageBody for Body { if len == 0 { Poll::Ready(None) } else { - Poll::Ready(Some(Ok(mem::replace(bin, Bytes::new())))) + Poll::Ready(Some(Ok(mem::take(bin)))) } } Body::Message(ref mut body) => Pin::new(body.as_mut()).poll_next(cx), @@ -307,7 +307,7 @@ impl MessageBody for Bytes { if self.is_empty() { Poll::Ready(None) } else { - Poll::Ready(Some(Ok(mem::replace(self.get_mut(), Bytes::new())))) + Poll::Ready(Some(Ok(mem::take(self.get_mut())))) } } } @@ -324,9 +324,7 @@ impl MessageBody for BytesMut { if self.is_empty() { Poll::Ready(None) } else { - Poll::Ready(Some(Ok( - mem::replace(self.get_mut(), BytesMut::new()).freeze() - ))) + Poll::Ready(Some(Ok(mem::take(self.get_mut()).freeze()))) } } } @@ -344,7 +342,7 @@ impl MessageBody for &'static str { Poll::Ready(None) } else { Poll::Ready(Some(Ok(Bytes::from_static( - mem::replace(self.get_mut(), "").as_ref(), + mem::take(self.get_mut()).as_ref(), )))) } } @@ -362,10 +360,7 @@ impl MessageBody for Vec { if self.is_empty() { Poll::Ready(None) } else { - Poll::Ready(Some(Ok(Bytes::from(mem::replace( - self.get_mut(), - Vec::new(), - ))))) + Poll::Ready(Some(Ok(Bytes::from(mem::take(self.get_mut()))))) } } } @@ -383,7 +378,7 @@ impl MessageBody for String { Poll::Ready(None) } else { Poll::Ready(Some(Ok(Bytes::from( - mem::replace(self.get_mut(), String::new()).into_bytes(), + mem::take(self.get_mut()).into_bytes(), )))) } } diff --git a/actix-http/src/cookie/jar.rs b/actix-http/src/cookie/jar.rs index 0c76c1cfe..fbefa1bbf 100644 --- a/actix-http/src/cookie/jar.rs +++ b/actix-http/src/cookie/jar.rs @@ -1,5 +1,5 @@ use std::collections::HashSet; -use std::mem::replace; +use std::mem; use time::{Duration, OffsetDateTime}; @@ -273,7 +273,7 @@ impl CookieJar { )] pub fn clear(&mut self) { self.delta_cookies.clear(); - for delta in replace(&mut self.original_cookies, HashSet::new()) { + for delta in mem::take(&mut self.original_cookies) { self.remove(delta.cookie); } } diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index 72bb7d603..ef69aa039 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -106,7 +106,7 @@ impl MessageBody for EncoderBody { if b.is_empty() { Poll::Ready(None) } else { - Poll::Ready(Some(Ok(std::mem::replace(b, Bytes::new())))) + Poll::Ready(Some(Ok(std::mem::take(b)))) } } EncoderBody::Stream(b) => b.poll_next(cx), diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 88f11c7c7..c95000bf9 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -1,6 +1,3 @@ -// Because MSRV is 1.39.0. -#![allow(clippy::mem_replace_with_default)] - use std::collections::VecDeque; use std::future::Future; use std::pin::Pin; @@ -795,13 +792,10 @@ where let inner_p = inner.as_mut().project(); let mut parts = FramedParts::with_read_buf( inner_p.io.take().unwrap(), - std::mem::replace(inner_p.codec, Codec::default()), - std::mem::replace(inner_p.read_buf, BytesMut::default()), - ); - parts.write_buf = std::mem::replace( - inner_p.write_buf, - BytesMut::default(), + std::mem::take(inner_p.codec), + std::mem::take(inner_p.read_buf), ); + parts.write_buf = std::mem::take(inner_p.write_buf); let framed = Framed::from_parts(parts); let upgrade = inner_p.upgrade.take().unwrap().call((req, framed)); diff --git a/actix-session/src/lib.rs b/actix-session/src/lib.rs index b6e5dd331..e2bf0143b 100644 --- a/actix-session/src/lib.rs +++ b/actix-session/src/lib.rs @@ -200,8 +200,7 @@ impl Session { .extensions() .get::>>() { - let state = - std::mem::replace(&mut s_impl.borrow_mut().state, HashMap::new()); + let state = std::mem::take(&mut s_impl.borrow_mut().state); (s_impl.borrow().status.clone(), Some(state.into_iter())) } else { (SessionStatus::Unchanged, None) diff --git a/src/app_service.rs b/src/app_service.rs index 2d64bed3e..693624ba0 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -12,7 +12,7 @@ use actix_service::{fn_service, Service, ServiceFactory}; use futures::future::{join_all, ok, FutureExt, LocalBoxFuture}; use crate::config::{AppConfig, AppService}; -use crate::data::{FnDataFactory, DataFactory}; +use crate::data::{DataFactory, FnDataFactory}; use crate::error::Error; use crate::guard::Guard; use crate::request::{HttpRequest, HttpRequestPool}; @@ -76,7 +76,7 @@ where let mut config = AppService::new(config, default.clone(), self.data.clone()); // register services - std::mem::replace(&mut *self.services.borrow_mut(), Vec::new()) + std::mem::take(&mut *self.services.borrow_mut()) .into_iter() .for_each(|mut srv| srv.register(&mut config)); @@ -99,7 +99,7 @@ where }); // external resources - for mut rdef in std::mem::replace(&mut *self.external.borrow_mut(), Vec::new()) { + for mut rdef in std::mem::take(&mut *self.external.borrow_mut()) { rmap.add(&mut rdef, None); } diff --git a/src/resource.rs b/src/resource.rs index 477f0bfba..4c0e26c18 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -379,7 +379,7 @@ where let guards = if self.guards.is_empty() { None } else { - Some(std::mem::replace(&mut self.guards, Vec::new())) + Some(std::mem::take(&mut self.guards)) }; let mut rdef = if config.is_root() || !self.rdef.is_empty() { ResourceDef::new(insert_slash(self.rdef.clone())) diff --git a/src/route.rs b/src/route.rs index f7e391746..11455630c 100644 --- a/src/route.rs +++ b/src/route.rs @@ -56,7 +56,7 @@ impl Route { } pub(crate) fn take_guards(&mut self) -> Vec> { - std::mem::replace(Rc::get_mut(&mut self.guards).unwrap(), Vec::new()) + std::mem::take(Rc::get_mut(&mut self.guards).unwrap()) } } diff --git a/src/scope.rs b/src/scope.rs index 407d4946d..5afca61da 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -429,7 +429,7 @@ where let mut rmap = ResourceMap::new(ResourceDef::root_prefix(&self.rdef)); // external resources - for mut rdef in std::mem::replace(&mut self.external, Vec::new()) { + for mut rdef in std::mem::take(&mut self.external) { rmap.add(&mut rdef, None); } diff --git a/src/service.rs b/src/service.rs index 8dc9fa93d..c0148a9b2 100644 --- a/src/service.rs +++ b/src/service.rs @@ -515,7 +515,7 @@ where let guards = if self.guards.is_empty() { None } else { - Some(std::mem::replace(&mut self.guards, Vec::new())) + Some(std::mem::take(&mut self.guards)) }; let mut rdef = if config.is_root() || !self.rdef.is_empty() { From 433a4563cf5db9c6ca6d059a4934fb2a954bea63 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 17 May 2020 10:50:19 +0900 Subject: [PATCH 119/157] Remove outdated members --- Cargo.toml | 3 - actix-cors/CHANGES.md | 15 - actix-cors/Cargo.toml | 26 - actix-cors/LICENSE-APACHE | 1 - actix-cors/LICENSE-MIT | 1 - actix-cors/src/lib.rs | 1204 --------------------------------- actix-identity/CHANGES.md | 17 - actix-identity/Cargo.toml | 29 - actix-identity/LICENSE-APACHE | 1 - actix-identity/LICENSE-MIT | 1 - actix-identity/src/lib.rs | 1128 ------------------------------ actix-session/CHANGES.md | 73 -- actix-session/Cargo.toml | 35 - actix-session/LICENSE-APACHE | 1 - actix-session/LICENSE-MIT | 1 - actix-session/src/cookie.rs | 545 --------------- actix-session/src/lib.rs | 321 --------- 17 files changed, 3402 deletions(-) delete mode 100644 actix-cors/CHANGES.md delete mode 100644 actix-cors/Cargo.toml delete mode 120000 actix-cors/LICENSE-APACHE delete mode 120000 actix-cors/LICENSE-MIT delete mode 100644 actix-cors/src/lib.rs delete mode 100644 actix-identity/CHANGES.md delete mode 100644 actix-identity/Cargo.toml delete mode 120000 actix-identity/LICENSE-APACHE delete mode 120000 actix-identity/LICENSE-MIT delete mode 100644 actix-identity/src/lib.rs delete mode 100644 actix-session/CHANGES.md delete mode 100644 actix-session/Cargo.toml delete mode 120000 actix-session/LICENSE-APACHE delete mode 120000 actix-session/LICENSE-MIT delete mode 100644 actix-session/src/cookie.rs delete mode 100644 actix-session/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index b24cc89d2..4d2d783ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,11 +30,8 @@ members = [ ".", "awc", "actix-http", -# "actix-cors", "actix-files", "actix-framed", -# "actix-session", -# "actix-identity", "actix-multipart", "actix-web-actors", "actix-web-codegen", diff --git a/actix-cors/CHANGES.md b/actix-cors/CHANGES.md deleted file mode 100644 index 8022ea4e8..000000000 --- a/actix-cors/CHANGES.md +++ /dev/null @@ -1,15 +0,0 @@ -# Changes - -## [0.2.0] - 2019-12-20 - -* Release - -## [0.2.0-alpha.3] - 2019-12-07 - -* Migrate to actix-web 2.0.0 - -* Bump `derive_more` crate version to 0.99.0 - -## [0.1.0] - 2019-06-15 - -* Move cors middleware to separate crate diff --git a/actix-cors/Cargo.toml b/actix-cors/Cargo.toml deleted file mode 100644 index 3fcd92f4f..000000000 --- a/actix-cors/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "actix-cors" -version = "0.2.0" -authors = ["Nikolay Kim "] -description = "Cross-origin resource sharing (CORS) for Actix applications." -readme = "README.md" -keywords = ["web", "framework"] -homepage = "https://actix.rs" -repository = "https://github.com/actix/actix-web.git" -documentation = "https://docs.rs/actix-cors/" -license = "MIT/Apache-2.0" -edition = "2018" -workspace = ".." - -[lib] -name = "actix_cors" -path = "src/lib.rs" - -[dependencies] -actix-web = "2.0.0-rc" -actix-service = "1.0.1" -derive_more = "0.99.2" -futures = "0.3.1" - -[dev-dependencies] -actix-rt = "1.0.0" diff --git a/actix-cors/LICENSE-APACHE b/actix-cors/LICENSE-APACHE deleted file mode 120000 index 965b606f3..000000000 --- a/actix-cors/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/actix-cors/LICENSE-MIT b/actix-cors/LICENSE-MIT deleted file mode 120000 index 76219eb72..000000000 --- a/actix-cors/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/actix-cors/src/lib.rs b/actix-cors/src/lib.rs deleted file mode 100644 index 429fe9eab..000000000 --- a/actix-cors/src/lib.rs +++ /dev/null @@ -1,1204 +0,0 @@ -#![allow(clippy::borrow_interior_mutable_const, clippy::type_complexity)] -//! Cross-origin resource sharing (CORS) for Actix applications -//! -//! CORS middleware could be used with application and with resource. -//! Cors middleware could be used as parameter for `App::wrap()`, -//! `Resource::wrap()` or `Scope::wrap()` methods. -//! -//! # Example -//! -//! ```rust -//! use actix_cors::Cors; -//! use actix_web::{http, web, App, HttpRequest, HttpResponse, HttpServer}; -//! -//! async fn index(req: HttpRequest) -> &'static str { -//! "Hello world" -//! } -//! -//! fn main() -> std::io::Result<()> { -//! HttpServer::new(|| App::new() -//! .wrap( -//! Cors::new() // <- Construct CORS middleware builder -//! .allowed_origin("https://www.rust-lang.org/") -//! .allowed_methods(vec!["GET", "POST"]) -//! .allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT]) -//! .allowed_header(http::header::CONTENT_TYPE) -//! .max_age(3600) -//! .finish()) -//! .service( -//! web::resource("/index.html") -//! .route(web::get().to(index)) -//! .route(web::head().to(|| HttpResponse::MethodNotAllowed())) -//! )) -//! .bind("127.0.0.1:8080")?; -//! -//! Ok(()) -//! } -//! ``` -//! In this example custom *CORS* middleware get registered for "/index.html" -//! endpoint. -//! -//! Cors middleware automatically handle *OPTIONS* preflight request. -use std::collections::HashSet; -use std::convert::TryFrom; -use std::iter::FromIterator; -use std::rc::Rc; -use std::task::{Context, Poll}; - -use actix_service::{Service, Transform}; -use actix_web::dev::{RequestHead, ServiceRequest, ServiceResponse}; -use actix_web::error::{Error, ResponseError, Result}; -use actix_web::http::header::{self, HeaderName, HeaderValue}; -use actix_web::http::{self, Error as HttpError, Method, StatusCode, Uri}; -use actix_web::HttpResponse; -use derive_more::Display; -use futures::future::{ok, Either, FutureExt, LocalBoxFuture, Ready}; - -/// A set of errors that can occur during processing CORS -#[derive(Debug, Display)] -pub enum CorsError { - /// The HTTP request header `Origin` is required but was not provided - #[display( - fmt = "The HTTP request header `Origin` is required but was not provided" - )] - MissingOrigin, - /// The HTTP request header `Origin` could not be parsed correctly. - #[display(fmt = "The HTTP request header `Origin` could not be parsed correctly.")] - BadOrigin, - /// The request header `Access-Control-Request-Method` is required but is - /// missing - #[display( - fmt = "The request header `Access-Control-Request-Method` is required but is missing" - )] - MissingRequestMethod, - /// The request header `Access-Control-Request-Method` has an invalid value - #[display( - fmt = "The request header `Access-Control-Request-Method` has an invalid value" - )] - BadRequestMethod, - /// The request header `Access-Control-Request-Headers` has an invalid - /// value - #[display( - fmt = "The request header `Access-Control-Request-Headers` has an invalid value" - )] - BadRequestHeaders, - /// Origin is not allowed to make this request - #[display(fmt = "Origin is not allowed to make this request")] - OriginNotAllowed, - /// Requested method is not allowed - #[display(fmt = "Requested method is not allowed")] - MethodNotAllowed, - /// One or more headers requested are not allowed - #[display(fmt = "One or more headers requested are not allowed")] - HeadersNotAllowed, -} - -impl ResponseError for CorsError { - fn status_code(&self) -> StatusCode { - StatusCode::BAD_REQUEST - } - - fn error_response(&self) -> HttpResponse { - HttpResponse::with_body(StatusCode::BAD_REQUEST, format!("{}", self).into()) - } -} - -/// An enum signifying that some of type T is allowed, or `All` (everything is -/// allowed). -/// -/// `Default` is implemented for this enum and is `All`. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum AllOrSome { - /// Everything is allowed. Usually equivalent to the "*" value. - All, - /// Only some of `T` is allowed - Some(T), -} - -impl Default for AllOrSome { - fn default() -> Self { - AllOrSome::All - } -} - -impl AllOrSome { - /// Returns whether this is an `All` variant - pub fn is_all(&self) -> bool { - match *self { - AllOrSome::All => true, - AllOrSome::Some(_) => false, - } - } - - /// Returns whether this is a `Some` variant - pub fn is_some(&self) -> bool { - !self.is_all() - } - - /// Returns &T - pub fn as_ref(&self) -> Option<&T> { - match *self { - AllOrSome::All => None, - AllOrSome::Some(ref t) => Some(t), - } - } -} - -/// Structure that follows the builder pattern for building `Cors` middleware -/// structs. -/// -/// To construct a cors: -/// -/// 1. Call [`Cors::build`](struct.Cors.html#method.build) to start building. -/// 2. Use any of the builder methods to set fields in the backend. -/// 3. Call [finish](struct.Cors.html#method.finish) to retrieve the -/// constructed backend. -/// -/// # Example -/// -/// ```rust -/// use actix_cors::Cors; -/// use actix_web::http::header; -/// -/// # fn main() { -/// let cors = Cors::new() -/// .allowed_origin("https://www.rust-lang.org/") -/// .allowed_methods(vec!["GET", "POST"]) -/// .allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT]) -/// .allowed_header(header::CONTENT_TYPE) -/// .max_age(3600); -/// # } -/// ``` -#[derive(Default)] -pub struct Cors { - cors: Option, - methods: bool, - error: Option, - expose_hdrs: HashSet, -} - -impl Cors { - /// Build a new CORS middleware instance - pub fn new() -> Cors { - Cors { - cors: Some(Inner { - origins: AllOrSome::All, - origins_str: None, - methods: HashSet::new(), - headers: AllOrSome::All, - expose_hdrs: None, - max_age: None, - preflight: true, - send_wildcard: false, - supports_credentials: false, - vary_header: true, - }), - methods: false, - error: None, - expose_hdrs: HashSet::new(), - } - } - - /// Build a new CORS default middleware - pub fn default() -> CorsFactory { - let inner = Inner { - origins: AllOrSome::default(), - origins_str: None, - methods: HashSet::from_iter( - vec![ - Method::GET, - Method::HEAD, - Method::POST, - Method::OPTIONS, - Method::PUT, - Method::PATCH, - Method::DELETE, - ] - .into_iter(), - ), - headers: AllOrSome::All, - expose_hdrs: None, - max_age: None, - preflight: true, - send_wildcard: false, - supports_credentials: false, - vary_header: true, - }; - CorsFactory { - inner: Rc::new(inner), - } - } - - /// Add an origin that are allowed to make requests. - /// Will be verified against the `Origin` request header. - /// - /// When `All` is set, and `send_wildcard` is set, "*" will be sent in - /// the `Access-Control-Allow-Origin` response header. Otherwise, the - /// client's `Origin` request header will be echoed back in the - /// `Access-Control-Allow-Origin` response header. - /// - /// When `Some` is set, the client's `Origin` request header will be - /// checked in a case-sensitive manner. - /// - /// This is the `list of origins` in the - /// [Resource Processing Model](https://www.w3.org/TR/cors/#resource-processing-model). - /// - /// Defaults to `All`. - /// - /// Builder panics if supplied origin is not valid uri. - pub fn allowed_origin(mut self, origin: &str) -> Cors { - if let Some(cors) = cors(&mut self.cors, &self.error) { - match Uri::try_from(origin) { - Ok(_) => { - if cors.origins.is_all() { - cors.origins = AllOrSome::Some(HashSet::new()); - } - if let AllOrSome::Some(ref mut origins) = cors.origins { - origins.insert(origin.to_owned()); - } - } - Err(e) => { - self.error = Some(e.into()); - } - } - } - self - } - - /// Set a list of methods which the allowed origins are allowed to access - /// for requests. - /// - /// This is the `list of methods` in the - /// [Resource Processing Model](https://www.w3.org/TR/cors/#resource-processing-model). - /// - /// Defaults to `[GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE]` - pub fn allowed_methods(mut self, methods: U) -> Cors - where - U: IntoIterator, - Method: TryFrom, - >::Error: Into, - { - self.methods = true; - if let Some(cors) = cors(&mut self.cors, &self.error) { - for m in methods { - match Method::try_from(m) { - Ok(method) => { - cors.methods.insert(method); - } - Err(e) => { - self.error = Some(e.into()); - break; - } - } - } - } - self - } - - /// Set an allowed header - pub fn allowed_header(mut self, header: H) -> Cors - where - HeaderName: TryFrom, - >::Error: Into, - { - if let Some(cors) = cors(&mut self.cors, &self.error) { - match HeaderName::try_from(header) { - Ok(method) => { - if cors.headers.is_all() { - cors.headers = AllOrSome::Some(HashSet::new()); - } - if let AllOrSome::Some(ref mut headers) = cors.headers { - headers.insert(method); - } - } - Err(e) => self.error = Some(e.into()), - } - } - self - } - - /// Set a list of header field names which can be used when - /// this resource is accessed by allowed origins. - /// - /// If `All` is set, whatever is requested by the client in - /// `Access-Control-Request-Headers` will be echoed back in the - /// `Access-Control-Allow-Headers` header. - /// - /// This is the `list of headers` in the - /// [Resource Processing Model](https://www.w3.org/TR/cors/#resource-processing-model). - /// - /// Defaults to `All`. - pub fn allowed_headers(mut self, headers: U) -> Cors - where - U: IntoIterator, - HeaderName: TryFrom, - >::Error: Into, - { - if let Some(cors) = cors(&mut self.cors, &self.error) { - for h in headers { - match HeaderName::try_from(h) { - Ok(method) => { - if cors.headers.is_all() { - cors.headers = AllOrSome::Some(HashSet::new()); - } - if let AllOrSome::Some(ref mut headers) = cors.headers { - headers.insert(method); - } - } - Err(e) => { - self.error = Some(e.into()); - break; - } - } - } - } - self - } - - /// Set a list of headers which are safe to expose to the API of a CORS API - /// specification. This corresponds to the - /// `Access-Control-Expose-Headers` response header. - /// - /// This is the `list of exposed headers` in the - /// [Resource Processing Model](https://www.w3.org/TR/cors/#resource-processing-model). - /// - /// This defaults to an empty set. - pub fn expose_headers(mut self, headers: U) -> Cors - where - U: IntoIterator, - HeaderName: TryFrom, - >::Error: Into, - { - for h in headers { - match HeaderName::try_from(h) { - Ok(method) => { - self.expose_hdrs.insert(method); - } - Err(e) => { - self.error = Some(e.into()); - break; - } - } - } - self - } - - /// Set a maximum time for which this CORS request maybe cached. - /// This value is set as the `Access-Control-Max-Age` header. - /// - /// This defaults to `None` (unset). - pub fn max_age(mut self, max_age: usize) -> Cors { - if let Some(cors) = cors(&mut self.cors, &self.error) { - cors.max_age = Some(max_age) - } - self - } - - /// Set a wildcard origins - /// - /// If send wildcard is set and the `allowed_origins` parameter is `All`, a - /// wildcard `Access-Control-Allow-Origin` response header is sent, - /// rather than the requestโ€™s `Origin` header. - /// - /// This is the `supports credentials flag` in the - /// [Resource Processing Model](https://www.w3.org/TR/cors/#resource-processing-model). - /// - /// This **CANNOT** be used in conjunction with `allowed_origins` set to - /// `All` and `allow_credentials` set to `true`. Depending on the mode - /// of usage, this will either result in an `Error:: - /// CredentialsWithWildcardOrigin` error during actix launch or runtime. - /// - /// Defaults to `false`. - pub fn send_wildcard(mut self) -> Cors { - if let Some(cors) = cors(&mut self.cors, &self.error) { - cors.send_wildcard = true - } - self - } - - /// Allows users to make authenticated requests - /// - /// If true, injects the `Access-Control-Allow-Credentials` header in - /// responses. This allows cookies and credentials to be submitted - /// across domains. - /// - /// This option cannot be used in conjunction with an `allowed_origin` set - /// to `All` and `send_wildcards` set to `true`. - /// - /// Defaults to `false`. - /// - /// Builder panics if credentials are allowed, but the Origin is set to "*". - /// This is not allowed by W3C - pub fn supports_credentials(mut self) -> Cors { - if let Some(cors) = cors(&mut self.cors, &self.error) { - cors.supports_credentials = true - } - self - } - - /// Disable `Vary` header support. - /// - /// When enabled the header `Vary: Origin` will be returned as per the W3 - /// implementation guidelines. - /// - /// Setting this header when the `Access-Control-Allow-Origin` is - /// dynamically generated (e.g. when there is more than one allowed - /// origin, and an Origin than '*' is returned) informs CDNs and other - /// caches that the CORS headers are dynamic, and cannot be cached. - /// - /// By default `vary` header support is enabled. - pub fn disable_vary_header(mut self) -> Cors { - if let Some(cors) = cors(&mut self.cors, &self.error) { - cors.vary_header = false - } - self - } - - /// Disable *preflight* request support. - /// - /// When enabled cors middleware automatically handles *OPTIONS* request. - /// This is useful application level middleware. - /// - /// By default *preflight* support is enabled. - pub fn disable_preflight(mut self) -> Cors { - if let Some(cors) = cors(&mut self.cors, &self.error) { - cors.preflight = false - } - self - } - - /// Construct cors middleware - pub fn finish(self) -> CorsFactory { - let mut slf = if !self.methods { - self.allowed_methods(vec![ - Method::GET, - Method::HEAD, - Method::POST, - Method::OPTIONS, - Method::PUT, - Method::PATCH, - Method::DELETE, - ]) - } else { - self - }; - - if let Some(e) = slf.error.take() { - panic!("{}", e); - } - - let mut cors = slf.cors.take().expect("cannot reuse CorsBuilder"); - - if cors.supports_credentials && cors.send_wildcard && cors.origins.is_all() { - panic!("Credentials are allowed, but the Origin is set to \"*\""); - } - - if let AllOrSome::Some(ref origins) = cors.origins { - let s = origins - .iter() - .fold(String::new(), |s, v| format!("{}, {}", s, v)); - cors.origins_str = Some(HeaderValue::try_from(&s[2..]).unwrap()); - } - - if !slf.expose_hdrs.is_empty() { - cors.expose_hdrs = Some( - slf.expose_hdrs - .iter() - .fold(String::new(), |s, v| format!("{}, {}", s, v.as_str()))[2..] - .to_owned(), - ); - } - - CorsFactory { - inner: Rc::new(cors), - } - } -} - -fn cors<'a>( - parts: &'a mut Option, - err: &Option, -) -> Option<&'a mut Inner> { - if err.is_some() { - return None; - } - parts.as_mut() -} - -/// `Middleware` for Cross-origin resource sharing support -/// -/// The Cors struct contains the settings for CORS requests to be validated and -/// for responses to be generated. -pub struct CorsFactory { - inner: Rc, -} - -impl Transform for CorsFactory -where - S: Service, Error = Error>, - S::Future: 'static, - B: 'static, -{ - type Request = ServiceRequest; - type Response = ServiceResponse; - type Error = Error; - type InitError = (); - type Transform = CorsMiddleware; - type Future = Ready>; - - fn new_transform(&self, service: S) -> Self::Future { - ok(CorsMiddleware { - service, - inner: self.inner.clone(), - }) - } -} - -/// `Middleware` for Cross-origin resource sharing support -/// -/// The Cors struct contains the settings for CORS requests to be validated and -/// for responses to be generated. -#[derive(Clone)] -pub struct CorsMiddleware { - service: S, - inner: Rc, -} - -struct Inner { - methods: HashSet, - origins: AllOrSome>, - origins_str: Option, - headers: AllOrSome>, - expose_hdrs: Option, - max_age: Option, - preflight: bool, - send_wildcard: bool, - supports_credentials: bool, - vary_header: bool, -} - -impl Inner { - fn validate_origin(&self, req: &RequestHead) -> Result<(), CorsError> { - if let Some(hdr) = req.headers().get(&header::ORIGIN) { - if let Ok(origin) = hdr.to_str() { - return match self.origins { - AllOrSome::All => Ok(()), - AllOrSome::Some(ref allowed_origins) => allowed_origins - .get(origin) - .and_then(|_| Some(())) - .ok_or_else(|| CorsError::OriginNotAllowed), - }; - } - Err(CorsError::BadOrigin) - } else { - match self.origins { - AllOrSome::All => Ok(()), - _ => Err(CorsError::MissingOrigin), - } - } - } - - fn access_control_allow_origin(&self, req: &RequestHead) -> Option { - match self.origins { - AllOrSome::All => { - if self.send_wildcard { - Some(HeaderValue::from_static("*")) - } else if let Some(origin) = req.headers().get(&header::ORIGIN) { - Some(origin.clone()) - } else { - None - } - } - AllOrSome::Some(ref origins) => { - if let Some(origin) = - req.headers() - .get(&header::ORIGIN) - .filter(|o| match o.to_str() { - Ok(os) => origins.contains(os), - _ => false, - }) - { - Some(origin.clone()) - } else { - Some(self.origins_str.as_ref().unwrap().clone()) - } - } - } - } - - fn validate_allowed_method(&self, req: &RequestHead) -> Result<(), CorsError> { - if let Some(hdr) = req.headers().get(&header::ACCESS_CONTROL_REQUEST_METHOD) { - if let Ok(meth) = hdr.to_str() { - if let Ok(method) = Method::try_from(meth) { - return self - .methods - .get(&method) - .and_then(|_| Some(())) - .ok_or_else(|| CorsError::MethodNotAllowed); - } - } - Err(CorsError::BadRequestMethod) - } else { - Err(CorsError::MissingRequestMethod) - } - } - - fn validate_allowed_headers(&self, req: &RequestHead) -> Result<(), CorsError> { - match self.headers { - AllOrSome::All => Ok(()), - AllOrSome::Some(ref allowed_headers) => { - if let Some(hdr) = - req.headers().get(&header::ACCESS_CONTROL_REQUEST_HEADERS) - { - if let Ok(headers) = hdr.to_str() { - let mut hdrs = HashSet::new(); - for hdr in headers.split(',') { - match HeaderName::try_from(hdr.trim()) { - Ok(hdr) => hdrs.insert(hdr), - Err(_) => return Err(CorsError::BadRequestHeaders), - }; - } - // `Access-Control-Request-Headers` must contain 1 or more - // `field-name`. - if !hdrs.is_empty() { - if !hdrs.is_subset(allowed_headers) { - return Err(CorsError::HeadersNotAllowed); - } - return Ok(()); - } - } - Err(CorsError::BadRequestHeaders) - } else { - Ok(()) - } - } - } - } -} - -impl Service for CorsMiddleware -where - S: Service, Error = Error>, - S::Future: 'static, - B: 'static, -{ - type Request = ServiceRequest; - type Response = ServiceResponse; - type Error = Error; - type Future = Either< - Ready>, - LocalBoxFuture<'static, Result>, - >; - - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { - self.service.poll_ready(cx) - } - - fn call(&mut self, req: ServiceRequest) -> Self::Future { - if self.inner.preflight && Method::OPTIONS == *req.method() { - if let Err(e) = self - .inner - .validate_origin(req.head()) - .and_then(|_| self.inner.validate_allowed_method(req.head())) - .and_then(|_| self.inner.validate_allowed_headers(req.head())) - { - return Either::Left(ok(req.error_response(e))); - } - - // allowed headers - let headers = if let Some(headers) = self.inner.headers.as_ref() { - Some( - HeaderValue::try_from( - &headers - .iter() - .fold(String::new(), |s, v| s + "," + v.as_str()) - .as_str()[1..], - ) - .unwrap(), - ) - } else if let Some(hdr) = - req.headers().get(&header::ACCESS_CONTROL_REQUEST_HEADERS) - { - Some(hdr.clone()) - } else { - None - }; - - let res = HttpResponse::Ok() - .if_some(self.inner.max_age.as_ref(), |max_age, resp| { - let _ = resp.header( - header::ACCESS_CONTROL_MAX_AGE, - format!("{}", max_age).as_str(), - ); - }) - .if_some(headers, |headers, resp| { - let _ = resp.header(header::ACCESS_CONTROL_ALLOW_HEADERS, headers); - }) - .if_some( - self.inner.access_control_allow_origin(req.head()), - |origin, resp| { - let _ = resp.header(header::ACCESS_CONTROL_ALLOW_ORIGIN, origin); - }, - ) - .if_true(self.inner.supports_credentials, |resp| { - resp.header(header::ACCESS_CONTROL_ALLOW_CREDENTIALS, "true"); - }) - .header( - header::ACCESS_CONTROL_ALLOW_METHODS, - &self - .inner - .methods - .iter() - .fold(String::new(), |s, v| s + "," + v.as_str()) - .as_str()[1..], - ) - .finish() - .into_body(); - - Either::Left(ok(req.into_response(res))) - } else { - if req.headers().contains_key(&header::ORIGIN) { - // Only check requests with a origin header. - if let Err(e) = self.inner.validate_origin(req.head()) { - return Either::Left(ok(req.error_response(e))); - } - } - - let inner = self.inner.clone(); - let has_origin = req.headers().contains_key(&header::ORIGIN); - let fut = self.service.call(req); - - Either::Right( - async move { - let res = fut.await; - - if has_origin { - let mut res = res?; - if let Some(origin) = - inner.access_control_allow_origin(res.request().head()) - { - res.headers_mut().insert( - header::ACCESS_CONTROL_ALLOW_ORIGIN, - origin.clone(), - ); - }; - - if let Some(ref expose) = inner.expose_hdrs { - res.headers_mut().insert( - header::ACCESS_CONTROL_EXPOSE_HEADERS, - HeaderValue::try_from(expose.as_str()).unwrap(), - ); - } - if inner.supports_credentials { - res.headers_mut().insert( - header::ACCESS_CONTROL_ALLOW_CREDENTIALS, - HeaderValue::from_static("true"), - ); - } - if inner.vary_header { - let value = if let Some(hdr) = - res.headers_mut().get(&header::VARY) - { - let mut val: Vec = - Vec::with_capacity(hdr.as_bytes().len() + 8); - val.extend(hdr.as_bytes()); - val.extend(b", Origin"); - HeaderValue::try_from(&val[..]).unwrap() - } else { - HeaderValue::from_static("Origin") - }; - res.headers_mut().insert(header::VARY, value); - } - Ok(res) - } else { - res - } - } - .boxed_local(), - ) - } - } -} - -#[cfg(test)] -mod tests { - use actix_service::{fn_service, Transform}; - use actix_web::test::{self, TestRequest}; - - use super::*; - - #[actix_rt::test] - #[should_panic(expected = "Credentials are allowed, but the Origin is set to")] - async fn cors_validates_illegal_allow_credentials() { - let _cors = Cors::new().supports_credentials().send_wildcard().finish(); - } - - #[actix_rt::test] - async fn validate_origin_allows_all_origins() { - let mut cors = Cors::new() - .finish() - .new_transform(test::ok_service()) - .await - .unwrap(); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .to_srv_request(); - - let resp = test::call_service(&mut cors, req).await; - assert_eq!(resp.status(), StatusCode::OK); - } - - #[actix_rt::test] - async fn default() { - let mut cors = Cors::default() - .new_transform(test::ok_service()) - .await - .unwrap(); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .to_srv_request(); - - let resp = test::call_service(&mut cors, req).await; - assert_eq!(resp.status(), StatusCode::OK); - } - - #[actix_rt::test] - async fn test_preflight() { - let mut cors = Cors::new() - .send_wildcard() - .max_age(3600) - .allowed_methods(vec![Method::GET, Method::OPTIONS, Method::POST]) - .allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT]) - .allowed_header(header::CONTENT_TYPE) - .finish() - .new_transform(test::ok_service()) - .await - .unwrap(); - - let req = TestRequest::with_header("Origin", "https://www.example.com") - .method(Method::OPTIONS) - .header(header::ACCESS_CONTROL_REQUEST_HEADERS, "X-Not-Allowed") - .to_srv_request(); - - assert!(cors.inner.validate_allowed_method(req.head()).is_err()); - assert!(cors.inner.validate_allowed_headers(req.head()).is_err()); - let resp = test::call_service(&mut cors, req).await; - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); - - let req = TestRequest::with_header("Origin", "https://www.example.com") - .header(header::ACCESS_CONTROL_REQUEST_METHOD, "put") - .method(Method::OPTIONS) - .to_srv_request(); - - assert!(cors.inner.validate_allowed_method(req.head()).is_err()); - assert!(cors.inner.validate_allowed_headers(req.head()).is_ok()); - - let req = TestRequest::with_header("Origin", "https://www.example.com") - .header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST") - .header( - header::ACCESS_CONTROL_REQUEST_HEADERS, - "AUTHORIZATION,ACCEPT", - ) - .method(Method::OPTIONS) - .to_srv_request(); - - let resp = test::call_service(&mut cors, req).await; - assert_eq!( - &b"*"[..], - resp.headers() - .get(&header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); - assert_eq!( - &b"3600"[..], - resp.headers() - .get(&header::ACCESS_CONTROL_MAX_AGE) - .unwrap() - .as_bytes() - ); - let hdr = resp - .headers() - .get(&header::ACCESS_CONTROL_ALLOW_HEADERS) - .unwrap() - .to_str() - .unwrap(); - assert!(hdr.contains("authorization")); - assert!(hdr.contains("accept")); - assert!(hdr.contains("content-type")); - - let methods = resp - .headers() - .get(header::ACCESS_CONTROL_ALLOW_METHODS) - .unwrap() - .to_str() - .unwrap(); - assert!(methods.contains("POST")); - assert!(methods.contains("GET")); - assert!(methods.contains("OPTIONS")); - - Rc::get_mut(&mut cors.inner).unwrap().preflight = false; - - let req = TestRequest::with_header("Origin", "https://www.example.com") - .header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST") - .header( - header::ACCESS_CONTROL_REQUEST_HEADERS, - "AUTHORIZATION,ACCEPT", - ) - .method(Method::OPTIONS) - .to_srv_request(); - - let resp = test::call_service(&mut cors, req).await; - assert_eq!(resp.status(), StatusCode::OK); - } - - // #[actix_rt::test] - // #[should_panic(expected = "MissingOrigin")] - // async fn test_validate_missing_origin() { - // let cors = Cors::build() - // .allowed_origin("https://www.example.com") - // .finish(); - // let mut req = HttpRequest::default(); - // cors.start(&req).unwrap(); - // } - - #[actix_rt::test] - #[should_panic(expected = "OriginNotAllowed")] - async fn test_validate_not_allowed_origin() { - let cors = Cors::new() - .allowed_origin("https://www.example.com") - .finish() - .new_transform(test::ok_service()) - .await - .unwrap(); - - let req = TestRequest::with_header("Origin", "https://www.unknown.com") - .method(Method::GET) - .to_srv_request(); - cors.inner.validate_origin(req.head()).unwrap(); - cors.inner.validate_allowed_method(req.head()).unwrap(); - cors.inner.validate_allowed_headers(req.head()).unwrap(); - } - - #[actix_rt::test] - async fn test_validate_origin() { - let mut cors = Cors::new() - .allowed_origin("https://www.example.com") - .finish() - .new_transform(test::ok_service()) - .await - .unwrap(); - - let req = TestRequest::with_header("Origin", "https://www.example.com") - .method(Method::GET) - .to_srv_request(); - - let resp = test::call_service(&mut cors, req).await; - assert_eq!(resp.status(), StatusCode::OK); - } - - #[actix_rt::test] - async fn test_no_origin_response() { - let mut cors = Cors::new() - .disable_preflight() - .finish() - .new_transform(test::ok_service()) - .await - .unwrap(); - - let req = TestRequest::default().method(Method::GET).to_srv_request(); - let resp = test::call_service(&mut cors, req).await; - assert!(resp - .headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .is_none()); - - let req = TestRequest::with_header("Origin", "https://www.example.com") - .method(Method::OPTIONS) - .to_srv_request(); - let resp = test::call_service(&mut cors, req).await; - assert_eq!( - &b"https://www.example.com"[..], - resp.headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); - } - - #[actix_rt::test] - async fn test_response() { - let exposed_headers = vec![header::AUTHORIZATION, header::ACCEPT]; - let mut cors = Cors::new() - .send_wildcard() - .disable_preflight() - .max_age(3600) - .allowed_methods(vec![Method::GET, Method::OPTIONS, Method::POST]) - .allowed_headers(exposed_headers.clone()) - .expose_headers(exposed_headers.clone()) - .allowed_header(header::CONTENT_TYPE) - .finish() - .new_transform(test::ok_service()) - .await - .unwrap(); - - let req = TestRequest::with_header("Origin", "https://www.example.com") - .method(Method::OPTIONS) - .to_srv_request(); - - let resp = test::call_service(&mut cors, req).await; - assert_eq!( - &b"*"[..], - resp.headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); - assert_eq!( - &b"Origin"[..], - resp.headers().get(header::VARY).unwrap().as_bytes() - ); - - { - let headers = resp - .headers() - .get(header::ACCESS_CONTROL_EXPOSE_HEADERS) - .unwrap() - .to_str() - .unwrap() - .split(',') - .map(|s| s.trim()) - .collect::>(); - - for h in exposed_headers { - assert!(headers.contains(&h.as_str())); - } - } - - let exposed_headers = vec![header::AUTHORIZATION, header::ACCEPT]; - let mut cors = Cors::new() - .send_wildcard() - .disable_preflight() - .max_age(3600) - .allowed_methods(vec![Method::GET, Method::OPTIONS, Method::POST]) - .allowed_headers(exposed_headers.clone()) - .expose_headers(exposed_headers.clone()) - .allowed_header(header::CONTENT_TYPE) - .finish() - .new_transform(fn_service(|req: ServiceRequest| { - ok(req.into_response( - HttpResponse::Ok().header(header::VARY, "Accept").finish(), - )) - })) - .await - .unwrap(); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .method(Method::OPTIONS) - .to_srv_request(); - let resp = test::call_service(&mut cors, req).await; - assert_eq!( - &b"Accept, Origin"[..], - resp.headers().get(header::VARY).unwrap().as_bytes() - ); - - let mut cors = Cors::new() - .disable_vary_header() - .allowed_origin("https://www.example.com") - .allowed_origin("https://www.google.com") - .finish() - .new_transform(test::ok_service()) - .await - .unwrap(); - - let req = TestRequest::with_header("Origin", "https://www.example.com") - .method(Method::OPTIONS) - .header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST") - .to_srv_request(); - let resp = test::call_service(&mut cors, req).await; - - let origins_str = resp - .headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .to_str() - .unwrap(); - - assert_eq!("https://www.example.com", origins_str); - } - - #[actix_rt::test] - async fn test_multiple_origins() { - let mut cors = Cors::new() - .allowed_origin("https://example.com") - .allowed_origin("https://example.org") - .allowed_methods(vec![Method::GET]) - .finish() - .new_transform(test::ok_service()) - .await - .unwrap(); - - let req = TestRequest::with_header("Origin", "https://example.com") - .method(Method::GET) - .to_srv_request(); - - let resp = test::call_service(&mut cors, req).await; - assert_eq!( - &b"https://example.com"[..], - resp.headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); - - let req = TestRequest::with_header("Origin", "https://example.org") - .method(Method::GET) - .to_srv_request(); - - let resp = test::call_service(&mut cors, req).await; - assert_eq!( - &b"https://example.org"[..], - resp.headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); - } - - #[actix_rt::test] - async fn test_multiple_origins_preflight() { - let mut cors = Cors::new() - .allowed_origin("https://example.com") - .allowed_origin("https://example.org") - .allowed_methods(vec![Method::GET]) - .finish() - .new_transform(test::ok_service()) - .await - .unwrap(); - - let req = TestRequest::with_header("Origin", "https://example.com") - .header(header::ACCESS_CONTROL_REQUEST_METHOD, "GET") - .method(Method::OPTIONS) - .to_srv_request(); - - let resp = test::call_service(&mut cors, req).await; - assert_eq!( - &b"https://example.com"[..], - resp.headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); - - let req = TestRequest::with_header("Origin", "https://example.org") - .header(header::ACCESS_CONTROL_REQUEST_METHOD, "GET") - .method(Method::OPTIONS) - .to_srv_request(); - - let resp = test::call_service(&mut cors, req).await; - assert_eq!( - &b"https://example.org"[..], - resp.headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); - } -} diff --git a/actix-identity/CHANGES.md b/actix-identity/CHANGES.md deleted file mode 100644 index 0c9809ea1..000000000 --- a/actix-identity/CHANGES.md +++ /dev/null @@ -1,17 +0,0 @@ -# Changes - -## [Unreleased] - 2020-xx-xx - -* Update the `time` dependency to 0.2.5 - -## [0.2.1] - 2020-01-10 - -* Fix panic with already borrowed: BorrowMutError #1263 - -## [0.2.0] - 2019-12-20 - -* Use actix-web 2.0 - -## [0.1.0] - 2019-06-xx - -* Move identity middleware to separate crate diff --git a/actix-identity/Cargo.toml b/actix-identity/Cargo.toml deleted file mode 100644 index f97b66291..000000000 --- a/actix-identity/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "actix-identity" -version = "0.2.1" -authors = ["Nikolay Kim "] -description = "Identity service for actix web framework." -readme = "README.md" -keywords = ["http", "web", "framework", "async", "futures"] -homepage = "https://actix.rs" -repository = "https://github.com/actix/actix-web.git" -documentation = "https://docs.rs/actix-identity/" -license = "MIT/Apache-2.0" -edition = "2018" - -[lib] -name = "actix_identity" -path = "src/lib.rs" - -[dependencies] -actix-web = { version = "2.0.0", default-features = false, features = ["secure-cookies"] } -actix-service = "1.0.5" -futures = "0.3.1" -serde = "1.0" -serde_json = "1.0" -time = { version = "0.2.5", default-features = false, features = ["std"] } - -[dev-dependencies] -actix-rt = "1.0.0" -actix-http = "1.0.1" -bytes = "0.5.4" diff --git a/actix-identity/LICENSE-APACHE b/actix-identity/LICENSE-APACHE deleted file mode 120000 index 965b606f3..000000000 --- a/actix-identity/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/actix-identity/LICENSE-MIT b/actix-identity/LICENSE-MIT deleted file mode 120000 index 76219eb72..000000000 --- a/actix-identity/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/actix-identity/src/lib.rs b/actix-identity/src/lib.rs deleted file mode 100644 index b584b1af7..000000000 --- a/actix-identity/src/lib.rs +++ /dev/null @@ -1,1128 +0,0 @@ -//! Request identity service for Actix applications. -//! -//! [**IdentityService**](struct.IdentityService.html) middleware can be -//! used with different policies types to store identity information. -//! -//! By default, only cookie identity policy is implemented. Other backend -//! implementations can be added separately. -//! -//! [**CookieIdentityPolicy**](struct.CookieIdentityPolicy.html) -//! uses cookies as identity storage. -//! -//! To access current request identity -//! [**Identity**](struct.Identity.html) extractor should be used. -//! -//! ```rust -//! use actix_web::*; -//! use actix_identity::{Identity, CookieIdentityPolicy, IdentityService}; -//! -//! async fn index(id: Identity) -> String { -//! // access request identity -//! if let Some(id) = id.identity() { -//! format!("Welcome! {}", id) -//! } else { -//! "Welcome Anonymous!".to_owned() -//! } -//! } -//! -//! async fn login(id: Identity) -> HttpResponse { -//! id.remember("User1".to_owned()); // <- remember identity -//! HttpResponse::Ok().finish() -//! } -//! -//! async fn logout(id: Identity) -> HttpResponse { -//! id.forget(); // <- remove identity -//! HttpResponse::Ok().finish() -//! } -//! -//! fn main() { -//! let app = App::new().wrap(IdentityService::new( -//! // <- create identity middleware -//! CookieIdentityPolicy::new(&[0; 32]) // <- create cookie identity policy -//! .name("auth-cookie") -//! .secure(false))) -//! .service(web::resource("/index.html").to(index)) -//! .service(web::resource("/login.html").to(login)) -//! .service(web::resource("/logout.html").to(logout)); -//! } -//! ``` -use std::cell::RefCell; -use std::future::Future; -use std::rc::Rc; -use std::task::{Context, Poll}; -use std::time::SystemTime; - -use actix_service::{Service, Transform}; -use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; -use serde::{Deserialize, Serialize}; -use time::Duration; - -use actix_web::cookie::{Cookie, CookieJar, Key, SameSite}; -use actix_web::dev::{Extensions, Payload, ServiceRequest, ServiceResponse}; -use actix_web::error::{Error, Result}; -use actix_web::http::header::{self, HeaderValue}; -use actix_web::{FromRequest, HttpMessage, HttpRequest}; - -/// The extractor type to obtain your identity from a request. -/// -/// ```rust -/// use actix_web::*; -/// use actix_identity::Identity; -/// -/// fn index(id: Identity) -> Result { -/// // access request identity -/// if let Some(id) = id.identity() { -/// Ok(format!("Welcome! {}", id)) -/// } else { -/// Ok("Welcome Anonymous!".to_owned()) -/// } -/// } -/// -/// fn login(id: Identity) -> HttpResponse { -/// id.remember("User1".to_owned()); // <- remember identity -/// HttpResponse::Ok().finish() -/// } -/// -/// fn logout(id: Identity) -> HttpResponse { -/// id.forget(); // <- remove identity -/// HttpResponse::Ok().finish() -/// } -/// # fn main() {} -/// ``` -#[derive(Clone)] -pub struct Identity(HttpRequest); - -impl Identity { - /// Return the claimed identity of the user associated request or - /// ``None`` if no identity can be found associated with the request. - pub fn identity(&self) -> Option { - Identity::get_identity(&self.0.extensions()) - } - - /// Remember identity. - pub fn remember(&self, identity: String) { - if let Some(id) = self.0.extensions_mut().get_mut::() { - id.id = Some(identity); - id.changed = true; - } - } - - /// This method is used to 'forget' the current identity on subsequent - /// requests. - pub fn forget(&self) { - if let Some(id) = self.0.extensions_mut().get_mut::() { - id.id = None; - id.changed = true; - } - } - - fn get_identity(extensions: &Extensions) -> Option { - if let Some(id) = extensions.get::() { - id.id.clone() - } else { - None - } - } -} - -struct IdentityItem { - id: Option, - changed: bool, -} - -/// Helper trait that allows to get Identity. -/// -/// It could be used in middleware but identity policy must be set before any other middleware that needs identity -/// RequestIdentity is implemented both for `ServiceRequest` and `HttpRequest`. -pub trait RequestIdentity { - fn get_identity(&self) -> Option; -} - -impl RequestIdentity for T -where - T: HttpMessage, -{ - fn get_identity(&self) -> Option { - Identity::get_identity(&self.extensions()) - } -} - -/// Extractor implementation for Identity type. -/// -/// ```rust -/// # use actix_web::*; -/// use actix_identity::Identity; -/// -/// fn index(id: Identity) -> String { -/// // access request identity -/// if let Some(id) = id.identity() { -/// format!("Welcome! {}", id) -/// } else { -/// "Welcome Anonymous!".to_owned() -/// } -/// } -/// # fn main() {} -/// ``` -impl FromRequest for Identity { - type Config = (); - type Error = Error; - type Future = Ready>; - - #[inline] - fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { - ok(Identity(req.clone())) - } -} - -/// Identity policy definition. -pub trait IdentityPolicy: Sized + 'static { - /// The return type of the middleware - type Future: Future, Error>>; - - /// The return type of the middleware - type ResponseFuture: Future>; - - /// Parse the session from request and load data from a service identity. - fn from_request(&self, request: &mut ServiceRequest) -> Self::Future; - - /// Write changes to response - fn to_response( - &self, - identity: Option, - changed: bool, - response: &mut ServiceResponse, - ) -> Self::ResponseFuture; -} - -/// Request identity middleware -/// -/// ```rust -/// use actix_web::App; -/// use actix_identity::{CookieIdentityPolicy, IdentityService}; -/// -/// fn main() { -/// let app = App::new().wrap(IdentityService::new( -/// // <- create identity middleware -/// CookieIdentityPolicy::new(&[0; 32]) // <- create cookie session backend -/// .name("auth-cookie") -/// .secure(false), -/// )); -/// } -/// ``` -pub struct IdentityService { - backend: Rc, -} - -impl IdentityService { - /// Create new identity service with specified backend. - pub fn new(backend: T) -> Self { - IdentityService { - backend: Rc::new(backend), - } - } -} - -impl Transform for IdentityService -where - S: Service, Error = Error> - + 'static, - S::Future: 'static, - T: IdentityPolicy, - B: 'static, -{ - type Request = ServiceRequest; - type Response = ServiceResponse; - type Error = Error; - type InitError = (); - type Transform = IdentityServiceMiddleware; - type Future = Ready>; - - fn new_transform(&self, service: S) -> Self::Future { - ok(IdentityServiceMiddleware { - backend: self.backend.clone(), - service: Rc::new(RefCell::new(service)), - }) - } -} - -#[doc(hidden)] -pub struct IdentityServiceMiddleware { - backend: Rc, - service: Rc>, -} - -impl Clone for IdentityServiceMiddleware { - fn clone(&self) -> Self { - Self { - backend: self.backend.clone(), - service: self.service.clone(), - } - } -} - -impl Service for IdentityServiceMiddleware -where - B: 'static, - S: Service, Error = Error> - + 'static, - S::Future: 'static, - T: IdentityPolicy, -{ - type Request = ServiceRequest; - type Response = ServiceResponse; - type Error = Error; - type Future = LocalBoxFuture<'static, Result>; - - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { - self.service.borrow_mut().poll_ready(cx) - } - - fn call(&mut self, mut req: ServiceRequest) -> Self::Future { - let srv = self.service.clone(); - let backend = self.backend.clone(); - let fut = self.backend.from_request(&mut req); - - async move { - match fut.await { - Ok(id) => { - req.extensions_mut() - .insert(IdentityItem { id, changed: false }); - - // https://github.com/actix/actix-web/issues/1263 - let fut = { srv.borrow_mut().call(req) }; - let mut res = fut.await?; - let id = res.request().extensions_mut().remove::(); - - if let Some(id) = id { - match backend.to_response(id.id, id.changed, &mut res).await { - Ok(_) => Ok(res), - Err(e) => Ok(res.error_response(e)), - } - } else { - Ok(res) - } - } - Err(err) => Ok(req.error_response(err)), - } - } - .boxed_local() - } -} - -struct CookieIdentityInner { - key: Key, - key_v2: Key, - name: String, - path: String, - domain: Option, - secure: bool, - max_age: Option, - same_site: Option, - visit_deadline: Option, - login_deadline: Option, -} - -#[derive(Deserialize, Serialize, Debug)] -struct CookieValue { - identity: String, - #[serde(skip_serializing_if = "Option::is_none")] - login_timestamp: Option, - #[serde(skip_serializing_if = "Option::is_none")] - visit_timestamp: Option, -} - -#[derive(Debug)] -struct CookieIdentityExtention { - login_timestamp: Option, -} - -impl CookieIdentityInner { - fn new(key: &[u8]) -> CookieIdentityInner { - let key_v2: Vec = key.iter().chain([1, 0, 0, 0].iter()).cloned().collect(); - CookieIdentityInner { - key: Key::from_master(key), - key_v2: Key::from_master(&key_v2), - name: "actix-identity".to_owned(), - path: "/".to_owned(), - domain: None, - secure: true, - max_age: None, - same_site: None, - visit_deadline: None, - login_deadline: None, - } - } - - fn set_cookie( - &self, - resp: &mut ServiceResponse, - value: Option, - ) -> Result<()> { - let add_cookie = value.is_some(); - let val = value.map(|val| { - if !self.legacy_supported() { - serde_json::to_string(&val) - } else { - Ok(val.identity) - } - }); - let mut cookie = - Cookie::new(self.name.clone(), val.unwrap_or_else(|| Ok(String::new()))?); - cookie.set_path(self.path.clone()); - cookie.set_secure(self.secure); - cookie.set_http_only(true); - - if let Some(ref domain) = self.domain { - cookie.set_domain(domain.clone()); - } - - if let Some(max_age) = self.max_age { - cookie.set_max_age(max_age); - } - - if let Some(same_site) = self.same_site { - cookie.set_same_site(same_site); - } - - let mut jar = CookieJar::new(); - let key = if self.legacy_supported() { - &self.key - } else { - &self.key_v2 - }; - if add_cookie { - jar.private(&key).add(cookie); - } else { - jar.add_original(cookie.clone()); - jar.private(&key).remove(cookie); - } - for cookie in jar.delta() { - let val = HeaderValue::from_str(&cookie.to_string())?; - resp.headers_mut().append(header::SET_COOKIE, val); - } - Ok(()) - } - - fn load(&self, req: &ServiceRequest) -> Option { - let cookie = req.cookie(&self.name)?; - let mut jar = CookieJar::new(); - jar.add_original(cookie.clone()); - let res = if self.legacy_supported() { - jar.private(&self.key).get(&self.name).map(|n| CookieValue { - identity: n.value().to_string(), - login_timestamp: None, - visit_timestamp: None, - }) - } else { - None - }; - res.or_else(|| { - jar.private(&self.key_v2) - .get(&self.name) - .and_then(|c| self.parse(c)) - }) - } - - fn parse(&self, cookie: Cookie) -> Option { - let value: CookieValue = serde_json::from_str(cookie.value()).ok()?; - let now = SystemTime::now(); - if let Some(visit_deadline) = self.visit_deadline { - if now.duration_since(value.visit_timestamp?).ok()? - > visit_deadline - { - return None; - } - } - if let Some(login_deadline) = self.login_deadline { - if now.duration_since(value.login_timestamp?).ok()? - > login_deadline - { - return None; - } - } - Some(value) - } - - fn legacy_supported(&self) -> bool { - self.visit_deadline.is_none() && self.login_deadline.is_none() - } - - fn always_update_cookie(&self) -> bool { - self.visit_deadline.is_some() - } - - fn requires_oob_data(&self) -> bool { - self.login_deadline.is_some() - } -} - -/// Use cookies for request identity storage. -/// -/// The constructors take a key as an argument. -/// This is the private key for cookie - when this value is changed, -/// all identities are lost. The constructors will panic if the key is less -/// than 32 bytes in length. -/// -/// # Example -/// -/// ```rust -/// use actix_web::App; -/// use actix_identity::{CookieIdentityPolicy, IdentityService}; -/// -/// fn main() { -/// let app = App::new().wrap(IdentityService::new( -/// // <- create identity middleware -/// CookieIdentityPolicy::new(&[0; 32]) // <- construct cookie policy -/// .domain("www.rust-lang.org") -/// .name("actix_auth") -/// .path("/") -/// .secure(true), -/// )); -/// } -/// ``` -pub struct CookieIdentityPolicy(Rc); - -impl CookieIdentityPolicy { - /// Construct new `CookieIdentityPolicy` instance. - /// - /// Panics if key length is less than 32 bytes. - pub fn new(key: &[u8]) -> CookieIdentityPolicy { - CookieIdentityPolicy(Rc::new(CookieIdentityInner::new(key))) - } - - /// Sets the `path` field in the session cookie being built. - pub fn path>(mut self, value: S) -> CookieIdentityPolicy { - Rc::get_mut(&mut self.0).unwrap().path = value.into(); - self - } - - /// Sets the `name` field in the session cookie being built. - pub fn name>(mut self, value: S) -> CookieIdentityPolicy { - Rc::get_mut(&mut self.0).unwrap().name = value.into(); - self - } - - /// Sets the `domain` field in the session cookie being built. - pub fn domain>(mut self, value: S) -> CookieIdentityPolicy { - Rc::get_mut(&mut self.0).unwrap().domain = Some(value.into()); - self - } - - /// Sets the `secure` field in the session cookie being built. - /// - /// If the `secure` field is set, a cookie will only be transmitted when the - /// connection is secure - i.e. `https` - pub fn secure(mut self, value: bool) -> CookieIdentityPolicy { - Rc::get_mut(&mut self.0).unwrap().secure = value; - self - } - - /// Sets the `max-age` field in the session cookie being built with given number of seconds. - pub fn max_age(self, seconds: i64) -> CookieIdentityPolicy { - self.max_age_time(Duration::seconds(seconds)) - } - - /// Sets the `max-age` field in the session cookie being built with `chrono::Duration`. - pub fn max_age_time(mut self, value: Duration) -> CookieIdentityPolicy { - Rc::get_mut(&mut self.0).unwrap().max_age = Some(value); - self - } - - /// Sets the `same_site` field in the session cookie being built. - pub fn same_site(mut self, same_site: SameSite) -> Self { - Rc::get_mut(&mut self.0).unwrap().same_site = Some(same_site); - self - } - - /// Accepts only users whose cookie has been seen before the given deadline - /// - /// By default visit deadline is disabled. - pub fn visit_deadline(mut self, value: Duration) -> CookieIdentityPolicy { - Rc::get_mut(&mut self.0).unwrap().visit_deadline = Some(value); - self - } - - /// Accepts only users which has been authenticated before the given deadline - /// - /// By default login deadline is disabled. - pub fn login_deadline(mut self, value: Duration) -> CookieIdentityPolicy { - Rc::get_mut(&mut self.0).unwrap().login_deadline = Some(value); - self - } -} - -impl IdentityPolicy for CookieIdentityPolicy { - type Future = Ready, Error>>; - type ResponseFuture = Ready>; - - fn from_request(&self, req: &mut ServiceRequest) -> Self::Future { - ok(self.0.load(req).map( - |CookieValue { - identity, - login_timestamp, - .. - }| { - if self.0.requires_oob_data() { - req.extensions_mut() - .insert(CookieIdentityExtention { login_timestamp }); - } - identity - }, - )) - } - - fn to_response( - &self, - id: Option, - changed: bool, - res: &mut ServiceResponse, - ) -> Self::ResponseFuture { - let _ = if changed { - let login_timestamp = SystemTime::now(); - self.0.set_cookie( - res, - id.map(|identity| CookieValue { - identity, - login_timestamp: self.0.login_deadline.map(|_| login_timestamp), - visit_timestamp: self.0.visit_deadline.map(|_| login_timestamp), - }), - ) - } else if self.0.always_update_cookie() && id.is_some() { - let visit_timestamp = SystemTime::now(); - let login_timestamp = if self.0.requires_oob_data() { - let CookieIdentityExtention { - login_timestamp: lt, - } = res.request().extensions_mut().remove().unwrap(); - lt - } else { - None - }; - self.0.set_cookie( - res, - Some(CookieValue { - identity: id.unwrap(), - login_timestamp, - visit_timestamp: self.0.visit_deadline.map(|_| visit_timestamp), - }), - ) - } else { - Ok(()) - }; - ok(()) - } -} - -#[cfg(test)] -mod tests { - use std::borrow::Borrow; - - use super::*; - use actix_service::into_service; - use actix_web::http::StatusCode; - use actix_web::test::{self, TestRequest}; - use actix_web::{error, web, App, Error, HttpResponse}; - - const COOKIE_KEY_MASTER: [u8; 32] = [0; 32]; - const COOKIE_NAME: &'static str = "actix_auth"; - const COOKIE_LOGIN: &'static str = "test"; - - #[actix_rt::test] - async fn test_identity() { - let mut srv = test::init_service( - App::new() - .wrap(IdentityService::new( - CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) - .domain("www.rust-lang.org") - .name(COOKIE_NAME) - .path("/") - .secure(true), - )) - .service(web::resource("/index").to(|id: Identity| { - if id.identity().is_some() { - HttpResponse::Created() - } else { - HttpResponse::Ok() - } - })) - .service(web::resource("/login").to(|id: Identity| { - id.remember(COOKIE_LOGIN.to_string()); - HttpResponse::Ok() - })) - .service(web::resource("/logout").to(|id: Identity| { - if id.identity().is_some() { - id.forget(); - HttpResponse::Ok() - } else { - HttpResponse::BadRequest() - } - })), - ) - .await; - let resp = - test::call_service(&mut srv, TestRequest::with_uri("/index").to_request()) - .await; - assert_eq!(resp.status(), StatusCode::OK); - - let resp = - test::call_service(&mut srv, TestRequest::with_uri("/login").to_request()) - .await; - assert_eq!(resp.status(), StatusCode::OK); - let c = resp.response().cookies().next().unwrap().to_owned(); - - let resp = test::call_service( - &mut srv, - TestRequest::with_uri("/index") - .cookie(c.clone()) - .to_request(), - ) - .await; - assert_eq!(resp.status(), StatusCode::CREATED); - - let resp = test::call_service( - &mut srv, - TestRequest::with_uri("/logout") - .cookie(c.clone()) - .to_request(), - ) - .await; - assert_eq!(resp.status(), StatusCode::OK); - assert!(resp.headers().contains_key(header::SET_COOKIE)) - } - - #[actix_rt::test] - async fn test_identity_max_age_time() { - let duration = Duration::days(1); - let mut srv = test::init_service( - App::new() - .wrap(IdentityService::new( - CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) - .domain("www.rust-lang.org") - .name(COOKIE_NAME) - .path("/") - .max_age_time(duration) - .secure(true), - )) - .service(web::resource("/login").to(|id: Identity| { - id.remember("test".to_string()); - HttpResponse::Ok() - })), - ) - .await; - let resp = - test::call_service(&mut srv, TestRequest::with_uri("/login").to_request()) - .await; - assert_eq!(resp.status(), StatusCode::OK); - assert!(resp.headers().contains_key(header::SET_COOKIE)); - let c = resp.response().cookies().next().unwrap().to_owned(); - assert_eq!(duration, c.max_age().unwrap()); - } - - #[actix_rt::test] - async fn test_identity_max_age() { - let seconds = 60; - let mut srv = test::init_service( - App::new() - .wrap(IdentityService::new( - CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) - .domain("www.rust-lang.org") - .name(COOKIE_NAME) - .path("/") - .max_age(seconds) - .secure(true), - )) - .service(web::resource("/login").to(|id: Identity| { - id.remember("test".to_string()); - HttpResponse::Ok() - })), - ) - .await; - let resp = - test::call_service(&mut srv, TestRequest::with_uri("/login").to_request()) - .await; - assert_eq!(resp.status(), StatusCode::OK); - assert!(resp.headers().contains_key(header::SET_COOKIE)); - let c = resp.response().cookies().next().unwrap().to_owned(); - assert_eq!(Duration::seconds(seconds as i64), c.max_age().unwrap()); - } - - async fn create_identity_server< - F: Fn(CookieIdentityPolicy) -> CookieIdentityPolicy + Sync + Send + Clone + 'static, - >( - f: F, - ) -> impl actix_service::Service< - Request = actix_http::Request, - Response = ServiceResponse, - Error = Error, - > { - test::init_service( - App::new() - .wrap(IdentityService::new(f(CookieIdentityPolicy::new( - &COOKIE_KEY_MASTER, - ) - .secure(false) - .name(COOKIE_NAME)))) - .service(web::resource("/").to(|id: Identity| { - async move { - let identity = id.identity(); - if identity.is_none() { - id.remember(COOKIE_LOGIN.to_string()) - } - web::Json(identity) - } - })), - ) - .await - } - - fn legacy_login_cookie(identity: &'static str) -> Cookie<'static> { - let mut jar = CookieJar::new(); - jar.private(&Key::from_master(&COOKIE_KEY_MASTER)) - .add(Cookie::new(COOKIE_NAME, identity)); - jar.get(COOKIE_NAME).unwrap().clone() - } - - fn login_cookie( - identity: &'static str, - login_timestamp: Option, - visit_timestamp: Option, - ) -> Cookie<'static> { - let mut jar = CookieJar::new(); - let key: Vec = COOKIE_KEY_MASTER - .iter() - .chain([1, 0, 0, 0].iter()) - .map(|e| *e) - .collect(); - jar.private(&Key::from_master(&key)).add(Cookie::new( - COOKIE_NAME, - serde_json::to_string(&CookieValue { - identity: identity.to_string(), - login_timestamp, - visit_timestamp, - }) - .unwrap(), - )); - jar.get(COOKIE_NAME).unwrap().clone() - } - - async fn assert_logged_in(response: ServiceResponse, identity: Option<&str>) { - let bytes = test::read_body(response).await; - let resp: Option = serde_json::from_slice(&bytes[..]).unwrap(); - assert_eq!(resp.as_ref().map(|s| s.borrow()), identity); - } - - fn assert_legacy_login_cookie(response: &mut ServiceResponse, identity: &str) { - let mut cookies = CookieJar::new(); - for cookie in response.headers().get_all(header::SET_COOKIE) { - cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap()); - } - let cookie = cookies - .private(&Key::from_master(&COOKIE_KEY_MASTER)) - .get(COOKIE_NAME) - .unwrap(); - assert_eq!(cookie.value(), identity); - } - - enum LoginTimestampCheck { - NoTimestamp, - NewTimestamp, - OldTimestamp(SystemTime), - } - - enum VisitTimeStampCheck { - NoTimestamp, - NewTimestamp, - } - - fn assert_login_cookie( - response: &mut ServiceResponse, - identity: &str, - login_timestamp: LoginTimestampCheck, - visit_timestamp: VisitTimeStampCheck, - ) { - let mut cookies = CookieJar::new(); - for cookie in response.headers().get_all(header::SET_COOKIE) { - cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap()); - } - let key: Vec = COOKIE_KEY_MASTER - .iter() - .chain([1, 0, 0, 0].iter()) - .map(|e| *e) - .collect(); - let cookie = cookies - .private(&Key::from_master(&key)) - .get(COOKIE_NAME) - .unwrap(); - let cv: CookieValue = serde_json::from_str(cookie.value()).unwrap(); - assert_eq!(cv.identity, identity); - let now = SystemTime::now(); - let t30sec_ago = now - Duration::seconds(30); - match login_timestamp { - LoginTimestampCheck::NoTimestamp => assert_eq!(cv.login_timestamp, None), - LoginTimestampCheck::NewTimestamp => assert!( - t30sec_ago <= cv.login_timestamp.unwrap() - && cv.login_timestamp.unwrap() <= now - ), - LoginTimestampCheck::OldTimestamp(old_timestamp) => { - assert_eq!(cv.login_timestamp, Some(old_timestamp)) - } - } - match visit_timestamp { - VisitTimeStampCheck::NoTimestamp => assert_eq!(cv.visit_timestamp, None), - VisitTimeStampCheck::NewTimestamp => assert!( - t30sec_ago <= cv.visit_timestamp.unwrap() - && cv.visit_timestamp.unwrap() <= now - ), - } - } - - fn assert_no_login_cookie(response: &mut ServiceResponse) { - let mut cookies = CookieJar::new(); - for cookie in response.headers().get_all(header::SET_COOKIE) { - cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap()); - } - assert!(cookies.get(COOKIE_NAME).is_none()); - } - - #[actix_rt::test] - async fn test_identity_legacy_cookie_is_set() { - let mut srv = create_identity_server(|c| c).await; - let mut resp = - test::call_service(&mut srv, TestRequest::with_uri("/").to_request()).await; - assert_legacy_login_cookie(&mut resp, COOKIE_LOGIN); - assert_logged_in(resp, None).await; - } - - #[actix_rt::test] - async fn test_identity_legacy_cookie_works() { - let mut srv = create_identity_server(|c| c).await; - let cookie = legacy_login_cookie(COOKIE_LOGIN); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ) - .await; - assert_no_login_cookie(&mut resp); - assert_logged_in(resp, Some(COOKIE_LOGIN)).await; - } - - #[actix_rt::test] - async fn test_identity_legacy_cookie_rejected_if_visit_timestamp_needed() { - let mut srv = - create_identity_server(|c| c.visit_deadline(Duration::days(90))).await; - let cookie = legacy_login_cookie(COOKIE_LOGIN); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ) - .await; - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::NoTimestamp, - VisitTimeStampCheck::NewTimestamp, - ); - assert_logged_in(resp, None).await; - } - - #[actix_rt::test] - async fn test_identity_legacy_cookie_rejected_if_login_timestamp_needed() { - let mut srv = - create_identity_server(|c| c.login_deadline(Duration::days(90))).await; - let cookie = legacy_login_cookie(COOKIE_LOGIN); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ) - .await; - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::NewTimestamp, - VisitTimeStampCheck::NoTimestamp, - ); - assert_logged_in(resp, None).await; - } - - #[actix_rt::test] - async fn test_identity_cookie_rejected_if_login_timestamp_needed() { - let mut srv = - create_identity_server(|c| c.login_deadline(Duration::days(90))).await; - let cookie = login_cookie(COOKIE_LOGIN, None, Some(SystemTime::now())); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ) - .await; - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::NewTimestamp, - VisitTimeStampCheck::NoTimestamp, - ); - assert_logged_in(resp, None).await; - } - - #[actix_rt::test] - async fn test_identity_cookie_rejected_if_visit_timestamp_needed() { - let mut srv = - create_identity_server(|c| c.visit_deadline(Duration::days(90))).await; - let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ) - .await; - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::NoTimestamp, - VisitTimeStampCheck::NewTimestamp, - ); - assert_logged_in(resp, None).await; - } - - #[actix_rt::test] - async fn test_identity_cookie_rejected_if_login_timestamp_too_old() { - let mut srv = - create_identity_server(|c| c.login_deadline(Duration::days(90))).await; - let cookie = login_cookie( - COOKIE_LOGIN, - Some(SystemTime::now() - Duration::days(180)), - None, - ); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ) - .await; - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::NewTimestamp, - VisitTimeStampCheck::NoTimestamp, - ); - assert_logged_in(resp, None).await; - } - - #[actix_rt::test] - async fn test_identity_cookie_rejected_if_visit_timestamp_too_old() { - let mut srv = - create_identity_server(|c| c.visit_deadline(Duration::days(90))).await; - let cookie = login_cookie( - COOKIE_LOGIN, - None, - Some(SystemTime::now() - Duration::days(180)), - ); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ) - .await; - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::NoTimestamp, - VisitTimeStampCheck::NewTimestamp, - ); - assert_logged_in(resp, None).await; - } - - #[actix_rt::test] - async fn test_identity_cookie_not_updated_on_login_deadline() { - let mut srv = - create_identity_server(|c| c.login_deadline(Duration::days(90))).await; - let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ) - .await; - assert_no_login_cookie(&mut resp); - assert_logged_in(resp, Some(COOKIE_LOGIN)).await; - } - - // https://github.com/actix/actix-web/issues/1263 - #[actix_rt::test] - async fn test_identity_cookie_updated_on_visit_deadline() { - let mut srv = create_identity_server(|c| { - c.visit_deadline(Duration::days(90)) - .login_deadline(Duration::days(90)) - }) - .await; - let timestamp = SystemTime::now() - Duration::days(1); - let cookie = login_cookie(COOKIE_LOGIN, Some(timestamp), Some(timestamp)); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ) - .await; - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::OldTimestamp(timestamp), - VisitTimeStampCheck::NewTimestamp, - ); - assert_logged_in(resp, Some(COOKIE_LOGIN)).await; - } - - #[actix_rt::test] - async fn test_borrowed_mut_error() { - use futures::future::{lazy, ok, Ready}; - - struct Ident; - impl IdentityPolicy for Ident { - type Future = Ready, Error>>; - type ResponseFuture = Ready>; - - fn from_request(&self, _: &mut ServiceRequest) -> Self::Future { - ok(Some("test".to_string())) - } - - fn to_response( - &self, - _: Option, - _: bool, - _: &mut ServiceResponse, - ) -> Self::ResponseFuture { - ok(()) - } - } - - let mut srv = IdentityServiceMiddleware { - backend: Rc::new(Ident), - service: Rc::new(RefCell::new(into_service(|_: ServiceRequest| { - async move { - actix_rt::time::delay_for(std::time::Duration::from_secs(100)).await; - Err::(error::ErrorBadRequest("error")) - } - }))), - }; - - let mut srv2 = srv.clone(); - let req = TestRequest::default().to_srv_request(); - actix_rt::spawn(async move { - let _ = srv2.call(req).await; - }); - actix_rt::time::delay_for(std::time::Duration::from_millis(50)).await; - - let _ = lazy(|cx| srv.poll_ready(cx)).await; - } -} diff --git a/actix-session/CHANGES.md b/actix-session/CHANGES.md deleted file mode 100644 index f6753ae58..000000000 --- a/actix-session/CHANGES.md +++ /dev/null @@ -1,73 +0,0 @@ -# Changes - -## [Unreleased] - 2020-01-xx - -* Update the `time` dependency to 0.2.5 -* [#1292](https://github.com/actix/actix-web/pull/1292) Long lasting auto-prolonged session - -## [0.3.0] - 2019-12-20 - -* Release - -## [0.3.0-alpha.4] - 2019-12-xx - -* Allow access to sessions also from not mutable references to the request - -## [0.3.0-alpha.3] - 2019-12-xx - -* Add access to the session from RequestHead for use of session from guard methods - -* Migrate to `std::future` - -* Migrate to `actix-web` 2.0 - -## [0.2.0] - 2019-07-08 - -* Enhanced ``actix-session`` to facilitate state changes. Use ``Session.renew()`` - at successful login to cycle a session (new key/cookie but keeps state). - Use ``Session.purge()`` at logout to invalid a session cookie (and remove - from redis cache, if applicable). - -## [0.1.1] - 2019-06-03 - -* Fix optional cookie session support - -## [0.1.0] - 2019-05-18 - -* Use actix-web 1.0.0-rc - -## [0.1.0-beta.4] - 2019-05-12 - -* Use actix-web 1.0.0-beta.4 - -## [0.1.0-beta.2] - 2019-04-28 - -* Add helper trait `UserSession` which allows to get session for ServiceRequest and HttpRequest - -## [0.1.0-beta.1] - 2019-04-20 - -* Update actix-web to beta.1 - -* `CookieSession::max_age()` accepts value in seconds - -## [0.1.0-alpha.6] - 2019-04-14 - -* Update actix-web alpha.6 - -## [0.1.0-alpha.4] - 2019-04-08 - -* Update actix-web - -## [0.1.0-alpha.3] - 2019-04-02 - -* Update actix-web - -## [0.1.0-alpha.2] - 2019-03-29 - -* Update actix-web - -* Use new feature name for secure cookies - -## [0.1.0-alpha.1] - 2019-03-28 - -* Initial impl diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml deleted file mode 100644 index b0a89ee29..000000000 --- a/actix-session/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -[package] -name = "actix-session" -version = "0.3.0" -authors = ["Nikolay Kim "] -description = "Session for actix web framework." -readme = "README.md" -keywords = ["http", "web", "framework", "async", "futures"] -homepage = "https://actix.rs" -repository = "https://github.com/actix/actix-web.git" -documentation = "https://docs.rs/actix-session/" -license = "MIT/Apache-2.0" -edition = "2018" - -[lib] -name = "actix_session" -path = "src/lib.rs" - -[features] -default = ["cookie-session"] - -# sessions feature, session require "ring" crate and c compiler -cookie-session = ["actix-web/secure-cookies"] - -[dependencies] -actix-web = { version = "2.0.0" } -actix-service = "1.0.5" -bytes = "0.5.4" -derive_more = "0.99.2" -futures = "0.3.1" -serde = "1.0" -serde_json = "1.0" -time = { version = "0.2.5", default-features = false, features = ["std"] } - -[dev-dependencies] -actix-rt = "1.0.0" diff --git a/actix-session/LICENSE-APACHE b/actix-session/LICENSE-APACHE deleted file mode 120000 index 965b606f3..000000000 --- a/actix-session/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/actix-session/LICENSE-MIT b/actix-session/LICENSE-MIT deleted file mode 120000 index 76219eb72..000000000 --- a/actix-session/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/actix-session/src/cookie.rs b/actix-session/src/cookie.rs deleted file mode 100644 index b5297f561..000000000 --- a/actix-session/src/cookie.rs +++ /dev/null @@ -1,545 +0,0 @@ -//! Cookie session. -//! -//! [**CookieSession**](struct.CookieSession.html) -//! uses cookies as session storage. `CookieSession` creates sessions -//! which are limited to storing fewer than 4000 bytes of data, as the payload -//! must fit into a single cookie. An internal server error is generated if a -//! session contains more than 4000 bytes. -//! -//! A cookie may have a security policy of *signed* or *private*. Each has -//! a respective `CookieSession` constructor. -//! -//! A *signed* cookie may be viewed but not modified by the client. A *private* -//! cookie may neither be viewed nor modified by the client. -//! -//! The constructors take a key as an argument. This is the private key -//! for cookie session - when this value is changed, all session data is lost. - -use std::collections::HashMap; -use std::rc::Rc; -use std::task::{Context, Poll}; - -use actix_service::{Service, Transform}; -use actix_web::cookie::{Cookie, CookieJar, Key, SameSite}; -use actix_web::dev::{ServiceRequest, ServiceResponse}; -use actix_web::http::{header::SET_COOKIE, HeaderValue}; -use actix_web::{Error, HttpMessage, ResponseError}; -use derive_more::{Display, From}; -use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; -use serde_json::error::Error as JsonError; -use time::{Duration, OffsetDateTime}; - -use crate::{Session, SessionStatus}; - -/// Errors that can occur during handling cookie session -#[derive(Debug, From, Display)] -pub enum CookieSessionError { - /// Size of the serialized session is greater than 4000 bytes. - #[display(fmt = "Size of the serialized session is greater than 4000 bytes.")] - Overflow, - /// Fail to serialize session. - #[display(fmt = "Fail to serialize session")] - Serialize(JsonError), -} - -impl ResponseError for CookieSessionError {} - -enum CookieSecurity { - Signed, - Private, -} - -struct CookieSessionInner { - key: Key, - security: CookieSecurity, - name: String, - path: String, - domain: Option, - secure: bool, - http_only: bool, - max_age: Option, - expires_in: Option, - same_site: Option, -} - -impl CookieSessionInner { - fn new(key: &[u8], security: CookieSecurity) -> CookieSessionInner { - CookieSessionInner { - security, - key: Key::from_master(key), - name: "actix-session".to_owned(), - path: "/".to_owned(), - domain: None, - secure: true, - http_only: true, - max_age: None, - expires_in: None, - same_site: None, - } - } - - fn set_cookie( - &self, - res: &mut ServiceResponse, - state: impl Iterator, - ) -> Result<(), Error> { - let state: HashMap = state.collect(); - let value = - serde_json::to_string(&state).map_err(CookieSessionError::Serialize)?; - if value.len() > 4064 { - return Err(CookieSessionError::Overflow.into()); - } - - let mut cookie = Cookie::new(self.name.clone(), value); - cookie.set_path(self.path.clone()); - cookie.set_secure(self.secure); - cookie.set_http_only(self.http_only); - - if let Some(ref domain) = self.domain { - cookie.set_domain(domain.clone()); - } - - if let Some(expires_in) = self.expires_in { - cookie.set_expires(OffsetDateTime::now() + expires_in); - } - - if let Some(max_age) = self.max_age { - cookie.set_max_age(max_age); - } - - if let Some(same_site) = self.same_site { - cookie.set_same_site(same_site); - } - - let mut jar = CookieJar::new(); - - match self.security { - CookieSecurity::Signed => jar.signed(&self.key).add(cookie), - CookieSecurity::Private => jar.private(&self.key).add(cookie), - } - - for cookie in jar.delta() { - let val = HeaderValue::from_str(&cookie.encoded().to_string())?; - res.headers_mut().append(SET_COOKIE, val); - } - - Ok(()) - } - - /// invalidates session cookie - fn remove_cookie(&self, res: &mut ServiceResponse) -> Result<(), Error> { - let mut cookie = Cookie::named(self.name.clone()); - cookie.set_value(""); - cookie.set_max_age(Duration::zero()); - cookie.set_expires(OffsetDateTime::now() - Duration::days(365)); - - let val = HeaderValue::from_str(&cookie.to_string())?; - res.headers_mut().append(SET_COOKIE, val); - - Ok(()) - } - - fn load(&self, req: &ServiceRequest) -> (bool, HashMap) { - if let Ok(cookies) = req.cookies() { - for cookie in cookies.iter() { - if cookie.name() == self.name { - let mut jar = CookieJar::new(); - jar.add_original(cookie.clone()); - - let cookie_opt = match self.security { - CookieSecurity::Signed => jar.signed(&self.key).get(&self.name), - CookieSecurity::Private => { - jar.private(&self.key).get(&self.name) - } - }; - if let Some(cookie) = cookie_opt { - if let Ok(val) = serde_json::from_str(cookie.value()) { - return (false, val); - } - } - } - } - } - (true, HashMap::new()) - } -} - -/// Use cookies for session storage. -/// -/// `CookieSession` creates sessions which are limited to storing -/// fewer than 4000 bytes of data (as the payload must fit into a single -/// cookie). An Internal Server Error is generated if the session contains more -/// than 4000 bytes. -/// -/// A cookie may have a security policy of *signed* or *private*. Each has a -/// respective `CookieSessionBackend` constructor. -/// -/// A *signed* cookie is stored on the client as plaintext alongside -/// a signature such that the cookie may be viewed but not modified by the -/// client. -/// -/// A *private* cookie is stored on the client as encrypted text -/// such that it may neither be viewed nor modified by the client. -/// -/// The constructors take a key as an argument. -/// This is the private key for cookie session - when this value is changed, -/// all session data is lost. The constructors will panic if the key is less -/// than 32 bytes in length. -/// -/// The backend relies on `cookie` crate to create and read cookies. -/// By default all cookies are percent encoded, but certain symbols may -/// cause troubles when reading cookie, if they are not properly percent encoded. -/// -/// # Example -/// -/// ```rust -/// use actix_session::CookieSession; -/// use actix_web::{web, App, HttpResponse, HttpServer}; -/// -/// fn main() { -/// let app = App::new().wrap( -/// CookieSession::signed(&[0; 32]) -/// .domain("www.rust-lang.org") -/// .name("actix_session") -/// .path("/") -/// .secure(true)) -/// .service(web::resource("/").to(|| HttpResponse::Ok())); -/// } -/// ``` -pub struct CookieSession(Rc); - -impl CookieSession { - /// Construct new *signed* `CookieSessionBackend` instance. - /// - /// Panics if key length is less than 32 bytes. - pub fn signed(key: &[u8]) -> CookieSession { - CookieSession(Rc::new(CookieSessionInner::new( - key, - CookieSecurity::Signed, - ))) - } - - /// Construct new *private* `CookieSessionBackend` instance. - /// - /// Panics if key length is less than 32 bytes. - pub fn private(key: &[u8]) -> CookieSession { - CookieSession(Rc::new(CookieSessionInner::new( - key, - CookieSecurity::Private, - ))) - } - - /// Sets the `path` field in the session cookie being built. - pub fn path>(mut self, value: S) -> CookieSession { - Rc::get_mut(&mut self.0).unwrap().path = value.into(); - self - } - - /// Sets the `name` field in the session cookie being built. - pub fn name>(mut self, value: S) -> CookieSession { - Rc::get_mut(&mut self.0).unwrap().name = value.into(); - self - } - - /// Sets the `domain` field in the session cookie being built. - pub fn domain>(mut self, value: S) -> CookieSession { - Rc::get_mut(&mut self.0).unwrap().domain = Some(value.into()); - self - } - - /// Sets the `secure` field in the session cookie being built. - /// - /// If the `secure` field is set, a cookie will only be transmitted when the - /// connection is secure - i.e. `https` - pub fn secure(mut self, value: bool) -> CookieSession { - Rc::get_mut(&mut self.0).unwrap().secure = value; - self - } - - /// Sets the `http_only` field in the session cookie being built. - pub fn http_only(mut self, value: bool) -> CookieSession { - Rc::get_mut(&mut self.0).unwrap().http_only = value; - self - } - - /// Sets the `same_site` field in the session cookie being built. - pub fn same_site(mut self, value: SameSite) -> CookieSession { - Rc::get_mut(&mut self.0).unwrap().same_site = Some(value); - self - } - - /// Sets the `max-age` field in the session cookie being built. - pub fn max_age(self, seconds: i64) -> CookieSession { - self.max_age_time(Duration::seconds(seconds)) - } - - /// Sets the `max-age` field in the session cookie being built. - pub fn max_age_time(mut self, value: time::Duration) -> CookieSession { - Rc::get_mut(&mut self.0).unwrap().max_age = Some(value); - self - } - - /// Sets the `expires` field in the session cookie being built. - pub fn expires_in(self, seconds: i64) -> CookieSession { - self.expires_in_time(Duration::seconds(seconds)) - } - - /// Sets the `expires` field in the session cookie being built. - pub fn expires_in_time(mut self, value: Duration) -> CookieSession { - Rc::get_mut(&mut self.0).unwrap().expires_in = Some(value); - self - } -} - -impl Transform for CookieSession -where - S: Service>, - S::Future: 'static, - S::Error: 'static, -{ - type Request = ServiceRequest; - type Response = ServiceResponse; - type Error = S::Error; - type InitError = (); - type Transform = CookieSessionMiddleware; - type Future = Ready>; - - fn new_transform(&self, service: S) -> Self::Future { - ok(CookieSessionMiddleware { - service, - inner: self.0.clone(), - }) - } -} - -/// Cookie session middleware -pub struct CookieSessionMiddleware { - service: S, - inner: Rc, -} - -impl Service for CookieSessionMiddleware -where - S: Service>, - S::Future: 'static, - S::Error: 'static, -{ - type Request = ServiceRequest; - type Response = ServiceResponse; - type Error = S::Error; - type Future = LocalBoxFuture<'static, Result>; - - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { - self.service.poll_ready(cx) - } - - /// On first request, a new session cookie is returned in response, regardless - /// of whether any session state is set. With subsequent requests, if the - /// session state changes, then set-cookie is returned in response. As - /// a user logs out, call session.purge() to set SessionStatus accordingly - /// and this will trigger removal of the session cookie in the response. - fn call(&mut self, mut req: ServiceRequest) -> Self::Future { - let inner = self.inner.clone(); - let (is_new, state) = self.inner.load(&req); - let prolong_expiration = self.inner.expires_in.is_some(); - Session::set_session(state.into_iter(), &mut req); - - let fut = self.service.call(req); - - async move { - fut.await.map(|mut res| { - match Session::get_changes(&mut res) { - (SessionStatus::Changed, Some(state)) - | (SessionStatus::Renewed, Some(state)) => { - res.checked_expr(|res| inner.set_cookie(res, state)) - } - (SessionStatus::Unchanged, Some(state)) if prolong_expiration => { - res.checked_expr(|res| inner.set_cookie(res, state)) - } - (SessionStatus::Unchanged, _) => - // set a new session cookie upon first request (new client) - { - if is_new { - let state: HashMap = HashMap::new(); - res.checked_expr(|res| { - inner.set_cookie(res, state.into_iter()) - }) - } else { - res - } - } - (SessionStatus::Purged, _) => { - let _ = inner.remove_cookie(&mut res); - res - } - _ => res, - } - }) - } - .boxed_local() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use actix_web::{test, web, App}; - use bytes::Bytes; - - #[actix_rt::test] - async fn cookie_session() { - let mut app = test::init_service( - App::new() - .wrap(CookieSession::signed(&[0; 32]).secure(false)) - .service(web::resource("/").to(|ses: Session| { - async move { - let _ = ses.set("counter", 100); - "test" - } - })), - ) - .await; - - let request = test::TestRequest::get().to_request(); - let response = app.call(request).await.unwrap(); - assert!(response - .response() - .cookies() - .find(|c| c.name() == "actix-session") - .is_some()); - } - - #[actix_rt::test] - async fn private_cookie() { - let mut app = test::init_service( - App::new() - .wrap(CookieSession::private(&[0; 32]).secure(false)) - .service(web::resource("/").to(|ses: Session| { - async move { - let _ = ses.set("counter", 100); - "test" - } - })), - ) - .await; - - let request = test::TestRequest::get().to_request(); - let response = app.call(request).await.unwrap(); - assert!(response - .response() - .cookies() - .find(|c| c.name() == "actix-session") - .is_some()); - } - - #[actix_rt::test] - async fn cookie_session_extractor() { - let mut app = test::init_service( - App::new() - .wrap(CookieSession::signed(&[0; 32]).secure(false)) - .service(web::resource("/").to(|ses: Session| { - async move { - let _ = ses.set("counter", 100); - "test" - } - })), - ) - .await; - - let request = test::TestRequest::get().to_request(); - let response = app.call(request).await.unwrap(); - assert!(response - .response() - .cookies() - .find(|c| c.name() == "actix-session") - .is_some()); - } - - #[actix_rt::test] - async fn basics() { - let mut app = test::init_service( - App::new() - .wrap( - CookieSession::signed(&[0; 32]) - .path("/test/") - .name("actix-test") - .domain("localhost") - .http_only(true) - .same_site(SameSite::Lax) - .max_age(100), - ) - .service(web::resource("/").to(|ses: Session| { - async move { - let _ = ses.set("counter", 100); - "test" - } - })) - .service(web::resource("/test/").to(|ses: Session| { - async move { - let val: usize = ses.get("counter").unwrap().unwrap(); - format!("counter: {}", val) - } - })), - ) - .await; - - let request = test::TestRequest::get().to_request(); - let response = app.call(request).await.unwrap(); - let cookie = response - .response() - .cookies() - .find(|c| c.name() == "actix-test") - .unwrap() - .clone(); - assert_eq!(cookie.path().unwrap(), "/test/"); - - let request = test::TestRequest::with_uri("/test/") - .cookie(cookie) - .to_request(); - let body = test::read_response(&mut app, request).await; - assert_eq!(body, Bytes::from_static(b"counter: 100")); - } - - #[actix_rt::test] - async fn prolong_expiration() { - let mut app = test::init_service( - App::new() - .wrap(CookieSession::signed(&[0; 32]).secure(false).expires_in(60)) - .service(web::resource("/").to(|ses: Session| { - async move { - let _ = ses.set("counter", 100); - "test" - } - })) - .service( - web::resource("/test/") - .to(|| async move { "no-changes-in-session" }), - ), - ) - .await; - - let request = test::TestRequest::get().to_request(); - let response = app.call(request).await.unwrap(); - let expires_1 = response - .response() - .cookies() - .find(|c| c.name() == "actix-session") - .expect("Cookie is set") - .expires() - .expect("Expiration is set"); - - actix_rt::time::delay_for(std::time::Duration::from_secs(1)).await; - - let request = test::TestRequest::with_uri("/test/").to_request(); - let response = app.call(request).await.unwrap(); - let expires_2 = response - .response() - .cookies() - .find(|c| c.name() == "actix-session") - .expect("Cookie is set") - .expires() - .expect("Expiration is set"); - - assert!(expires_2 - expires_1 >= Duration::seconds(1)); - } -} diff --git a/actix-session/src/lib.rs b/actix-session/src/lib.rs deleted file mode 100644 index e2bf0143b..000000000 --- a/actix-session/src/lib.rs +++ /dev/null @@ -1,321 +0,0 @@ -//! User sessions. -//! -//! Actix provides a general solution for session management. Session -//! middlewares could provide different implementations which could -//! be accessed via general session api. -//! -//! By default, only cookie session backend is implemented. Other -//! backend implementations can be added. -//! -//! In general, you insert a *session* middleware and initialize it -//! , such as a `CookieSessionBackend`. To access session data, -//! [*Session*](struct.Session.html) extractor must be used. Session -//! extractor allows us to get or set session data. -//! -//! ```rust,no_run -//! use actix_web::{web, App, HttpServer, HttpResponse, Error}; -//! use actix_session::{Session, CookieSession}; -//! -//! fn index(session: Session) -> Result<&'static str, Error> { -//! // access session data -//! if let Some(count) = session.get::("counter")? { -//! println!("SESSION value: {}", count); -//! session.set("counter", count+1)?; -//! } else { -//! session.set("counter", 1)?; -//! } -//! -//! Ok("Welcome!") -//! } -//! -//! #[actix_rt::main] -//! async fn main() -> std::io::Result<()> { -//! HttpServer::new( -//! || App::new().wrap( -//! CookieSession::signed(&[0; 32]) // <- create cookie based session middleware -//! .secure(false) -//! ) -//! .service(web::resource("/").to(|| HttpResponse::Ok()))) -//! .bind("127.0.0.1:59880")? -//! .run() -//! .await -//! } -//! ``` -use std::cell::RefCell; -use std::collections::HashMap; -use std::rc::Rc; - -use actix_web::dev::{ - Extensions, Payload, RequestHead, ServiceRequest, ServiceResponse, -}; -use actix_web::{Error, FromRequest, HttpMessage, HttpRequest}; -use futures::future::{ok, Ready}; -use serde::de::DeserializeOwned; -use serde::Serialize; -use serde_json; - -#[cfg(feature = "cookie-session")] -mod cookie; -#[cfg(feature = "cookie-session")] -pub use crate::cookie::CookieSession; - -/// The high-level interface you use to modify session data. -/// -/// Session object could be obtained with -/// [`RequestSession::session`](trait.RequestSession.html#tymethod.session) -/// method. `RequestSession` trait is implemented for `HttpRequest`. -/// -/// ```rust -/// use actix_session::Session; -/// use actix_web::*; -/// -/// fn index(session: Session) -> Result<&'static str> { -/// // access session data -/// if let Some(count) = session.get::("counter")? { -/// session.set("counter", count + 1)?; -/// } else { -/// session.set("counter", 1)?; -/// } -/// -/// Ok("Welcome!") -/// } -/// # fn main() {} -/// ``` -pub struct Session(Rc>); - -/// Helper trait that allows to get session -pub trait UserSession { - fn get_session(&self) -> Session; -} - -impl UserSession for HttpRequest { - fn get_session(&self) -> Session { - Session::get_session(&mut *self.extensions_mut()) - } -} - -impl UserSession for ServiceRequest { - fn get_session(&self) -> Session { - Session::get_session(&mut *self.extensions_mut()) - } -} - -impl UserSession for RequestHead { - fn get_session(&self) -> Session { - Session::get_session(&mut *self.extensions_mut()) - } -} - -#[derive(PartialEq, Clone, Debug)] -pub enum SessionStatus { - Changed, - Purged, - Renewed, - Unchanged, -} -impl Default for SessionStatus { - fn default() -> SessionStatus { - SessionStatus::Unchanged - } -} - -#[derive(Default)] -struct SessionInner { - state: HashMap, - pub status: SessionStatus, -} - -impl Session { - /// Get a `value` from the session. - pub fn get(&self, key: &str) -> Result, Error> { - if let Some(s) = self.0.borrow().state.get(key) { - Ok(Some(serde_json::from_str(s)?)) - } else { - Ok(None) - } - } - - /// Set a `value` from the session. - pub fn set(&self, key: &str, value: T) -> Result<(), Error> { - let mut inner = self.0.borrow_mut(); - if inner.status != SessionStatus::Purged { - inner.status = SessionStatus::Changed; - inner - .state - .insert(key.to_owned(), serde_json::to_string(&value)?); - } - Ok(()) - } - - /// Remove value from the session. - pub fn remove(&self, key: &str) { - let mut inner = self.0.borrow_mut(); - if inner.status != SessionStatus::Purged { - inner.status = SessionStatus::Changed; - inner.state.remove(key); - } - } - - /// Clear the session. - pub fn clear(&self) { - let mut inner = self.0.borrow_mut(); - if inner.status != SessionStatus::Purged { - inner.status = SessionStatus::Changed; - inner.state.clear() - } - } - - /// Removes session, both client and server side. - pub fn purge(&self) { - let mut inner = self.0.borrow_mut(); - inner.status = SessionStatus::Purged; - inner.state.clear(); - } - - /// Renews the session key, assigning existing session state to new key. - pub fn renew(&self) { - let mut inner = self.0.borrow_mut(); - if inner.status != SessionStatus::Purged { - inner.status = SessionStatus::Renewed; - } - } - - pub fn set_session( - data: impl Iterator, - req: &mut ServiceRequest, - ) { - let session = Session::get_session(&mut *req.extensions_mut()); - let mut inner = session.0.borrow_mut(); - inner.state.extend(data); - } - - pub fn get_changes( - res: &mut ServiceResponse, - ) -> ( - SessionStatus, - Option>, - ) { - if let Some(s_impl) = res - .request() - .extensions() - .get::>>() - { - let state = std::mem::take(&mut s_impl.borrow_mut().state); - (s_impl.borrow().status.clone(), Some(state.into_iter())) - } else { - (SessionStatus::Unchanged, None) - } - } - - fn get_session(extensions: &mut Extensions) -> Session { - if let Some(s_impl) = extensions.get::>>() { - return Session(Rc::clone(&s_impl)); - } - let inner = Rc::new(RefCell::new(SessionInner::default())); - extensions.insert(inner.clone()); - Session(inner) - } -} - -/// Extractor implementation for Session type. -/// -/// ```rust -/// # use actix_web::*; -/// use actix_session::Session; -/// -/// fn index(session: Session) -> Result<&'static str> { -/// // access session data -/// if let Some(count) = session.get::("counter")? { -/// session.set("counter", count + 1)?; -/// } else { -/// session.set("counter", 1)?; -/// } -/// -/// Ok("Welcome!") -/// } -/// # fn main() {} -/// ``` -impl FromRequest for Session { - type Error = Error; - type Future = Ready>; - type Config = (); - - #[inline] - fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { - ok(Session::get_session(&mut *req.extensions_mut())) - } -} - -#[cfg(test)] -mod tests { - use actix_web::{test, HttpResponse}; - - use super::*; - - #[test] - fn session() { - let mut req = test::TestRequest::default().to_srv_request(); - - Session::set_session( - vec![("key".to_string(), "\"value\"".to_string())].into_iter(), - &mut req, - ); - let session = Session::get_session(&mut *req.extensions_mut()); - let res = session.get::("key").unwrap(); - assert_eq!(res, Some("value".to_string())); - - session.set("key2", "value2".to_string()).unwrap(); - session.remove("key"); - - let mut res = req.into_response(HttpResponse::Ok().finish()); - let (_status, state) = Session::get_changes(&mut res); - let changes: Vec<_> = state.unwrap().collect(); - assert_eq!(changes, [("key2".to_string(), "\"value2\"".to_string())]); - } - - #[test] - fn get_session() { - let mut req = test::TestRequest::default().to_srv_request(); - - Session::set_session( - vec![("key".to_string(), "\"value\"".to_string())].into_iter(), - &mut req, - ); - - let session = req.get_session(); - let res = session.get::("key").unwrap(); - assert_eq!(res, Some("value".to_string())); - } - - #[test] - fn get_session_from_request_head() { - let mut req = test::TestRequest::default().to_srv_request(); - - Session::set_session( - vec![("key".to_string(), "\"value\"".to_string())].into_iter(), - &mut req, - ); - - let session = req.head_mut().get_session(); - let res = session.get::("key").unwrap(); - assert_eq!(res, Some("value".to_string())); - } - - #[test] - fn purge_session() { - let req = test::TestRequest::default().to_srv_request(); - let session = Session::get_session(&mut *req.extensions_mut()); - assert_eq!(session.0.borrow().status, SessionStatus::Unchanged); - session.purge(); - assert_eq!(session.0.borrow().status, SessionStatus::Purged); - } - - #[test] - fn renew_session() { - let req = test::TestRequest::default().to_srv_request(); - let session = Session::get_session(&mut *req.extensions_mut()); - assert_eq!(session.0.borrow().status, SessionStatus::Unchanged); - session.renew(); - assert_eq!(session.0.borrow().status, SessionStatus::Renewed); - } -} From b0866a8a0fbdc728afa49e115934ec78cf59634d Mon Sep 17 00:00:00 2001 From: Omid Rad Date: Sun, 17 May 2020 23:54:42 +0200 Subject: [PATCH 120/157] Actix-files will always send SizedStream (#1496) * Fixes #1384 * There is no need to set no_chunking * Test content-length for static files * Update the tests * Add Changelog * Try to simply fix Windows test issues! Co-authored-by: Rob Ede --- actix-files/CHANGES.md | 1 + actix-files/src/lib.rs | 159 ++++++++++++++------------------------- actix-files/src/named.rs | 7 +- 3 files changed, 63 insertions(+), 104 deletions(-) diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index 7901a392b..22c98d7c5 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -3,6 +3,7 @@ ## [Unreleased] - 2020-xx-xx * Bump minimum supported Rust version to 1.40 +* Support sending Content-Length when Content-Range is specified #1384 ## [0.2.1] - 2019-12-22 diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 6d2da6c73..27ec6c583 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -952,135 +952,92 @@ mod tests { #[actix_rt::test] async fn test_named_file_content_range_headers() { - let mut srv = test::init_service( - App::new().service(Files::new("/test", ".").index_file("tests/test.binary")), - ) - .await; + let srv = test::start(|| { + App::new().service(Files::new("/", ".")) + }); // Valid range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") + let response = srv + .get("/tests/test.binary") .header(header::RANGE, "bytes=10-20") - .to_request(); - - let response = test::call_service(&mut srv, request).await; - let contentrange = response - .headers() - .get(header::CONTENT_RANGE) - .unwrap() - .to_str() + .send() + .await .unwrap(); - - assert_eq!(contentrange, "bytes 10-20/100"); + let content_range = response.headers().get(header::CONTENT_RANGE).unwrap(); + assert_eq!(content_range.to_str().unwrap(), "bytes 10-20/100"); // Invalid range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") + let response = srv + .get("/tests/test.binary") .header(header::RANGE, "bytes=10-5") - .to_request(); - let response = test::call_service(&mut srv, request).await; - - let contentrange = response - .headers() - .get(header::CONTENT_RANGE) - .unwrap() - .to_str() + .send() + .await .unwrap(); - - assert_eq!(contentrange, "bytes */100"); + let content_range = response.headers().get(header::CONTENT_RANGE).unwrap(); + assert_eq!(content_range.to_str().unwrap(), "bytes */100"); } #[actix_rt::test] async fn test_named_file_content_length_headers() { - // use actix_web::body::{MessageBody, ResponseBody}; - - let mut srv = test::init_service( - App::new().service(Files::new("test", ".").index_file("tests/test.binary")), - ) - .await; + let srv = test::start(|| { + App::new().service(Files::new("/", ".")) + }); // Valid range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") + let response = srv + .get("/tests/test.binary") .header(header::RANGE, "bytes=10-20") - .to_request(); - let _response = test::call_service(&mut srv, request).await; + .send() + .await + .unwrap(); + let content_length = response.headers().get(header::CONTENT_LENGTH).unwrap(); + assert_eq!(content_length.to_str().unwrap(), "11"); - // let contentlength = response - // .headers() - // .get(header::CONTENT_LENGTH) - // .unwrap() - // .to_str() - // .unwrap(); - // assert_eq!(contentlength, "11"); - - // Invalid range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") - .header(header::RANGE, "bytes=10-8") - .to_request(); - let response = test::call_service(&mut srv, request).await; - assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE); + // Valid range header, starting from 0 + let response = srv + .get("/tests/test.binary") + .header(header::RANGE, "bytes=0-20") + .send() + .await + .unwrap(); + let content_length = response.headers().get(header::CONTENT_LENGTH).unwrap(); + assert_eq!(content_length.to_str().unwrap(), "21"); // Without range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") - // .no_default_headers() - .to_request(); - let _response = test::call_service(&mut srv, request).await; + let mut response = srv.get("/tests/test.binary").send().await.unwrap(); + let content_length = response.headers().get(header::CONTENT_LENGTH).unwrap(); + assert_eq!(content_length.to_str().unwrap(), "100"); - // let contentlength = response - // .headers() - // .get(header::CONTENT_LENGTH) - // .unwrap() - // .to_str() - // .unwrap(); - // assert_eq!(contentlength, "100"); + // Should be no transfer-encoding + let transfer_encoding = response.headers().get(header::TRANSFER_ENCODING); + assert!(transfer_encoding.is_none()); - // chunked - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") - .to_request(); - let response = test::call_service(&mut srv, request).await; - - // with enabled compression - // { - // let te = response - // .headers() - // .get(header::TRANSFER_ENCODING) - // .unwrap() - // .to_str() - // .unwrap(); - // assert_eq!(te, "chunked"); - // } - - let bytes = test::read_body(response).await; + // Check file contents + let bytes = response.body().await.unwrap(); let data = Bytes::from(fs::read("tests/test.binary").unwrap()); assert_eq!(bytes, data); } #[actix_rt::test] async fn test_head_content_length_headers() { - let mut srv = test::init_service( - App::new().service(Files::new("test", ".").index_file("tests/test.binary")), - ) - .await; + let srv = test::start(|| { + App::new().service(Files::new("/", ".")) + }); - // Valid range header - let request = TestRequest::default() - .method(Method::HEAD) - .uri("/t%65st/tests/test.binary") - .to_request(); - let _response = test::call_service(&mut srv, request).await; + let response = srv + .head("/tests/test.binary") + .send() + .await + .unwrap(); - // TODO: fix check - // let contentlength = response - // .headers() - // .get(header::CONTENT_LENGTH) - // .unwrap() - // .to_str() - // .unwrap(); - // assert_eq!(contentlength, "100"); + let content_length = response + .headers() + .get(header::CONTENT_LENGTH) + .unwrap() + .to_str() + .unwrap(); + + assert_eq!(content_length, "100"); } #[actix_rt::test] diff --git a/actix-files/src/named.rs b/actix-files/src/named.rs index fdb055998..e05316904 100644 --- a/actix-files/src/named.rs +++ b/actix-files/src/named.rs @@ -388,11 +388,12 @@ impl NamedFile { fut: None, counter: 0, }; + if offset != 0 || length != self.md.len() { - Ok(resp.status(StatusCode::PARTIAL_CONTENT).streaming(reader)) - } else { - Ok(resp.body(SizedStream::new(length, reader))) + resp.status(StatusCode::PARTIAL_CONTENT); } + + Ok(resp.body(SizedStream::new(length, reader))) } } From 7e8ea44d5c7882f47161d248eed8abed1e90ba7d Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 18 May 2020 00:42:51 +0100 Subject: [PATCH 121/157] remove needless BodySize::Sized64 variant --- MIGRATION.md | 4 ++++ actix-http/CHANGES.md | 3 +++ actix-http/src/body.rs | 20 +++++++++----------- actix-http/src/client/h2proto.rs | 4 ---- actix-http/src/h1/encoder.rs | 27 ++------------------------- actix-http/src/h2/dispatcher.rs | 4 ---- actix-http/src/helpers.rs | 8 ++++---- 7 files changed, 22 insertions(+), 48 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 672aa253e..cb79e664b 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -3,9 +3,13 @@ * Setting a cookie's SameSite property, explicitly, to `SameSite::None` will now result in `SameSite=None` being sent with the response Set-Cookie header. To create a cookie without a SameSite attribute, remove any calls setting same_site. + * actix-http support for Actors messages was moved to actix-http crate and is enabled with feature `actors` +* `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a + `u64` instead of a `usize`. + ## 2.0.0 * `HttpServer::start()` renamed to `HttpServer::run()`. It also possible to diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index e96b0451d..a383b6a11 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -6,6 +6,9 @@ * Bump minimum supported Rust version to 1.40 +* `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a + `u64` instead of a `usize`. + ### Fixed * Support parsing of `SameSite=None` [#1503] diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index f887b53bb..b821e777f 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -15,8 +15,7 @@ use crate::error::Error; pub enum BodySize { None, Empty, - Sized(usize), - Sized64(u64), + Sized(u64), Stream, } @@ -25,8 +24,7 @@ impl BodySize { match self { BodySize::None | BodySize::Empty - | BodySize::Sized(0) - | BodySize::Sized64(0) => true, + | BodySize::Sized(0) => true, _ => false, } } @@ -170,7 +168,7 @@ impl MessageBody for Body { match self { Body::None => BodySize::None, Body::Empty => BodySize::Empty, - Body::Bytes(ref bin) => BodySize::Sized(bin.len()), + Body::Bytes(ref bin) => BodySize::Sized(bin.len() as u64), Body::Message(ref body) => body.size(), } } @@ -297,7 +295,7 @@ where impl MessageBody for Bytes { fn size(&self) -> BodySize { - BodySize::Sized(self.len()) + BodySize::Sized(self.len() as u64) } fn poll_next( @@ -314,7 +312,7 @@ impl MessageBody for Bytes { impl MessageBody for BytesMut { fn size(&self) -> BodySize { - BodySize::Sized(self.len()) + BodySize::Sized(self.len() as u64) } fn poll_next( @@ -331,7 +329,7 @@ impl MessageBody for BytesMut { impl MessageBody for &'static str { fn size(&self) -> BodySize { - BodySize::Sized(self.len()) + BodySize::Sized(self.len() as u64) } fn poll_next( @@ -350,7 +348,7 @@ impl MessageBody for &'static str { impl MessageBody for Vec { fn size(&self) -> BodySize { - BodySize::Sized(self.len()) + BodySize::Sized(self.len() as u64) } fn poll_next( @@ -367,7 +365,7 @@ impl MessageBody for Vec { impl MessageBody for String { fn size(&self) -> BodySize { - BodySize::Sized(self.len()) + BodySize::Sized(self.len() as u64) } fn poll_next( @@ -458,7 +456,7 @@ where S: Stream> + Unpin, { fn size(&self) -> BodySize { - BodySize::Sized64(self.size) + BodySize::Sized(self.size as u64) } /// Attempts to pull out the next value of the underlying [`Stream`]. diff --git a/actix-http/src/client/h2proto.rs b/actix-http/src/client/h2proto.rs index 2afd2d80b..48ab9fe4a 100644 --- a/actix-http/src/client/h2proto.rs +++ b/actix-http/src/client/h2proto.rs @@ -64,10 +64,6 @@ where CONTENT_LENGTH, HeaderValue::try_from(format!("{}", len)).unwrap(), ), - BodySize::Sized64(len) => req.headers_mut().insert( - CONTENT_LENGTH, - HeaderValue::try_from(format!("{}", len)).unwrap(), - ), }; // Extracting extra headers from RequestHeadType. HeaderMap::new() does not allocate. diff --git a/actix-http/src/h1/encoder.rs b/actix-http/src/h1/encoder.rs index 4689906b4..eb8c337dd 100644 --- a/actix-http/src/h1/encoder.rs +++ b/actix-http/src/h1/encoder.rs @@ -4,7 +4,7 @@ use std::ptr::copy_nonoverlapping; use std::slice::from_raw_parts_mut; use std::{cmp, io}; -use bytes::{buf::BufMutExt, BufMut, BytesMut}; +use bytes::{BufMut, BytesMut}; use crate::body::BodySize; use crate::config::ServiceConfig; @@ -95,15 +95,6 @@ pub(crate) trait MessageType: Sized { } } BodySize::Sized(len) => helpers::write_content_length(len, dst), - BodySize::Sized64(len) => { - if camel_case { - dst.put_slice(b"\r\nContent-Length: "); - } else { - dst.put_slice(b"\r\ncontent-length: "); - } - #[allow(clippy::write_with_newline)] - write!(dst.writer(), "{}\r\n", len)?; - } BodySize::None => dst.put_slice(b"\r\n"), } @@ -338,8 +329,7 @@ impl MessageEncoder { if !head { self.te = match length { BodySize::Empty => TransferEncoding::empty(), - BodySize::Sized(len) => TransferEncoding::length(len as u64), - BodySize::Sized64(len) => TransferEncoding::length(len), + BodySize::Sized(len) => TransferEncoding::length(len), BodySize::Stream => { if message.chunked() && !stream { TransferEncoding::chunked() @@ -582,19 +572,6 @@ mod tests { assert!(data.contains("Content-Type: plain/text\r\n")); assert!(data.contains("Date: date\r\n")); - let _ = head.encode_headers( - &mut bytes, - Version::HTTP_11, - BodySize::Sized64(100), - ConnectionType::KeepAlive, - &ServiceConfig::default(), - ); - let data = - String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap(); - assert!(data.contains("Content-Length: 100\r\n")); - assert!(data.contains("Content-Type: plain/text\r\n")); - assert!(data.contains("Date: date\r\n")); - let mut head = RequestHead::default(); head.set_camel_case_headers(false); head.headers.insert(DATE, HeaderValue::from_static("date")); diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index b07764a03..a189697a2 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -210,10 +210,6 @@ where CONTENT_LENGTH, HeaderValue::try_from(format!("{}", len)).unwrap(), ), - BodySize::Sized64(len) => res.headers_mut().insert( - CONTENT_LENGTH, - HeaderValue::try_from(format!("{}", len)).unwrap(), - ), }; // copy headers diff --git a/actix-http/src/helpers.rs b/actix-http/src/helpers.rs index ff647e72b..5ac6c9be5 100644 --- a/actix-http/src/helpers.rs +++ b/actix-http/src/helpers.rs @@ -30,7 +30,7 @@ pub(crate) fn write_status_line(version: Version, n: u16, bytes: &mut BytesMut) } /// NOTE: bytes object has to contain enough space -pub fn write_content_length(n: usize, bytes: &mut BytesMut) { +pub fn write_content_length(n: u64, bytes: &mut BytesMut) { bytes.put_slice(b"\r\ncontent-length: "); if n < 10 { @@ -96,16 +96,16 @@ pub fn write_content_length(n: usize, bytes: &mut BytesMut) { bytes.put_u8(DIGITS_START + d10); bytes.put_u8(DIGITS_START + d1); } else { - write_usize(n, bytes); + write_u64(n, bytes); } bytes.put_slice(b"\r\n"); } -pub(crate) fn write_usize(n: usize, bytes: &mut BytesMut) { +pub(crate) fn write_u64(n: u64, bytes: &mut BytesMut) { let mut n = n; - // 20 chars is max length of a usize (2^64) + // 20 chars is max length of a u64 (2^64) // digits will be added to the buffer from lsd to msd let mut buf = BytesMut::with_capacity(20); From 32a37b72823fee9ba4664a6f344493eeda9752d4 Mon Sep 17 00:00:00 2001 From: Omid Rad Date: Tue, 19 May 2020 00:46:31 +0200 Subject: [PATCH 122/157] Remove content_length from ResponseBuilder (#1491) * Remove content_length since it'll be overwritten by the response body. FIXES #1439 * Add setting of Content-Length to the no_chunking function * Add changes and migration documentations * Update MIGRATION.md Co-authored-by: Yuki Okushi Co-authored-by: Rob Ede Co-authored-by: Yuki Okushi --- MIGRATION.md | 2 ++ actix-http/CHANGES.md | 4 ++++ actix-http/src/response.rs | 10 +++------- actix-http/tests/test_openssl.rs | 2 +- actix-http/tests/test_rustls.rs | 2 +- actix-http/tests/test_server.rs | 2 +- tests/test_server.rs | 5 ++--- 7 files changed, 14 insertions(+), 13 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index cb79e664b..d2e9735fb 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -6,6 +6,8 @@ * actix-http support for Actors messages was moved to actix-http crate and is enabled with feature `actors` +* content_length function is removed from actix-http. + You can set Content-Length by normally setting the response body or calling no_chunking function. * `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a `u64` instead of a `usize`. diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index a383b6a11..1a71a0bc0 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -5,6 +5,7 @@ ### Changed * Bump minimum supported Rust version to 1.40 +* content_length function is removed, and you can set Content-Length by calling no_chunking function [#1439] * `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a `u64` instead of a `usize`. @@ -13,6 +14,9 @@ * Support parsing of `SameSite=None` [#1503] +[#1439]: https://github.com/actix/actix-web/pull/1439 +[#1503]: https://github.com/actix/actix-web/pull/1503 + ## [2.0.0-alpha.3] - 2020-05-08 ### Fixed diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index 7a9b82df2..9086212f1 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -472,7 +472,9 @@ impl ResponseBuilder { /// Disable chunked transfer encoding for HTTP/1.1 streaming responses. #[inline] - pub fn no_chunking(&mut self) -> &mut Self { + pub fn no_chunking(&mut self, len: u64) -> &mut Self { + self.header(header::CONTENT_LENGTH, len); + if let Some(parts) = parts(&mut self.head, &self.err) { parts.no_chunking(true); } @@ -497,12 +499,6 @@ impl ResponseBuilder { self } - /// Set content length - #[inline] - pub fn content_length(&mut self, len: u64) -> &mut Self { - self.header(header::CONTENT_LENGTH, len) - } - /// Set a cookie /// /// ```rust diff --git a/actix-http/tests/test_openssl.rs b/actix-http/tests/test_openssl.rs index 4af3a0a47..e8aaa7fc4 100644 --- a/actix-http/tests/test_openssl.rs +++ b/actix-http/tests/test_openssl.rs @@ -275,7 +275,7 @@ async fn test_h2_head_binary() { let mut srv = test_server(move || { HttpService::build() .h2(|_| { - ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) + ok::<_, ()>(Response::Ok().body(STR)) }) .openssl(ssl_acceptor()) .map_err(|_| ()) diff --git a/actix-http/tests/test_rustls.rs b/actix-http/tests/test_rustls.rs index 1c5583b08..fff32ac8b 100644 --- a/actix-http/tests/test_rustls.rs +++ b/actix-http/tests/test_rustls.rs @@ -281,7 +281,7 @@ async fn test_h2_head_binary() { let mut srv = test_server(move || { HttpService::build() .h2(|_| { - ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) + ok::<_, ()>(Response::Ok().body(STR)) }) .rustls(ssl_acceptor()) }) diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index 1ec819434..35c71e37a 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -490,7 +490,7 @@ async fn test_h1_head_binary() { let mut srv = test_server(|| { HttpService::build() .h1(|_| { - ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) + ok::<_, ()>(Response::Ok().body(STR)) }) .tcp() }) diff --git a/tests/test_server.rs b/tests/test_server.rs index 1916b372c..a4dfa65a0 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -349,7 +349,7 @@ async fn test_body_br_streaming() { async fn test_head_binary() { let srv = test::start_with(test::config().h1(), || { App::new().service(web::resource("/").route( - web::head().to(move || HttpResponse::Ok().content_length(100).body(STR)), + web::head().to(move || HttpResponse::Ok().body(STR)), )) }); @@ -371,8 +371,7 @@ async fn test_no_chunking() { let srv = test::start_with(test::config().h1(), || { App::new().service(web::resource("/").route(web::to(move || { HttpResponse::Ok() - .no_chunking() - .content_length(STR.len() as u64) + .no_chunking(STR.len() as u64) .streaming(TestBody::new(Bytes::from_static(STR.as_ref()), 24)) }))) }); From 245dd471dd164567c5e44a1b31cbc70419bf0f15 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 18 May 2020 11:41:07 +0900 Subject: [PATCH 123/157] files: Minimize `futures` dependencies --- actix-files/Cargo.toml | 3 ++- actix-files/src/lib.rs | 4 ++-- actix-files/src/named.rs | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 60f92c5d9..ead2b51f5 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -23,7 +23,8 @@ actix-http = "2.0.0-alpha.3" actix-service = "1.0.1" bitflags = "1" bytes = "0.5.3" -futures = "0.3.1" +futures-core = { version = "0.3.5", default-features = false } +futures-util = { version = "0.3.5", default-features = false } derive_more = "0.99.2" log = "0.4" mime = "0.3" diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 27ec6c583..76c68ce25 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -24,8 +24,8 @@ use actix_web::http::header::{self, DispositionType}; use actix_web::http::Method; use actix_web::{web, FromRequest, HttpRequest, HttpResponse}; use bytes::Bytes; -use futures::future::{ok, ready, Either, FutureExt, LocalBoxFuture, Ready}; -use futures::Stream; +use futures_core::Stream; +use futures_util::future::{ok, ready, Either, FutureExt, LocalBoxFuture, Ready}; use mime; use mime_guess::from_ext; use percent_encoding::{utf8_percent_encode, CONTROLS}; diff --git a/actix-files/src/named.rs b/actix-files/src/named.rs index e05316904..6ee561a4b 100644 --- a/actix-files/src/named.rs +++ b/actix-files/src/named.rs @@ -18,7 +18,7 @@ use actix_web::http::header::{ }; use actix_web::http::{ContentEncoding, StatusCode}; use actix_web::{Error, HttpMessage, HttpRequest, HttpResponse, Responder}; -use futures::future::{ready, Ready}; +use futures_util::future::{ready, Ready}; use crate::range::HttpRange; use crate::ChunkedReadFile; From 9bd640773038431ba8dc8a695a8096c3ad51e530 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 18 May 2020 11:41:31 +0900 Subject: [PATCH 124/157] framed: Minimize `futures` dependencies --- actix-framed/Cargo.toml | 2 +- actix-framed/src/app.rs | 2 +- actix-framed/src/helpers.rs | 2 +- actix-framed/src/route.rs | 2 +- actix-framed/src/service.rs | 4 ++-- actix-framed/tests/test_server.rs | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index 94009db6f..ba7a01e0c 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -26,7 +26,7 @@ actix-rt = "1.0.0" actix-http = "2.0.0-alpha.3" bytes = "0.5.3" -futures = "0.3.1" +futures-util = { version = "0.3.5", default-features = false } pin-project = "0.4.6" log = "0.4" diff --git a/actix-framed/src/app.rs b/actix-framed/src/app.rs index e4b91e6c4..2fddbc039 100644 --- a/actix-framed/src/app.rs +++ b/actix-framed/src/app.rs @@ -8,7 +8,7 @@ use actix_http::h1::{Codec, SendResponse}; use actix_http::{Error, Request, Response}; use actix_router::{Path, Router, Url}; use actix_service::{IntoServiceFactory, Service, ServiceFactory}; -use futures::future::{ok, FutureExt, LocalBoxFuture}; +use futures_util::future::{ok, FutureExt, LocalBoxFuture}; use crate::helpers::{BoxedHttpNewService, BoxedHttpService, HttpNewService}; use crate::request::FramedRequest; diff --git a/actix-framed/src/helpers.rs b/actix-framed/src/helpers.rs index 29492e45b..d83736a52 100644 --- a/actix-framed/src/helpers.rs +++ b/actix-framed/src/helpers.rs @@ -2,7 +2,7 @@ use std::task::{Context, Poll}; use actix_http::Error; use actix_service::{Service, ServiceFactory}; -use futures::future::{FutureExt, LocalBoxFuture}; +use futures_util::future::{FutureExt, LocalBoxFuture}; pub(crate) type BoxedHttpService = Box< dyn Service< diff --git a/actix-framed/src/route.rs b/actix-framed/src/route.rs index 793f46273..7b9004b64 100644 --- a/actix-framed/src/route.rs +++ b/actix-framed/src/route.rs @@ -6,7 +6,7 @@ use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite}; use actix_http::{http::Method, Error}; use actix_service::{Service, ServiceFactory}; -use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; +use futures_util::future::{ok, FutureExt, LocalBoxFuture, Ready}; use log::error; use crate::app::HttpServiceFactory; diff --git a/actix-framed/src/service.rs b/actix-framed/src/service.rs index 92393ca75..dd61f298d 100644 --- a/actix-framed/src/service.rs +++ b/actix-framed/src/service.rs @@ -1,3 +1,4 @@ +use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; @@ -9,8 +10,7 @@ use actix_http::h1::{Codec, Message}; use actix_http::ws::{verify_handshake, HandshakeError}; use actix_http::{Request, Response}; use actix_service::{Service, ServiceFactory}; -use futures::future::{err, ok, Either, Ready}; -use futures::Future; +use futures_util::future::{err, ok, Either, Ready}; /// Service that verifies incoming request if it is valid websocket /// upgrade request. In case of error returns `HandshakeError` diff --git a/actix-framed/tests/test_server.rs b/actix-framed/tests/test_server.rs index 150fc10f3..ec6897861 100644 --- a/actix-framed/tests/test_server.rs +++ b/actix-framed/tests/test_server.rs @@ -4,7 +4,7 @@ use actix_http_test::test_server; use actix_service::{pipeline_factory, IntoServiceFactory, ServiceFactory}; use actix_utils::framed::Dispatcher; use bytes::Bytes; -use futures::{future, SinkExt, StreamExt}; +use futures_util::{future, SinkExt, StreamExt}; use actix_framed::{FramedApp, FramedRequest, FramedRoute, SendError, VerifyWebSockets}; From 292af145cb3d8df2cd3296666cdde5c1b3be036e Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 18 May 2020 11:45:26 +0900 Subject: [PATCH 125/157] http: Minimize `futures` dependencies --- actix-http/Cargo.toml | 13 ++++++------- actix-http/examples/echo.rs | 2 +- actix-http/examples/echo2.rs | 2 +- actix-http/examples/hello-world.rs | 2 +- actix-http/src/body.rs | 2 +- actix-http/src/client/connection.rs | 3 ++- actix-http/tests/test_client.rs | 2 +- actix-http/tests/test_openssl.rs | 4 ++-- actix-http/tests/test_rustls.rs | 4 ++-- actix-http/tests/test_server.rs | 4 ++-- actix-http/tests/test_ws.rs | 7 ++++--- 11 files changed, 23 insertions(+), 22 deletions(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 7e398f6fa..894bd44f8 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -56,9 +56,9 @@ copyless = "0.1.4" derive_more = "0.99.2" either = "1.5.3" encoding_rs = "0.8" -futures-core = "0.3.1" -futures-util = "0.3.1" -futures-channel = "0.3.1" +futures-channel = { version = "0.3.5", default-features = false } +futures-core = { version = "0.3.5", default-features = false } +futures-util = { version = "0.3.5", default-features = false } fxhash = "0.2.1" h2 = "0.2.1" http = "0.2.0" @@ -88,11 +88,10 @@ flate2 = { version = "1.0.13", optional = true } [dev-dependencies] actix-server = "1.0.1" -actix-connect = { version = "2.0.0-alpha.2", features=["openssl"] } -actix-http-test = { version = "1.0.0", features=["openssl"] } -actix-tls = { version = "2.0.0-alpha.1", features=["openssl"] } +actix-connect = { version = "2.0.0-alpha.2", features = ["openssl"] } +actix-http-test = { version = "1.0.0", features = ["openssl"] } +actix-tls = { version = "2.0.0-alpha.1", features = ["openssl"] } criterion = "0.3" -futures = "0.3.1" env_logger = "0.7" serde_derive = "1.0" open-ssl = { version="0.10", package = "openssl" } diff --git a/actix-http/examples/echo.rs b/actix-http/examples/echo.rs index b2b88a7ea..beb1cce2c 100644 --- a/actix-http/examples/echo.rs +++ b/actix-http/examples/echo.rs @@ -3,7 +3,7 @@ use std::{env, io}; use actix_http::{Error, HttpService, Request, Response}; use actix_server::Server; use bytes::BytesMut; -use futures::StreamExt; +use futures_util::StreamExt; use http::header::HeaderValue; use log::info; diff --git a/actix-http/examples/echo2.rs b/actix-http/examples/echo2.rs index f89ea2dfb..5b7e504d3 100644 --- a/actix-http/examples/echo2.rs +++ b/actix-http/examples/echo2.rs @@ -4,7 +4,7 @@ use actix_http::http::HeaderValue; use actix_http::{Error, HttpService, Request, Response}; use actix_server::Server; use bytes::BytesMut; -use futures::StreamExt; +use futures_util::StreamExt; use log::info; async fn handle_request(mut req: Request) -> Result { diff --git a/actix-http/examples/hello-world.rs b/actix-http/examples/hello-world.rs index 4134ee734..d6477b152 100644 --- a/actix-http/examples/hello-world.rs +++ b/actix-http/examples/hello-world.rs @@ -2,7 +2,7 @@ use std::{env, io}; use actix_http::{HttpService, Response}; use actix_server::Server; -use futures::future; +use futures_util::future; use http::header::HeaderValue; use log::info; diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index b821e777f..9d6fb26ee 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -482,7 +482,7 @@ where #[cfg(test)] mod tests { use super::*; - use futures::stream; + use futures_util::stream; use futures_util::future::poll_fn; use futures_util::pin_mut; diff --git a/actix-http/src/client/connection.rs b/actix-http/src/client/connection.rs index 0ca788b32..c1362df85 100644 --- a/actix-http/src/client/connection.rs +++ b/actix-http/src/client/connection.rs @@ -1,10 +1,11 @@ +use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use std::{fmt, io, mem, time}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use bytes::{Buf, Bytes}; -use futures_util::future::{err, Either, Future, FutureExt, LocalBoxFuture, Ready}; +use futures_util::future::{err, Either, FutureExt, LocalBoxFuture, Ready}; use h2::client::SendRequest; use pin_project::{pin_project, project}; diff --git a/actix-http/tests/test_client.rs b/actix-http/tests/test_client.rs index 5347971a6..07104decc 100644 --- a/actix-http/tests/test_client.rs +++ b/actix-http/tests/test_client.rs @@ -1,6 +1,6 @@ use actix_service::ServiceFactory; use bytes::Bytes; -use futures::future::{self, ok}; +use futures_util::future::{self, ok}; use actix_http::{http, HttpService, Request, Response}; use actix_http_test::test_server; diff --git a/actix-http/tests/test_openssl.rs b/actix-http/tests/test_openssl.rs index e8aaa7fc4..3a7bfa409 100644 --- a/actix-http/tests/test_openssl.rs +++ b/actix-http/tests/test_openssl.rs @@ -5,8 +5,8 @@ use actix_http_test::test_server; use actix_service::{fn_service, ServiceFactory}; use bytes::{Bytes, BytesMut}; -use futures::future::{err, ok, ready}; -use futures::stream::{once, Stream, StreamExt}; +use futures_util::future::{err, ok, ready}; +use futures_util::stream::{once, Stream, StreamExt}; use open_ssl::ssl::{AlpnError, SslAcceptor, SslFiletype, SslMethod}; use actix_http::error::{ErrorBadRequest, PayloadError}; diff --git a/actix-http/tests/test_rustls.rs b/actix-http/tests/test_rustls.rs index fff32ac8b..465cba6df 100644 --- a/actix-http/tests/test_rustls.rs +++ b/actix-http/tests/test_rustls.rs @@ -7,8 +7,8 @@ use actix_http_test::test_server; use actix_service::{fn_factory_with_config, fn_service}; use bytes::{Bytes, BytesMut}; -use futures::future::{self, err, ok}; -use futures::stream::{once, Stream, StreamExt}; +use futures_util::future::{self, err, ok}; +use futures_util::stream::{once, Stream, StreamExt}; use rust_tls::{ internal::pemfile::{certs, pkcs8_private_keys}, NoClientAuth, ServerConfig as RustlsServerConfig, diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index 35c71e37a..bee5ebef2 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -6,8 +6,8 @@ use actix_http_test::test_server; use actix_rt::time::delay_for; use actix_service::fn_service; use bytes::Bytes; -use futures::future::{self, err, ok, ready, FutureExt}; -use futures::stream::{once, StreamExt}; +use futures_util::future::{self, err, ok, ready, FutureExt}; +use futures_util::stream::{once, StreamExt}; use regex::Regex; use actix_http::httpmessage::HttpMessage; diff --git a/actix-http/tests/test_ws.rs b/actix-http/tests/test_ws.rs index 4b4b8f089..ff9def85b 100644 --- a/actix-http/tests/test_ws.rs +++ b/actix-http/tests/test_ws.rs @@ -1,4 +1,5 @@ use std::cell::Cell; +use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; use std::sync::{Arc, Mutex}; @@ -9,9 +10,9 @@ use actix_http_test::test_server; use actix_service::{fn_factory, Service}; use actix_utils::framed::Dispatcher; use bytes::Bytes; -use futures::future; -use futures::task::{Context, Poll}; -use futures::{Future, SinkExt, StreamExt}; +use futures_util::future; +use futures_util::task::{Context, Poll}; +use futures_util::{SinkExt, StreamExt}; struct WsService(Arc, Cell)>>); From ab4d8704f1cfc5d61b456b89db1bda6d66cd94aa Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 18 May 2020 11:45:44 +0900 Subject: [PATCH 126/157] multipart: Minimize `futures` dependencies --- actix-multipart/Cargo.toml | 2 +- actix-multipart/src/extractor.rs | 4 ++-- actix-multipart/src/server.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index d8421d0eb..2fed60d33 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -22,7 +22,7 @@ actix-utils = "1.0.3" bytes = "0.5.3" derive_more = "0.99.2" httparse = "1.3" -futures = "0.3.1" +futures-util = { version = "0.3.5", default-features = false } log = "0.4" mime = "0.3" twoway = "0.2" diff --git a/actix-multipart/src/extractor.rs b/actix-multipart/src/extractor.rs index 71c815227..4e4caee01 100644 --- a/actix-multipart/src/extractor.rs +++ b/actix-multipart/src/extractor.rs @@ -1,6 +1,6 @@ //! Multipart payload support use actix_web::{dev::Payload, Error, FromRequest, HttpRequest}; -use futures::future::{ok, Ready}; +use futures_util::future::{ok, Ready}; use crate::server::Multipart; @@ -11,7 +11,7 @@ use crate::server::Multipart; /// ## Server example /// /// ```rust -/// use futures::{Stream, StreamExt}; +/// use futures_util::stream::{Stream, StreamExt}; /// use actix_web::{web, HttpResponse, Error}; /// use actix_multipart as mp; /// diff --git a/actix-multipart/src/server.rs b/actix-multipart/src/server.rs index 2555cb7a3..f96a7821a 100644 --- a/actix-multipart/src/server.rs +++ b/actix-multipart/src/server.rs @@ -8,7 +8,7 @@ use std::task::{Context, Poll}; use std::{cmp, fmt}; use bytes::{Bytes, BytesMut}; -use futures::stream::{LocalBoxStream, Stream, StreamExt}; +use futures_util::stream::{LocalBoxStream, Stream, StreamExt}; use httparse; use mime; @@ -814,7 +814,7 @@ mod tests { use actix_utils::mpsc; use actix_web::http::header::{DispositionParam, DispositionType}; use bytes::Bytes; - use futures::future::lazy; + use futures_util::future::lazy; #[actix_rt::test] async fn test_boundary() { From fc8e07b947b40d76dc9d251be9ba4392ee8a4df8 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 18 May 2020 11:46:02 +0900 Subject: [PATCH 127/157] actors: Minimize `futures` dependencies --- actix-web-actors/Cargo.toml | 4 +++- actix-web-actors/src/context.rs | 5 +++-- actix-web-actors/src/ws.rs | 5 +++-- actix-web-actors/tests/test_ws.rs | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index f9bcc65de..b62616958 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -21,9 +21,11 @@ actix-web = "3.0.0-alpha.2" actix-http = "2.0.0-alpha.3" actix-codec = "0.2.0" bytes = "0.5.2" -futures = "0.3.1" +futures-channel = { version = "0.3.5", default-features = false } +futures-core = { version = "0.3.5", default-features = false } pin-project = "0.4.6" [dev-dependencies] actix-rt = "1.0.0" env_logger = "0.7" +futures-util = { version = "0.3.5", default-features = false } diff --git a/actix-web-actors/src/context.rs b/actix-web-actors/src/context.rs index c889092d2..0839a4288 100644 --- a/actix-web-actors/src/context.rs +++ b/actix-web-actors/src/context.rs @@ -1,4 +1,5 @@ use std::collections::VecDeque; +use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; @@ -11,8 +12,8 @@ use actix::{ }; use actix_web::error::Error; use bytes::Bytes; -use futures::channel::oneshot::Sender; -use futures::{Future, Stream}; +use futures_channel::oneshot::Sender; +use futures_core::Stream; /// Execution context for http actors pub struct HttpContext
diff --git a/actix-web-actors/src/ws.rs b/actix-web-actors/src/ws.rs index b28aeade4..3f5972532 100644 --- a/actix-web-actors/src/ws.rs +++ b/actix-web-actors/src/ws.rs @@ -1,5 +1,6 @@ //! Websocket integration use std::collections::VecDeque; +use std::future::Future; use std::io; use std::pin::Pin; use std::task::{Context, Poll}; @@ -23,8 +24,8 @@ use actix_web::error::{Error, PayloadError}; use actix_web::http::{header, Method, StatusCode}; use actix_web::{HttpRequest, HttpResponse}; use bytes::{Bytes, BytesMut}; -use futures::channel::oneshot::Sender; -use futures::{Future, Stream}; +use futures_channel::oneshot::Sender; +use futures_core::Stream; /// Do websocket handshake and start ws actor. pub fn start(actor: A, req: &HttpRequest, stream: T) -> Result diff --git a/actix-web-actors/tests/test_ws.rs b/actix-web-actors/tests/test_ws.rs index 076e375d3..25977c2c2 100644 --- a/actix-web-actors/tests/test_ws.rs +++ b/actix-web-actors/tests/test_ws.rs @@ -2,7 +2,7 @@ use actix::prelude::*; use actix_web::{test, web, App, HttpRequest}; use actix_web_actors::*; use bytes::Bytes; -use futures::{SinkExt, StreamExt}; +use futures_util::{SinkExt, StreamExt}; struct Ws; From 24372467d96250cc6f71c9f055050afe0d9dc6f8 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 18 May 2020 11:46:37 +0900 Subject: [PATCH 128/157] codegen: Minimize `futures` dependencies --- actix-web-codegen/Cargo.toml | 2 +- actix-web-codegen/src/lib.rs | 1 - actix-web-codegen/tests/test_macro.rs | 3 ++- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 835c9fcc9..c7b06e311 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -19,4 +19,4 @@ proc-macro2 = "^1" [dev-dependencies] actix-rt = "1.0.0" actix-web = "3.0.0-alpha.2" -futures = "0.3.1" +futures-util = { version = "0.3.5", default-features = false } diff --git a/actix-web-codegen/src/lib.rs b/actix-web-codegen/src/lib.rs index 39a8a6464..2a49b4714 100644 --- a/actix-web-codegen/src/lib.rs +++ b/actix-web-codegen/src/lib.rs @@ -33,7 +33,6 @@ //! ```rust //! use actix_web::HttpResponse; //! use actix_web_codegen::get; -//! use futures::{future, Future}; //! //! #[get("/test")] //! async fn async_test() -> Result { diff --git a/actix-web-codegen/tests/test_macro.rs b/actix-web-codegen/tests/test_macro.rs index 8264a7fd7..0ef7e1c75 100644 --- a/actix-web-codegen/tests/test_macro.rs +++ b/actix-web-codegen/tests/test_macro.rs @@ -1,10 +1,11 @@ +use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use actix_web::{http, test, web::Path, App, HttpResponse, Responder, Error}; use actix_web::dev::{Service, Transform, ServiceRequest, ServiceResponse}; use actix_web_codegen::{connect, delete, get, head, options, patch, post, put, trace}; -use futures::{future, Future}; +use futures_util::future; use actix_web::http::header::{HeaderName, HeaderValue}; // Make sure that we can name function as 'config' From d7abbff3b0acfa0909a7dd6cd292419881bad746 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 18 May 2020 11:46:57 +0900 Subject: [PATCH 129/157] awc: Minimize `futures` dependencies --- awc/Cargo.toml | 4 ++-- awc/src/lib.rs | 1 - awc/src/request.rs | 2 +- awc/src/response.rs | 3 ++- awc/src/sender.rs | 3 ++- awc/tests/test_client.rs | 2 +- awc/tests/test_rustls_client.rs | 2 +- awc/tests/test_ssl_client.rs | 2 +- awc/tests/test_ws.rs | 4 ++-- 9 files changed, 12 insertions(+), 11 deletions(-) diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 5ccf31654..c31c19025 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -42,7 +42,7 @@ actix-rt = "1.0.0" base64 = "0.11" bytes = "0.5.3" derive_more = "0.99.2" -futures-core = "0.3.1" +futures-core = { version = "0.3.5", default-features = false } log =" 0.4" mime = "0.3" percent-encoding = "2.1" @@ -63,6 +63,6 @@ actix-server = "1.0.0" actix-tls = { version = "2.0.0-alpha.1", features = ["openssl", "rustls"] } brotli2 = "0.3.2" flate2 = "1.0.13" -futures = "0.3.1" +futures-util = { version = "0.3.5", default-features = false } env_logger = "0.7" webpki = "0.21" diff --git a/awc/src/lib.rs b/awc/src/lib.rs index 952a15369..1cc31a194 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -7,7 +7,6 @@ //! An HTTP Client //! //! ```rust -//! use futures::future::{lazy, Future}; //! use actix_rt::System; //! use awc::Client; //! diff --git a/awc/src/request.rs b/awc/src/request.rs index 67b063a8e..21a7cd911 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -156,7 +156,7 @@ impl ClientRequest { /// /// ```rust /// fn main() { - /// # actix_rt::System::new("test").block_on(futures::future::lazy(|_| { + /// # actix_rt::System::new("test").block_on(futures_util::future::lazy(|_| { /// let req = awc::Client::new() /// .get("http://www.rust-lang.org") /// .set(awc::http::header::Date::now()) diff --git a/awc/src/response.rs b/awc/src/response.rs index 20093c72d..ffc8c5408 100644 --- a/awc/src/response.rs +++ b/awc/src/response.rs @@ -1,11 +1,12 @@ use std::cell::{Ref, RefMut}; use std::fmt; +use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; use bytes::{Bytes, BytesMut}; -use futures_core::{ready, Future, Stream}; +use futures_core::{ready, Stream}; use actix_http::cookie::Cookie; use actix_http::error::{CookieParseError, PayloadError}; diff --git a/awc/src/sender.rs b/awc/src/sender.rs index 983e730e1..5e0f5beec 100644 --- a/awc/src/sender.rs +++ b/awc/src/sender.rs @@ -1,3 +1,4 @@ +use std::future::Future; use std::net; use std::pin::Pin; use std::rc::Rc; @@ -7,7 +8,7 @@ use std::time::Duration; use actix_rt::time::{delay_for, Delay}; use bytes::Bytes; use derive_more::From; -use futures_core::{Future, Stream}; +use futures_core::Stream; use serde::Serialize; use actix_http::body::{Body, BodyStream}; diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs index 449734a9f..cc61f1006 100644 --- a/awc/tests/test_client.rs +++ b/awc/tests/test_client.rs @@ -9,7 +9,7 @@ use bytes::Bytes; use flate2::read::GzDecoder; use flate2::write::GzEncoder; use flate2::Compression; -use futures::future::ok; +use futures_util::future::ok; use rand::Rng; use actix_http::HttpService; diff --git a/awc/tests/test_rustls_client.rs b/awc/tests/test_rustls_client.rs index 7407a33fc..0c6be76d4 100644 --- a/awc/tests/test_rustls_client.rs +++ b/awc/tests/test_rustls_client.rs @@ -7,7 +7,7 @@ use actix_http_test::test_server; use actix_service::{map_config, pipeline_factory, ServiceFactory}; use actix_web::http::Version; use actix_web::{dev::AppConfig, web, App, HttpResponse}; -use futures::future::ok; +use futures_util::future::ok; use open_ssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslVerifyMode}; use rust_tls::ClientConfig; diff --git a/awc/tests/test_ssl_client.rs b/awc/tests/test_ssl_client.rs index 8e128ad0c..b2a2e1785 100644 --- a/awc/tests/test_ssl_client.rs +++ b/awc/tests/test_ssl_client.rs @@ -7,7 +7,7 @@ use actix_http_test::test_server; use actix_service::{map_config, pipeline_factory, ServiceFactory}; use actix_web::http::Version; use actix_web::{dev::AppConfig, web, App, HttpResponse}; -use futures::future::ok; +use futures_util::future::ok; use open_ssl::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode}; fn ssl_acceptor() -> SslAcceptor { diff --git a/awc/tests/test_ws.rs b/awc/tests/test_ws.rs index 779a4301c..d3f66814f 100644 --- a/awc/tests/test_ws.rs +++ b/awc/tests/test_ws.rs @@ -4,8 +4,8 @@ use actix_codec::Framed; use actix_http::{body::BodySize, h1, ws, Error, HttpService, Request, Response}; use actix_http_test::test_server; use bytes::Bytes; -use futures::future::ok; -use futures::{SinkExt, StreamExt}; +use futures_util::future::ok; +use futures_util::{SinkExt, StreamExt}; async fn ws_service(req: ws::Frame) -> Result { match req { From a98e53ecb8933a351c4c299ef175c102b35a8d67 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 18 May 2020 11:47:20 +0900 Subject: [PATCH 130/157] web: Minimize `futures` dependencies --- Cargo.toml | 4 +++- benches/server.rs | 2 +- src/app.rs | 4 ++-- src/app_service.rs | 2 +- src/data.rs | 2 +- src/extract.rs | 6 +++--- src/handler.rs | 4 ++-- src/middleware/compress.rs | 4 ++-- src/middleware/condition.rs | 2 +- src/middleware/defaultheaders.rs | 4 ++-- src/middleware/errhandlers.rs | 4 ++-- src/middleware/logger.rs | 6 +++--- src/middleware/normalize.rs | 2 +- src/request.rs | 2 +- src/resource.rs | 4 ++-- src/responder.rs | 4 ++-- src/route.rs | 2 +- src/scope.rs | 5 +++-- src/server.rs | 2 +- src/service.rs | 2 +- src/test.rs | 5 +++-- src/types/form.rs | 4 ++-- src/types/json.rs | 4 ++-- src/types/path.rs | 2 +- src/types/payload.rs | 14 +++++++++----- src/types/query.rs | 2 +- src/types/readlines.rs | 4 ++-- src/web.rs | 4 ++-- tests/test_server.rs | 5 +++-- 29 files changed, 60 insertions(+), 51 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4d2d783ef..ce26e82e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,7 +84,9 @@ awc = { version = "2.0.0-alpha.1", default-features = false } bytes = "0.5.3" derive_more = "0.99.2" encoding_rs = "0.8" -futures = "0.3.1" +futures-channel = { version = "0.3.5", default-features = false } +futures-core = { version = "0.3.5", default-features = false } +futures-util = { version = "0.3.5", default-features = false } fxhash = "0.2.1" log = "0.4" mime = "0.3" diff --git a/benches/server.rs b/benches/server.rs index 93079a223..70531adf7 100644 --- a/benches/server.rs +++ b/benches/server.rs @@ -1,7 +1,7 @@ use actix_web::{test, web, App, HttpResponse}; use awc::Client; use criterion::{criterion_group, criterion_main, Criterion}; -use futures::future::join_all; +use futures_util::future::join_all; const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ diff --git a/src/app.rs b/src/app.rs index 7d3100db7..9e38f5bc9 100644 --- a/src/app.rs +++ b/src/app.rs @@ -10,7 +10,7 @@ use actix_service::boxed::{self, BoxServiceFactory}; use actix_service::{ apply, apply_fn_factory, IntoServiceFactory, ServiceFactory, Transform, }; -use futures::future::FutureExt; +use futures_util::future::FutureExt; use crate::app_service::{AppEntry, AppInit, AppRoutingFactory}; use crate::config::ServiceConfig; @@ -474,7 +474,7 @@ where mod tests { use actix_service::Service; use bytes::Bytes; - use futures::future::{ok, err}; + use futures_util::future::{ok, err}; use super::*; use crate::http::{header, HeaderValue, Method, StatusCode}; diff --git a/src/app_service.rs b/src/app_service.rs index 693624ba0..233cfc0d5 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -9,7 +9,7 @@ use actix_http::{Extensions, Request, Response}; use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url}; use actix_service::boxed::{self, BoxService, BoxServiceFactory}; use actix_service::{fn_service, Service, ServiceFactory}; -use futures::future::{join_all, ok, FutureExt, LocalBoxFuture}; +use futures_util::future::{join_all, ok, FutureExt, LocalBoxFuture}; use crate::config::{AppConfig, AppService}; use crate::data::{DataFactory, FnDataFactory}; diff --git a/src/data.rs b/src/data.rs index e657d8b7b..34ada863d 100644 --- a/src/data.rs +++ b/src/data.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use actix_http::error::{Error, ErrorInternalServerError}; use actix_http::Extensions; -use futures::future::{err, ok, LocalBoxFuture, Ready}; +use futures_util::future::{err, ok, LocalBoxFuture, Ready}; use crate::dev::Payload; use crate::extract::FromRequest; diff --git a/src/extract.rs b/src/extract.rs index 5289bd7db..df9c34cb3 100644 --- a/src/extract.rs +++ b/src/extract.rs @@ -4,7 +4,7 @@ use std::pin::Pin; use std::task::{Context, Poll}; use actix_http::error::Error; -use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; +use futures_util::future::{ok, FutureExt, LocalBoxFuture, Ready}; use crate::dev::Payload; use crate::request::HttpRequest; @@ -50,7 +50,7 @@ pub trait FromRequest: Sized { /// ```rust /// use actix_web::{web, dev, App, Error, HttpRequest, FromRequest}; /// use actix_web::error::ErrorBadRequest; -/// use futures::future::{ok, err, Ready}; +/// use futures_util::future::{ok, err, Ready}; /// use serde_derive::Deserialize; /// use rand; /// @@ -122,7 +122,7 @@ where /// ```rust /// use actix_web::{web, dev, App, Result, Error, HttpRequest, FromRequest}; /// use actix_web::error::ErrorBadRequest; -/// use futures::future::{ok, err, Ready}; +/// use futures_util::future::{ok, err, Ready}; /// use serde_derive::Deserialize; /// use rand; /// diff --git a/src/handler.rs b/src/handler.rs index 33cd2408d..669512ab3 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -6,8 +6,8 @@ use std::task::{Context, Poll}; use actix_http::{Error, Response}; use actix_service::{Service, ServiceFactory}; -use futures::future::{ok, Ready}; -use futures::ready; +use futures_util::future::{ok, Ready}; +use futures_util::ready; use pin_project::pin_project; use crate::extract::FromRequest; diff --git a/src/middleware/compress.rs b/src/middleware/compress.rs index 70006ab3c..6de451c84 100644 --- a/src/middleware/compress.rs +++ b/src/middleware/compress.rs @@ -11,7 +11,7 @@ use actix_http::encoding::Encoder; use actix_http::http::header::{ContentEncoding, ACCEPT_ENCODING}; use actix_http::Error; use actix_service::{Service, Transform}; -use futures::future::{ok, Ready}; +use futures_util::future::{ok, Ready}; use pin_project::pin_project; use crate::dev::BodyEncoding; @@ -133,7 +133,7 @@ where fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); - match futures::ready!(this.fut.poll(cx)) { + match futures_util::ready!(this.fut.poll(cx)) { Ok(resp) => { let enc = if let Some(enc) = resp.response().get_encoding() { enc diff --git a/src/middleware/condition.rs b/src/middleware/condition.rs index 8c6909134..7ff81437b 100644 --- a/src/middleware/condition.rs +++ b/src/middleware/condition.rs @@ -2,7 +2,7 @@ use std::task::{Context, Poll}; use actix_service::{Service, Transform}; -use futures::future::{ok, Either, FutureExt, LocalBoxFuture}; +use futures_util::future::{ok, Either, FutureExt, LocalBoxFuture}; /// `Middleware` for conditionally enables another middleware. /// The controlled middleware must not change the `Service` interfaces. diff --git a/src/middleware/defaultheaders.rs b/src/middleware/defaultheaders.rs index ba001c77b..ef2e56e69 100644 --- a/src/middleware/defaultheaders.rs +++ b/src/middleware/defaultheaders.rs @@ -4,7 +4,7 @@ use std::rc::Rc; use std::task::{Context, Poll}; use actix_service::{Service, Transform}; -use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; +use futures_util::future::{ok, FutureExt, LocalBoxFuture, Ready}; use crate::http::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use crate::http::{Error as HttpError, HeaderMap}; @@ -157,7 +157,7 @@ where #[cfg(test)] mod tests { use actix_service::IntoService; - use futures::future::ok; + use futures_util::future::ok; use super::*; use crate::dev::ServiceRequest; diff --git a/src/middleware/errhandlers.rs b/src/middleware/errhandlers.rs index 71886af0b..93a5d3f22 100644 --- a/src/middleware/errhandlers.rs +++ b/src/middleware/errhandlers.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use std::task::{Context, Poll}; use actix_service::{Service, Transform}; -use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; +use futures_util::future::{ok, FutureExt, LocalBoxFuture, Ready}; use fxhash::FxHashMap; use crate::dev::{ServiceRequest, ServiceResponse}; @@ -147,7 +147,7 @@ where #[cfg(test)] mod tests { use actix_service::IntoService; - use futures::future::ok; + use futures_util::future::ok; use super::*; use crate::http::{header::CONTENT_TYPE, HeaderValue, StatusCode}; diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index d6b931bb4..e63efc832 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -11,7 +11,7 @@ use std::task::{Context, Poll}; use actix_service::{Service, Transform}; use bytes::Bytes; -use futures::future::{ok, Ready}; +use futures_util::future::{ok, Ready}; use log::debug; use regex::Regex; use time::OffsetDateTime; @@ -216,7 +216,7 @@ where fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); - let res = match futures::ready!(this.fut.poll(cx)) { + let res = match futures_util::ready!(this.fut.poll(cx)) { Ok(res) => res, Err(e) => return Poll::Ready(Err(e)), }; @@ -506,7 +506,7 @@ impl<'a> fmt::Display for FormatDisplay<'a> { #[cfg(test)] mod tests { use actix_service::{IntoService, Service, Transform}; - use futures::future::ok; + use futures_util::future::ok; use super::*; use crate::http::{header, StatusCode}; diff --git a/src/middleware/normalize.rs b/src/middleware/normalize.rs index 38b5310f7..139ec892e 100644 --- a/src/middleware/normalize.rs +++ b/src/middleware/normalize.rs @@ -4,7 +4,7 @@ use std::task::{Context, Poll}; use actix_http::http::{PathAndQuery, Uri}; use actix_service::{Service, Transform}; use bytes::Bytes; -use futures::future::{ok, Ready}; +use futures_util::future::{ok, Ready}; use regex::Regex; use crate::service::{ServiceRequest, ServiceResponse}; diff --git a/src/request.rs b/src/request.rs index 72fea1faf..f8abeb1bb 100644 --- a/src/request.rs +++ b/src/request.rs @@ -5,7 +5,7 @@ use std::{fmt, net}; use actix_http::http::{HeaderMap, Method, Uri, Version}; use actix_http::{Error, Extensions, HttpMessage, Message, Payload, RequestHead}; use actix_router::{Path, Url}; -use futures::future::{ok, Ready}; +use futures_util::future::{ok, Ready}; use tinyvec::TinyVec; use crate::config::AppConfig; diff --git a/src/resource.rs b/src/resource.rs index 4c0e26c18..5da1de62f 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -11,7 +11,7 @@ use actix_service::boxed::{self, BoxService, BoxServiceFactory}; use actix_service::{ apply, apply_fn_factory, IntoServiceFactory, Service, ServiceFactory, Transform, }; -use futures::future::{ok, Either, LocalBoxFuture, Ready}; +use futures_util::future::{ok, Either, LocalBoxFuture, Ready}; use crate::data::Data; use crate::dev::{insert_slash, AppService, HttpServiceFactory, ResourceDef}; @@ -588,7 +588,7 @@ mod tests { use actix_rt::time::delay_for; use actix_service::Service; - use futures::future::ok; + use futures_util::future::ok; use crate::http::{header, HeaderValue, Method, StatusCode}; use crate::middleware::DefaultHeaders; diff --git a/src/responder.rs b/src/responder.rs index 7189eecf1..367c9fccf 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -10,8 +10,8 @@ use actix_http::http::{ }; use actix_http::{Error, Response, ResponseBuilder}; use bytes::{Bytes, BytesMut}; -use futures::future::{err, ok, Either as EitherFuture, Ready}; -use futures::ready; +use futures_util::future::{err, ok, Either as EitherFuture, Ready}; +use futures_util::ready; use pin_project::{pin_project, project}; use crate::request::HttpRequest; diff --git a/src/route.rs b/src/route.rs index 11455630c..8da9aec1b 100644 --- a/src/route.rs +++ b/src/route.rs @@ -5,7 +5,7 @@ use std::task::{Context, Poll}; use actix_http::{http::Method, Error}; use actix_service::{Service, ServiceFactory}; -use futures::future::{ready, FutureExt, LocalBoxFuture}; +use futures_util::future::{ready, FutureExt, LocalBoxFuture}; use crate::extract::FromRequest; use crate::guard::{self, Guard}; diff --git a/src/scope.rs b/src/scope.rs index 5afca61da..b9166a4bf 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; use std::fmt; +use std::future::Future; use std::pin::Pin; use std::rc::Rc; use std::task::{Context, Poll}; @@ -10,7 +11,7 @@ use actix_service::boxed::{self, BoxService, BoxServiceFactory}; use actix_service::{ apply, apply_fn_factory, IntoServiceFactory, Service, ServiceFactory, Transform, }; -use futures::future::{ok, Either, Future, LocalBoxFuture, Ready}; +use futures_util::future::{ok, Either, LocalBoxFuture, Ready}; use crate::config::ServiceConfig; use crate::data::Data; @@ -666,7 +667,7 @@ impl ServiceFactory for ScopeEndpoint { mod tests { use actix_service::Service; use bytes::Bytes; - use futures::future::ok; + use futures_util::future::ok; use crate::dev::{Body, ResponseBody}; use crate::http::{header, HeaderValue, Method, StatusCode}; diff --git a/src/server.rs b/src/server.rs index 97dd9f7f7..248cd39b3 100644 --- a/src/server.rs +++ b/src/server.rs @@ -13,7 +13,7 @@ use actix_http::Protocol; #[cfg(unix)] use actix_service::pipeline_factory; #[cfg(unix)] -use futures::future::ok; +use futures_util::future::ok; #[cfg(feature = "openssl")] use actix_tls::openssl::{AlpnError, SslAcceptor, SslAcceptorBuilder}; diff --git a/src/service.rs b/src/service.rs index c0148a9b2..232a2f132 100644 --- a/src/service.rs +++ b/src/service.rs @@ -536,7 +536,7 @@ mod tests { use crate::test::{init_service, TestRequest}; use crate::{guard, http, web, App, HttpResponse}; use actix_service::Service; - use futures::future::ok; + use futures_util::future::ok; #[test] fn test_service_request() { diff --git a/src/test.rs b/src/test.rs index c8a738d83..79e351eb3 100644 --- a/src/test.rs +++ b/src/test.rs @@ -18,8 +18,9 @@ use actix_service::{ use awc::error::PayloadError; use awc::{Client, ClientRequest, ClientResponse, Connector}; use bytes::{Bytes, BytesMut}; -use futures::future::ok; -use futures::stream::{Stream, StreamExt}; +use futures_core::Stream; +use futures_util::future::ok; +use futures_util::StreamExt; use net2::TcpBuilder; use serde::de::DeserializeOwned; use serde::Serialize; diff --git a/src/types/form.rs b/src/types/form.rs index d917345e1..ca1a4b103 100644 --- a/src/types/form.rs +++ b/src/types/form.rs @@ -9,8 +9,8 @@ use std::{fmt, ops}; use actix_http::{Error, HttpMessage, Payload, Response}; use bytes::BytesMut; use encoding_rs::{Encoding, UTF_8}; -use futures::future::{err, ok, FutureExt, LocalBoxFuture, Ready}; -use futures::StreamExt; +use futures_util::future::{err, ok, FutureExt, LocalBoxFuture, Ready}; +use futures_util::StreamExt; use serde::de::DeserializeOwned; use serde::Serialize; diff --git a/src/types/json.rs b/src/types/json.rs index df8aa4fc6..f746fd432 100644 --- a/src/types/json.rs +++ b/src/types/json.rs @@ -7,8 +7,8 @@ use std::task::{Context, Poll}; use std::{fmt, ops}; use bytes::BytesMut; -use futures::future::{err, ok, FutureExt, LocalBoxFuture, Ready}; -use futures::StreamExt; +use futures_util::future::{err, ok, FutureExt, LocalBoxFuture, Ready}; +use futures_util::StreamExt; use serde::de::DeserializeOwned; use serde::Serialize; diff --git a/src/types/path.rs b/src/types/path.rs index a37cb8f12..82050171c 100644 --- a/src/types/path.rs +++ b/src/types/path.rs @@ -4,7 +4,7 @@ use std::{fmt, ops}; use actix_http::error::{Error, ErrorNotFound}; use actix_router::PathDeserializer; -use futures::future::{ready, Ready}; +use futures_util::future::{ready, Ready}; use serde::de; use crate::dev::Payload; diff --git a/src/types/payload.rs b/src/types/payload.rs index 449e6c5b0..bad33bfc6 100644 --- a/src/types/payload.rs +++ b/src/types/payload.rs @@ -8,8 +8,9 @@ use actix_http::error::{Error, ErrorBadRequest, PayloadError}; use actix_http::HttpMessage; use bytes::{Bytes, BytesMut}; use encoding_rs::UTF_8; -use futures::future::{err, ok, Either, FutureExt, LocalBoxFuture, Ready}; -use futures::{Stream, StreamExt}; +use futures_core::stream::Stream; +use futures_util::future::{err, ok, Either, FutureExt, LocalBoxFuture, Ready}; +use futures_util::StreamExt; use mime::Mime; use crate::dev; @@ -22,9 +23,10 @@ use crate::request::HttpRequest; /// ## Example /// /// ```rust -/// use futures::{Future, Stream, StreamExt}; /// use actix_web::{web, error, App, Error, HttpResponse}; -/// +/// use std::future::Future; +/// use futures_core::stream::Stream; +/// use futures_util::StreamExt; /// /// extract binary data from request /// async fn index(mut body: web::Payload) -> Result /// { @@ -70,8 +72,10 @@ impl Stream for Payload { /// ## Example /// /// ```rust -/// use futures::{Future, Stream, StreamExt}; /// use actix_web::{web, error, App, Error, HttpResponse}; +/// use std::future::Future; +/// use futures_core::stream::Stream; +/// use futures_util::StreamExt; /// /// /// extract binary data from request /// async fn index(mut body: web::Payload) -> Result diff --git a/src/types/query.rs b/src/types/query.rs index 73ea14f17..cf1a8930d 100644 --- a/src/types/query.rs +++ b/src/types/query.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use std::{fmt, ops}; use actix_http::error::Error; -use futures::future::{err, ok, Ready}; +use futures_util::future::{err, ok, Ready}; use serde::de; use crate::dev::Payload; diff --git a/src/types/readlines.rs b/src/types/readlines.rs index 82853381b..f03235377 100644 --- a/src/types/readlines.rs +++ b/src/types/readlines.rs @@ -5,7 +5,7 @@ use std::task::{Context, Poll}; use bytes::{Bytes, BytesMut}; use encoding_rs::{Encoding, UTF_8}; -use futures::Stream; +use futures_util::stream::Stream; use crate::dev::Payload; use crate::error::{PayloadError, ReadlinesError}; @@ -172,7 +172,7 @@ where #[cfg(test)] mod tests { - use futures::stream::StreamExt; + use futures_util::stream::StreamExt; use super::*; use crate::test::TestRequest; diff --git a/src/web.rs b/src/web.rs index f47cf865e..1d1174f41 100644 --- a/src/web.rs +++ b/src/web.rs @@ -1,11 +1,11 @@ //! Essentials helper functions and types for application registration. use actix_http::http::Method; use actix_router::IntoPattern; -use futures::Future; +use std::future::Future; pub use actix_http::Response as HttpResponse; pub use bytes::{Bytes, BytesMut}; -pub use futures::channel::oneshot::Canceled; +pub use futures_channel::oneshot::Canceled; use crate::error::BlockingError; use crate::extract::FromRequest; diff --git a/tests/test_server.rs b/tests/test_server.rs index a4dfa65a0..926b211ee 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -1,3 +1,4 @@ +use std::future::Future; use std::io::{Read, Write}; use std::pin::Pin; use std::task::{Context, Poll}; @@ -11,7 +12,7 @@ use bytes::Bytes; use flate2::read::GzDecoder; use flate2::write::{GzEncoder, ZlibDecoder, ZlibEncoder}; use flate2::Compression; -use futures::{ready, Future}; +use futures_util::ready; use rand::{distributions::Alphanumeric, Rng}; use actix_web::dev::BodyEncoding; @@ -56,7 +57,7 @@ impl TestBody { } } -impl futures::Stream for TestBody { +impl futures_core::stream::Stream for TestBody { type Item = Result; fn poll_next( From 81b0c32062e43f369c4d5ffa9d211b311807d811 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 18 May 2020 11:47:33 +0900 Subject: [PATCH 131/157] test-server: Minimize `futures` dependencies --- test-server/Cargo.toml | 2 +- test-server/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index f44ecc5fd..8357815c9 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -41,7 +41,7 @@ awc = "2.0.0-alpha.1" base64 = "0.11" bytes = "0.5.3" -futures = "0.3.1" +futures-core = { version = "0.3.5", default-features = false } http = "0.2.0" log = "0.4" env_logger = "0.6" diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index c36cc706f..4fa0ecc17 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -7,7 +7,7 @@ use actix_rt::{net::TcpStream, System}; use actix_server::{Server, ServiceFactory}; use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connector}; use bytes::Bytes; -use futures::Stream; +use futures_core::stream::Stream; use http::Method; use net2::TcpBuilder; From 2dac9afc4e377490c1b41b238da2e9cf9ae800bf Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Tue, 19 May 2020 09:25:51 +0900 Subject: [PATCH 132/157] test-server: Replace `net2` crate with `socket2` --- test-server/CHANGES.md | 1 + test-server/Cargo.toml | 2 +- test-server/src/lib.rs | 10 +++++----- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/test-server/CHANGES.md b/test-server/CHANGES.md index 6a92d581c..1570e36d8 100644 --- a/test-server/CHANGES.md +++ b/test-server/CHANGES.md @@ -6,6 +6,7 @@ * Update `actix-connect` dependency to 2.0.0-alpha.2 * Make `test_server` `async` fn. * Bump minimum supported Rust version to 1.40 +* Replace deprecated `net2` crate with `socket2` ## [1.0.0] - 2019-12-13 diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 8357815c9..ebfbd55cd 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -45,7 +45,7 @@ futures-core = { version = "0.3.5", default-features = false } http = "0.2.0" log = "0.4" env_logger = "0.6" -net2 = "0.2" +socket2 = "0.3" serde = "1.0" serde_json = "1.0" sha1 = "0.6" diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index 4fa0ecc17..265d9a771 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -9,7 +9,7 @@ use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connec use bytes::Bytes; use futures_core::stream::Stream; use http::Method; -use net2::TcpBuilder; +use socket2::{Domain, Protocol, Socket, Type}; pub use actix_testing::*; @@ -104,10 +104,10 @@ pub async fn test_server>(factory: F) -> TestServer /// Get first available unused address pub fn unused_addr() -> net::SocketAddr { let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); - let socket = TcpBuilder::new_v4().unwrap(); - socket.bind(&addr).unwrap(); - socket.reuse_address(true).unwrap(); - let tcp = socket.to_tcp_listener().unwrap(); + let socket = Socket::new(Domain::ipv4(), Type::stream(), Some(Protocol::tcp())).unwrap(); + socket.bind(&addr.into()).unwrap(); + socket.set_reuse_address(true).unwrap(); + let tcp = socket.into_tcp_listener(); tcp.local_addr().unwrap() } From 9a7f93610a69b6bec00fda93598d1cffc50cb0e0 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Tue, 19 May 2020 09:34:37 +0900 Subject: [PATCH 133/157] web: Replace `net2` crate with `socket2` --- CHANGES.md | 2 ++ Cargo.toml | 2 +- src/server.rs | 17 +++++++++-------- src/test.rs | 10 +++++----- tests/test_httpserver.rs | 18 ++++-------------- 5 files changed, 21 insertions(+), 28 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ab660808e..d5f36a9cb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,6 +14,8 @@ * Bump minimum supported Rust version to 1.40 +* Replace deprecated `net2` crate with `socket2` + [#1485]: https://github.com/actix/actix-web/pull/1485 [#1509]: https://github.com/actix/actix-web/pull/1509 diff --git a/Cargo.toml b/Cargo.toml index ce26e82e0..a7b6e70d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,7 +90,7 @@ futures-util = { version = "0.3.5", default-features = false } fxhash = "0.2.1" log = "0.4" mime = "0.3" -net2 = "0.2.33" +socket2 = "0.3" pin-project = "0.4.6" regex = "1.3" serde = { version = "1.0", features=["derive"] } diff --git a/src/server.rs b/src/server.rs index 248cd39b3..b2695b004 100644 --- a/src/server.rs +++ b/src/server.rs @@ -6,8 +6,6 @@ use actix_http::{body::MessageBody, Error, HttpService, KeepAlive, Request, Resp use actix_server::{Server, ServerBuilder}; use actix_service::{map_config, IntoServiceFactory, Service, ServiceFactory}; -use net2::TcpBuilder; - #[cfg(unix)] use actix_http::Protocol; #[cfg(unix)] @@ -562,13 +560,16 @@ fn create_tcp_listener( addr: net::SocketAddr, backlog: i32, ) -> io::Result { - let builder = match addr { - net::SocketAddr::V4(_) => TcpBuilder::new_v4()?, - net::SocketAddr::V6(_) => TcpBuilder::new_v6()?, + use socket2::{Domain, Protocol, Socket, Type}; + let domain = match addr { + net::SocketAddr::V4(_) => Domain::ipv4(), + net::SocketAddr::V6(_) => Domain::ipv6(), }; - builder.reuse_address(true)?; - builder.bind(addr)?; - Ok(builder.listen(backlog)?) + let socket = Socket::new(domain, Type::stream(), Some(Protocol::tcp()))?; + socket.set_reuse_address(true)?; + socket.bind(&addr.into())?; + socket.listen(backlog)?; + Ok(socket.into_tcp_listener()) } #[cfg(feature = "openssl")] diff --git a/src/test.rs b/src/test.rs index 79e351eb3..684e9c116 100644 --- a/src/test.rs +++ b/src/test.rs @@ -21,10 +21,10 @@ use bytes::{Bytes, BytesMut}; use futures_core::Stream; use futures_util::future::ok; use futures_util::StreamExt; -use net2::TcpBuilder; use serde::de::DeserializeOwned; use serde::Serialize; use serde_json; +use socket2::{Domain, Protocol, Socket, Type}; pub use actix_http::test::TestBuffer; @@ -913,10 +913,10 @@ impl TestServerConfig { /// Get first available unused address pub fn unused_addr() -> net::SocketAddr { let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); - let socket = TcpBuilder::new_v4().unwrap(); - socket.bind(&addr).unwrap(); - socket.reuse_address(true).unwrap(); - let tcp = socket.to_tcp_listener().unwrap(); + let socket = Socket::new(Domain::ipv4(), Type::stream(), Some(Protocol::tcp())).unwrap(); + socket.bind(&addr.into()).unwrap(); + socket.set_reuse_address(true).unwrap(); + let tcp = socket.into_tcp_listener(); tcp.local_addr().unwrap() } diff --git a/tests/test_httpserver.rs b/tests/test_httpserver.rs index ecd5c9ffb..750084fdc 100644 --- a/tests/test_httpserver.rs +++ b/tests/test_httpserver.rs @@ -1,25 +1,15 @@ -use net2::TcpBuilder; use std::sync::mpsc; -use std::{net, thread, time::Duration}; +use std::{thread, time::Duration}; #[cfg(feature = "openssl")] use open_ssl::ssl::SslAcceptorBuilder; -use actix_web::{web, App, HttpResponse, HttpServer}; - -fn unused_addr() -> net::SocketAddr { - let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); - let socket = TcpBuilder::new_v4().unwrap(); - socket.bind(&addr).unwrap(); - socket.reuse_address(true).unwrap(); - let tcp = socket.to_tcp_listener().unwrap(); - tcp.local_addr().unwrap() -} +use actix_web::{test, web, App, HttpResponse, HttpServer}; #[cfg(unix)] #[actix_rt::test] async fn test_start() { - let addr = unused_addr(); + let addr = test::unused_addr(); let (tx, rx) = mpsc::channel(); thread::spawn(move || { @@ -92,7 +82,7 @@ fn ssl_acceptor() -> std::io::Result { async fn test_start_ssl() { use actix_web::HttpRequest; - let addr = unused_addr(); + let addr = test::unused_addr(); let (tx, rx) = mpsc::channel(); thread::spawn(move || { From 50adbdecbe6254405b9690a1e7cf94395198128b Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 20 May 2020 13:08:19 +0900 Subject: [PATCH 134/157] CI: Only run the server benchmark to avoid SIGKILL --- .github/workflows/bench.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 04fe83f66..1a2ee496b 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -20,3 +20,4 @@ jobs: uses: actions-rs/cargo@v1 with: command: bench + args: --bench=server From 864fc489ce642f56cf4dc3647ab3d7dc837701d8 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 20 May 2020 22:33:25 +0900 Subject: [PATCH 135/157] CI: Reduce sample size --- .github/workflows/bench.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 1a2ee496b..ce8a7da7e 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -20,4 +20,4 @@ jobs: uses: actions-rs/cargo@v1 with: command: bench - args: --bench=server + args: --bench=server -- --sample-size=15 From 83914279050de378e95abc89415628fb468b1ca8 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 21 May 2020 09:51:32 +0900 Subject: [PATCH 136/157] http: Update `base64` to 0.12 --- actix-http/CHANGES.md | 2 +- actix-http/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 1a71a0bc0..636c9e330 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -6,9 +6,9 @@ * Bump minimum supported Rust version to 1.40 * content_length function is removed, and you can set Content-Length by calling no_chunking function [#1439] - * `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a `u64` instead of a `usize`. +* Update `base64` dependency to 0.12 ### Fixed diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 894bd44f8..3ddc5181c 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -49,7 +49,7 @@ actix-threadpool = "0.3.1" actix-tls = { version = "2.0.0-alpha.1", optional = true } actix = { version = "0.10.0-alpha.1", optional = true } -base64 = "0.11" +base64 = "0.12" bitflags = "1.2" bytes = "0.5.3" copyless = "0.1.4" From 603973dedeaf1801a849c17a627a238b2d05dc8a Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 21 May 2020 09:51:58 +0900 Subject: [PATCH 137/157] awc: Update `base64` to 0.12 --- awc/CHANGES.md | 1 + awc/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 67bbc38c5..f501674ee 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -6,6 +6,7 @@ * Implement `std::error::Error` for our custom errors [#1422] * Bump minimum supported Rust version to 1.40 +* Update `base64` dependency to 0.12 [#1422]: https://github.com/actix/actix-web/pull/1422 diff --git a/awc/Cargo.toml b/awc/Cargo.toml index c31c19025..894674c90 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -39,7 +39,7 @@ actix-service = "1.0.1" actix-http = "2.0.0-alpha.3" actix-rt = "1.0.0" -base64 = "0.11" +base64 = "0.12" bytes = "0.5.3" derive_more = "0.99.2" futures-core = { version = "0.3.5", default-features = false } From 6c78f57a70236b91e2ab14df98d4d152f8a2b7f7 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 21 May 2020 09:52:15 +0900 Subject: [PATCH 138/157] test-server: Update dependencies --- test-server/CHANGES.md | 2 ++ test-server/Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test-server/CHANGES.md b/test-server/CHANGES.md index 1570e36d8..e97ee5d6f 100644 --- a/test-server/CHANGES.md +++ b/test-server/CHANGES.md @@ -7,6 +7,8 @@ * Make `test_server` `async` fn. * Bump minimum supported Rust version to 1.40 * Replace deprecated `net2` crate with `socket2` +* Update `base64` dependency to 0.12 +* Update `env_logger` dependency to 0.7 ## [1.0.0] - 2019-12-13 diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index ebfbd55cd..f4b02353c 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -39,12 +39,12 @@ actix-server = "1.0.0" actix-testing = "1.0.0" awc = "2.0.0-alpha.1" -base64 = "0.11" +base64 = "0.12" bytes = "0.5.3" futures-core = { version = "0.3.5", default-features = false } http = "0.2.0" log = "0.4" -env_logger = "0.6" +env_logger = "0.7" socket2 = "0.3" serde = "1.0" serde_json = "1.0" From 48fa78e1827c725b03d8ed028b90693034a0f0bb Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 21 May 2020 15:22:07 +0900 Subject: [PATCH 139/157] http: Bump up to 2.0.0-alpha.4 --- actix-http/CHANGES.md | 2 +- actix-http/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 636c9e330..c19e40e4c 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [Unreleased] +## [2.0.0-alpha.4] - 2020-05-21 ### Changed diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 3ddc5181c..573fcf1e4 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "2.0.0-alpha.3" +version = "2.0.0-alpha.4" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" From 9cfb32c7808bc052154b95d9d59aad9a27c3dbb1 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 21 May 2020 15:22:42 +0900 Subject: [PATCH 140/157] Update `actix-http` to 2.0.0-alpha.4 --- Cargo.toml | 2 +- actix-files/Cargo.toml | 2 +- actix-framed/Cargo.toml | 6 +++--- actix-multipart/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- awc/Cargo.toml | 4 ++-- test-server/Cargo.toml | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a7b6e70d6..b9b13fd73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,7 +78,7 @@ actix-threadpool = "0.3.1" actix-tls = "2.0.0-alpha.1" actix-web-codegen = "0.2.0" -actix-http = "2.0.0-alpha.3" +actix-http = "2.0.0-alpha.4" awc = { version = "2.0.0-alpha.1", default-features = false } bytes = "0.5.3" diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index ead2b51f5..d3d1ce073 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -19,7 +19,7 @@ path = "src/lib.rs" [dependencies] actix-web = { version = "3.0.0-alpha.2", default-features = false } -actix-http = "2.0.0-alpha.3" +actix-http = "2.0.0-alpha.4" actix-service = "1.0.1" bitflags = "1" bytes = "0.5.3" diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index ba7a01e0c..cae7939fd 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -23,7 +23,7 @@ actix-codec = "0.2.0" actix-service = "1.0.1" actix-router = "0.2.1" actix-rt = "1.0.0" -actix-http = "2.0.0-alpha.3" +actix-http = "2.0.0-alpha.4" bytes = "0.5.3" futures-util = { version = "0.3.5", default-features = false } @@ -32,6 +32,6 @@ log = "0.4" [dev-dependencies] actix-server = "1.0.0" -actix-connect = { version = "2.0.0-alpha.2", features=["openssl"] } -actix-http-test = { version = "1.0.0", features=["openssl"] } +actix-connect = { version = "2.0.0-alpha.2", features = ["openssl"] } +actix-http-test = { version = "1.0.0", features = ["openssl"] } actix-utils = "1.0.3" diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 2fed60d33..0bc79e5fa 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -29,4 +29,4 @@ twoway = "0.2" [dev-dependencies] actix-rt = "1.0.0" -actix-http = "2.0.0-alpha.3" +actix-http = "2.0.0-alpha.4" diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index b62616958..45d506940 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -18,7 +18,7 @@ path = "src/lib.rs" [dependencies] actix = "0.10.0-alpha.2" actix-web = "3.0.0-alpha.2" -actix-http = "2.0.0-alpha.3" +actix-http = "2.0.0-alpha.4" actix-codec = "0.2.0" bytes = "0.5.2" futures-channel = { version = "0.3.5", default-features = false } diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 894674c90..437872aa2 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -36,7 +36,7 @@ compress = ["actix-http/compress"] [dependencies] actix-codec = "0.2.0" actix-service = "1.0.1" -actix-http = "2.0.0-alpha.3" +actix-http = "2.0.0-alpha.4" actix-rt = "1.0.0" base64 = "0.12" @@ -56,7 +56,7 @@ rust-tls = { version = "0.17.0", package = "rustls", optional = true, features = [dev-dependencies] actix-connect = { version = "2.0.0-alpha.2", features = ["openssl"] } actix-web = { version = "3.0.0-alpha.2", features = ["openssl"] } -actix-http = { version = "2.0.0-alpha.3", features = ["openssl"] } +actix-http = { version = "2.0.0-alpha.4", features = ["openssl"] } actix-http-test = { version = "1.0.0", features = ["openssl"] } actix-utils = "1.0.3" actix-server = "1.0.0" diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index f4b02353c..7544ca19b 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -56,4 +56,4 @@ open-ssl = { version = "0.10", package = "openssl", optional = true } [dev-dependencies] actix-web = "3.0.0-alpha.2" -actix-http = "2.0.0-alpha.3" +actix-http = "2.0.0-alpha.4" From 0f826fd11a90ec2ad7ee1a36b66c7fc6182f927c Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 21 May 2020 16:47:16 +0900 Subject: [PATCH 141/157] awc: Bump up to 2.0.0-alpha.2 --- awc/CHANGES.md | 2 +- awc/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/awc/CHANGES.md b/awc/CHANGES.md index f501674ee..6d5a81b5e 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [Unreleased] +## [2.0.0-alpha.2] - 2020-05-21 ### Changed diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 437872aa2..a8f3a3fa4 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "2.0.0-alpha.1" +version = "2.0.0-alpha.2" authors = ["Nikolay Kim "] description = "Actix http client." readme = "README.md" From 9b72d33b79129e6371c705319d8761d2e0e8fef5 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 21 May 2020 16:48:20 +0900 Subject: [PATCH 142/157] Update `awc` to 2.0.0-alpha.2 --- Cargo.toml | 2 +- test-server/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b9b13fd73..f9f0491ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,7 +79,7 @@ actix-tls = "2.0.0-alpha.1" actix-web-codegen = "0.2.0" actix-http = "2.0.0-alpha.4" -awc = { version = "2.0.0-alpha.1", default-features = false } +awc = { version = "2.0.0-alpha.2", default-features = false } bytes = "0.5.3" derive_more = "0.99.2" diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 7544ca19b..09d1a9195 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -37,7 +37,7 @@ actix-utils = "1.0.3" actix-rt = "1.0.0" actix-server = "1.0.0" actix-testing = "1.0.0" -awc = "2.0.0-alpha.1" +awc = "2.0.0-alpha.2" base64 = "0.12" bytes = "0.5.3" From 5d39110470cc4f6723e01a816d53101585da3d60 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 21 May 2020 17:31:22 +0900 Subject: [PATCH 143/157] web: Bump up to 3.0.0-alpha.3 --- CHANGES.md | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index d5f36a9cb..739f1b13c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [Unreleased] +## [3.0.0-alpha.3] - 2020-05-21 ### Added diff --git a/Cargo.toml b/Cargo.toml index f9f0491ba..83d64f8f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "3.0.0-alpha.2" +version = "3.0.0-alpha.3" authors = ["Nikolay Kim "] description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." readme = "README.md" From fe89ba702793408733c15fc7452a0af94f2d5347 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 21 May 2020 17:32:36 +0900 Subject: [PATCH 144/157] Update `actix-web` dependency to 3.0.0-alpha.3 --- actix-files/Cargo.toml | 4 ++-- actix-multipart/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- actix-web-codegen/Cargo.toml | 2 +- awc/Cargo.toml | 2 +- test-server/Cargo.toml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index d3d1ce073..f84a70fe5 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -18,7 +18,7 @@ name = "actix_files" path = "src/lib.rs" [dependencies] -actix-web = { version = "3.0.0-alpha.2", default-features = false } +actix-web = { version = "3.0.0-alpha.3", default-features = false } actix-http = "2.0.0-alpha.4" actix-service = "1.0.1" bitflags = "1" @@ -34,4 +34,4 @@ v_htmlescape = "0.4" [dev-dependencies] actix-rt = "1.0.0" -actix-web = { version = "3.0.0-alpha.2", features = ["openssl"] } +actix-web = { version = "3.0.0-alpha.3", features = ["openssl"] } diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 0bc79e5fa..e12c07bf1 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -16,7 +16,7 @@ name = "actix_multipart" path = "src/lib.rs" [dependencies] -actix-web = { version = "3.0.0-alpha.2", default-features = false } +actix-web = { version = "3.0.0-alpha.3", default-features = false } actix-service = "1.0.1" actix-utils = "1.0.3" bytes = "0.5.3" diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 45d506940..20fd3743d 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -17,7 +17,7 @@ path = "src/lib.rs" [dependencies] actix = "0.10.0-alpha.2" -actix-web = "3.0.0-alpha.2" +actix-web = "3.0.0-alpha.3" actix-http = "2.0.0-alpha.4" actix-codec = "0.2.0" bytes = "0.5.2" diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index c7b06e311..d57022d4d 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -18,5 +18,5 @@ proc-macro2 = "^1" [dev-dependencies] actix-rt = "1.0.0" -actix-web = "3.0.0-alpha.2" +actix-web = "3.0.0-alpha.3" futures-util = { version = "0.3.5", default-features = false } diff --git a/awc/Cargo.toml b/awc/Cargo.toml index a8f3a3fa4..5e720ca87 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -55,7 +55,7 @@ rust-tls = { version = "0.17.0", package = "rustls", optional = true, features = [dev-dependencies] actix-connect = { version = "2.0.0-alpha.2", features = ["openssl"] } -actix-web = { version = "3.0.0-alpha.2", features = ["openssl"] } +actix-web = { version = "3.0.0-alpha.3", features = ["openssl"] } actix-http = { version = "2.0.0-alpha.4", features = ["openssl"] } actix-http-test = { version = "1.0.0", features = ["openssl"] } actix-utils = "1.0.3" diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 09d1a9195..2fa0691c3 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -55,5 +55,5 @@ time = { version = "0.2.7", default-features = false, features = ["std"] } open-ssl = { version = "0.10", package = "openssl", optional = true } [dev-dependencies] -actix-web = "3.0.0-alpha.2" +actix-web = "3.0.0-alpha.3" actix-http = "2.0.0-alpha.4" From 6dd78d935501c22b6f52c05fd9191e893054fab4 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 21 May 2020 17:56:53 +0900 Subject: [PATCH 145/157] Run rustfmt --- src/app.rs | 8 +++++--- src/middleware/logger.rs | 25 ++++++++++++++++--------- src/middleware/normalize.rs | 4 ++-- src/route.rs | 34 +++++++++++++--------------------- src/test.rs | 7 +++++-- tests/test_server.rs | 7 ++++--- 6 files changed, 45 insertions(+), 40 deletions(-) diff --git a/src/app.rs b/src/app.rs index 9e38f5bc9..8178d57fe 100644 --- a/src/app.rs +++ b/src/app.rs @@ -474,13 +474,15 @@ where mod tests { use actix_service::Service; use bytes::Bytes; - use futures_util::future::{ok, err}; + use futures_util::future::{err, ok}; use super::*; use crate::http::{header, HeaderValue, Method, StatusCode}; use crate::middleware::DefaultHeaders; use crate::service::ServiceRequest; - use crate::test::{call_service, init_service, try_init_service, read_body, TestRequest}; + use crate::test::{ + call_service, init_service, read_body, try_init_service, TestRequest, + }; use crate::{web, HttpRequest, HttpResponse}; #[actix_rt::test] @@ -556,7 +558,7 @@ mod tests { web::resource("/").to(|_: web::Data| HttpResponse::Ok()), )) .await; - + assert!(srv.is_err()); } diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index e63efc832..8b881c0a4 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -273,13 +273,15 @@ impl PinnedDrop for StreamLog { } } - impl MessageBody for StreamLog { fn size(&self) -> BodySize { self.body.size() } - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { let this = self.project(); match this.body.poll_next(cx) { Poll::Ready(Some(Ok(chunk))) => { @@ -324,11 +326,13 @@ impl Format { if let Some(key) = cap.get(2) { results.push(match cap.get(3).unwrap().as_str() { - "a" => if key.as_str() == "r" { - FormatText::RealIPRemoteAddr - } else { - unreachable!() - }, + "a" => { + if key.as_str() == "r" { + FormatText::RealIPRemoteAddr + } else { + unreachable!() + } + } "i" => FormatText::RequestHeader( HeaderName::try_from(key.as_str()).unwrap(), ), @@ -481,7 +485,8 @@ impl FormatText { *self = s; } FormatText::RealIPRemoteAddr => { - let s = if let Some(remote) = req.connection_info().realip_remote_addr() { + let s = if let Some(remote) = req.connection_info().realip_remote_addr() + { FormatText::Str(remote.to_string()) } else { FormatText::Str("-".to_string()) @@ -630,7 +635,9 @@ mod tests { let req = TestRequest::with_header( header::FORWARDED, - header::HeaderValue::from_static("for=192.0.2.60;proto=http;by=203.0.113.43"), + header::HeaderValue::from_static( + "for=192.0.2.60;proto=http;by=203.0.113.43", + ), ) .to_srv_request(); diff --git a/src/middleware/normalize.rs b/src/middleware/normalize.rs index 139ec892e..d23f03445 100644 --- a/src/middleware/normalize.rs +++ b/src/middleware/normalize.rs @@ -74,7 +74,7 @@ where fn call(&mut self, mut req: ServiceRequest) -> Self::Future { let head = req.head_mut(); - + // always add trailing slash, might be an extra one let path = head.uri.path().to_string() + "/"; let original_len = path.len(); @@ -177,7 +177,7 @@ mod tests { assert!(res.status().is_success()); } - #[actix_rt::test] + #[actix_rt::test] async fn should_normalize_nothing_notrail() { const URI: &str = "/v1/something"; diff --git a/src/route.rs b/src/route.rs index 8da9aec1b..2763f3b1a 100644 --- a/src/route.rs +++ b/src/route.rs @@ -362,31 +362,23 @@ mod tests { .service( web::resource("/test") .route(web::get().to(|| HttpResponse::Ok())) - .route(web::put().to(|| { - async { - Err::(error::ErrorBadRequest("err")) - } + .route(web::put().to(|| async { + Err::(error::ErrorBadRequest("err")) })) - .route(web::post().to(|| { - async { - delay_for(Duration::from_millis(100)).await; - HttpResponse::Created() - } + .route(web::post().to(|| async { + delay_for(Duration::from_millis(100)).await; + HttpResponse::Created() })) - .route(web::delete().to(|| { - async { - delay_for(Duration::from_millis(100)).await; - Err::(error::ErrorBadRequest("err")) - } + .route(web::delete().to(|| async { + delay_for(Duration::from_millis(100)).await; + Err::(error::ErrorBadRequest("err")) })), ) - .service(web::resource("/json").route(web::get().to(|| { - async { - delay_for(Duration::from_millis(25)).await; - web::Json(MyObject { - name: "test".to_string(), - }) - } + .service(web::resource("/json").route(web::get().to(|| async { + delay_for(Duration::from_millis(25)).await; + web::Json(MyObject { + name: "test".to_string(), + }) }))), ) .await; diff --git a/src/test.rs b/src/test.rs index 684e9c116..a64ec3a73 100644 --- a/src/test.rs +++ b/src/test.rs @@ -89,7 +89,9 @@ where >, S::InitError: std::fmt::Debug, { - try_init_service(app).await.expect("service initilization failed") + try_init_service(app) + .await + .expect("service initilization failed") } /// Fallible version of init_service that allows testing data factory errors. @@ -913,7 +915,8 @@ impl TestServerConfig { /// Get first available unused address pub fn unused_addr() -> net::SocketAddr { let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); - let socket = Socket::new(Domain::ipv4(), Type::stream(), Some(Protocol::tcp())).unwrap(); + let socket = + Socket::new(Domain::ipv4(), Type::stream(), Some(Protocol::tcp())).unwrap(); socket.bind(&addr.into()).unwrap(); socket.set_reuse_address(true).unwrap(); let tcp = socket.into_tcp_listener(); diff --git a/tests/test_server.rs b/tests/test_server.rs index 926b211ee..0ac4b0232 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -349,9 +349,10 @@ async fn test_body_br_streaming() { #[actix_rt::test] async fn test_head_binary() { let srv = test::start_with(test::config().h1(), || { - App::new().service(web::resource("/").route( - web::head().to(move || HttpResponse::Ok().body(STR)), - )) + App::new().service( + web::resource("/") + .route(web::head().to(move || HttpResponse::Ok().body(STR))), + ) }); let mut response = srv.head("/").send().await.unwrap(); From 7d8fb631a074dc31cde9a3da4a82acb9b027563f Mon Sep 17 00:00:00 2001 From: Nick Kolpinskiy Date: Thu, 21 May 2020 22:25:34 +0300 Subject: [PATCH 146/157] Use `itoa` in the content-length helper --- actix-http/Cargo.toml | 1 + actix-http/benches/content-length.rs | 24 +++++++ actix-http/src/helpers.rs | 101 +++------------------------ 3 files changed, 33 insertions(+), 93 deletions(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 573fcf1e4..deb63197a 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -64,6 +64,7 @@ h2 = "0.2.1" http = "0.2.0" httparse = "1.3" indexmap = "1.3" +itoa = "0.4" lazy_static = "1.4" language-tags = "0.2" log = "0.4" diff --git a/actix-http/benches/content-length.rs b/actix-http/benches/content-length.rs index b001b3931..18a55a33b 100644 --- a/actix-http/benches/content-length.rs +++ b/actix-http/benches/content-length.rs @@ -25,6 +25,13 @@ fn bench_write_content_length(c: &mut Criterion) { _new::write_content_length(i, &mut b) }) }); + + group.bench_with_input(BenchmarkId::new("itoa", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _itoa::write_content_length(i, &mut b) + }) + }); } group.finish(); @@ -33,6 +40,23 @@ fn bench_write_content_length(c: &mut Criterion) { criterion_group!(benches, bench_write_content_length); criterion_main!(benches); +mod _itoa { + use bytes::{BufMut, BytesMut}; + + pub fn write_content_length(n: usize, bytes: &mut BytesMut) { + if n == 0 { + bytes.put_slice(b"\r\ncontent-length: 0\r\n"); + return; + } + + let mut buf = itoa::Buffer::new(); + + bytes.put_slice(b"\r\ncontent-length: "); + bytes.put_slice(buf.format(n).as_bytes()); + bytes.put_slice(b"\r\n"); + } +} + mod _new { use bytes::{BufMut, BytesMut}; diff --git a/actix-http/src/helpers.rs b/actix-http/src/helpers.rs index 5ac6c9be5..bbf358b66 100644 --- a/actix-http/src/helpers.rs +++ b/actix-http/src/helpers.rs @@ -31,103 +31,18 @@ pub(crate) fn write_status_line(version: Version, n: u16, bytes: &mut BytesMut) /// NOTE: bytes object has to contain enough space pub fn write_content_length(n: u64, bytes: &mut BytesMut) { + if n == 0 { + bytes.put_slice(b"\r\ncontent-length: 0\r\n"); + return; + } + + let mut buf = itoa::Buffer::new(); + bytes.put_slice(b"\r\ncontent-length: "); - - if n < 10 { - bytes.put_u8(DIGITS_START + (n as u8)); - } else if n < 100 { - let n = n as u8; - - let d10 = n / 10; - let d1 = n % 10; - - bytes.put_u8(DIGITS_START + d10); - bytes.put_u8(DIGITS_START + d1); - } else if n < 1000 { - let n = n as u16; - - let d100 = (n / 100) as u8; - let d10 = ((n / 10) % 10) as u8; - let d1 = (n % 10) as u8; - - bytes.put_u8(DIGITS_START + d100); - bytes.put_u8(DIGITS_START + d10); - bytes.put_u8(DIGITS_START + d1); - } else if n < 10_000 { - let n = n as u16; - - let d1000 = (n / 1000) as u8; - let d100 = ((n / 100) % 10) as u8; - let d10 = ((n / 10) % 10) as u8; - let d1 = (n % 10) as u8; - - bytes.put_u8(DIGITS_START + d1000); - bytes.put_u8(DIGITS_START + d100); - bytes.put_u8(DIGITS_START + d10); - bytes.put_u8(DIGITS_START + d1); - } else if n < 100_000 { - let n = n as u32; - - let d10000 = (n / 10000) as u8; - let d1000 = ((n / 1000) % 10) as u8; - let d100 = ((n / 100) % 10) as u8; - let d10 = ((n / 10) % 10) as u8; - let d1 = (n % 10) as u8; - - bytes.put_u8(DIGITS_START + d10000); - bytes.put_u8(DIGITS_START + d1000); - bytes.put_u8(DIGITS_START + d100); - bytes.put_u8(DIGITS_START + d10); - bytes.put_u8(DIGITS_START + d1); - } else if n < 1_000_000 { - let n = n as u32; - - let d100000 = (n / 100_000) as u8; - let d10000 = ((n / 10000) % 10) as u8; - let d1000 = ((n / 1000) % 10) as u8; - let d100 = ((n / 100) % 10) as u8; - let d10 = ((n / 10) % 10) as u8; - let d1 = (n % 10) as u8; - - bytes.put_u8(DIGITS_START + d100000); - bytes.put_u8(DIGITS_START + d10000); - bytes.put_u8(DIGITS_START + d1000); - bytes.put_u8(DIGITS_START + d100); - bytes.put_u8(DIGITS_START + d10); - bytes.put_u8(DIGITS_START + d1); - } else { - write_u64(n, bytes); - } - + bytes.put_slice(buf.format(n).as_bytes()); bytes.put_slice(b"\r\n"); } -pub(crate) fn write_u64(n: u64, bytes: &mut BytesMut) { - let mut n = n; - - // 20 chars is max length of a u64 (2^64) - // digits will be added to the buffer from lsd to msd - let mut buf = BytesMut::with_capacity(20); - - while n > 9 { - // "pop" the least-significant digit - let lsd = (n % 10) as u8; - - // remove the lsd from n - n /= 10; - - buf.put_u8(DIGITS_START + lsd); - } - - // put msd to result buffer - bytes.put_u8(DIGITS_START + (n as u8)); - - // put, in reverse (msd to lsd), remaining digits to buffer - for i in (0..buf.len()).rev() { - bytes.put_u8(buf[i]); - } -} - pub(crate) struct Writer<'a>(pub &'a mut BytesMut); impl<'a> io::Write for Writer<'a> { From 9c5a2d658062d752810da83a2cba301fca331f82 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 22 May 2020 13:14:01 +0900 Subject: [PATCH 147/157] Add links to Gitter on the issue template --- .github/ISSUE_TEMPLATE/config.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..3e62958d8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: true +contact_links: + - name: Gitter channel (actix-web) + url: https://gitter.im/actix/actix-web + about: Please ask and answer questions about the actix-web here. + - name: Gitter channel (actix) + url: https://gitter.im/actix/actix + about: Please ask and answer questions about the actix here. From 905f86b540601c43302344f463928c26145d3af9 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 22 May 2020 13:54:17 +0900 Subject: [PATCH 148/157] http-test: Bump up to 2.0.0-alpha.1 --- test-server/CHANGES.md | 2 +- test-server/Cargo.toml | 2 +- test-server/src/lib.rs | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test-server/CHANGES.md b/test-server/CHANGES.md index e97ee5d6f..ce7ec6dfb 100644 --- a/test-server/CHANGES.md +++ b/test-server/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [Unreleased] - 2020-xx-xx +## [2.0.0-alpha.1] - 2020-05-22 * Update the `time` dependency to 0.2.7 * Update `actix-connect` dependency to 2.0.0-alpha.2 diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 2fa0691c3..8a8c0c8f0 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http-test" -version = "1.0.0" +version = "2.0.0-alpha.1" authors = ["Nikolay Kim "] description = "Actix http test server" readme = "README.md" diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index 265d9a771..f6c1183b4 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -104,7 +104,8 @@ pub async fn test_server>(factory: F) -> TestServer /// Get first available unused address pub fn unused_addr() -> net::SocketAddr { let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); - let socket = Socket::new(Domain::ipv4(), Type::stream(), Some(Protocol::tcp())).unwrap(); + let socket = + Socket::new(Domain::ipv4(), Type::stream(), Some(Protocol::tcp())).unwrap(); socket.bind(&addr.into()).unwrap(); socket.set_reuse_address(true).unwrap(); let tcp = socket.into_tcp_listener(); From 4a955c425db4a776c68127d0399587639a8496e1 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 22 May 2020 13:54:41 +0900 Subject: [PATCH 149/157] Update `actix-http-test` dependency to 2.0.0-alpha.1 --- actix-framed/Cargo.toml | 2 +- actix-http/Cargo.toml | 2 +- awc/Cargo.toml | 2 +- test-server/CHANGES.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index cae7939fd..494dd6f42 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -33,5 +33,5 @@ log = "0.4" [dev-dependencies] actix-server = "1.0.0" actix-connect = { version = "2.0.0-alpha.2", features = ["openssl"] } -actix-http-test = { version = "1.0.0", features = ["openssl"] } +actix-http-test = { version = "2.0.0-alpha.1", features = ["openssl"] } actix-utils = "1.0.3" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index deb63197a..edd8201d5 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -90,7 +90,7 @@ flate2 = { version = "1.0.13", optional = true } [dev-dependencies] actix-server = "1.0.1" actix-connect = { version = "2.0.0-alpha.2", features = ["openssl"] } -actix-http-test = { version = "1.0.0", features = ["openssl"] } +actix-http-test = { version = "2.0.0-alpha.1", features = ["openssl"] } actix-tls = { version = "2.0.0-alpha.1", features = ["openssl"] } criterion = "0.3" env_logger = "0.7" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 5e720ca87..b36e735ca 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -57,7 +57,7 @@ rust-tls = { version = "0.17.0", package = "rustls", optional = true, features = actix-connect = { version = "2.0.0-alpha.2", features = ["openssl"] } actix-web = { version = "3.0.0-alpha.3", features = ["openssl"] } actix-http = { version = "2.0.0-alpha.4", features = ["openssl"] } -actix-http-test = { version = "1.0.0", features = ["openssl"] } +actix-http-test = { version = "2.0.0-alpha.1", features = ["openssl"] } actix-utils = "1.0.3" actix-server = "1.0.0" actix-tls = { version = "2.0.0-alpha.1", features = ["openssl", "rustls"] } diff --git a/test-server/CHANGES.md b/test-server/CHANGES.md index ce7ec6dfb..079cad74a 100644 --- a/test-server/CHANGES.md +++ b/test-server/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [2.0.0-alpha.1] - 2020-05-22 +## [2.0.0-alpha.1] - 2020-05-23 * Update the `time` dependency to 0.2.7 * Update `actix-connect` dependency to 2.0.0-alpha.2 From bb89d04080735340f86d41bbb562c3514d9d0f89 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sat, 23 May 2020 17:22:30 +0900 Subject: [PATCH 150/157] codegen: Bump up to 0.2.2 --- Cargo.toml | 2 +- actix-web-codegen/CHANGES.md | 6 ++++-- actix-web-codegen/Cargo.toml | 8 ++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 83d64f8f1..74aa284e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,7 @@ actix-macros = "0.1.0" actix-threadpool = "0.3.1" actix-tls = "2.0.0-alpha.1" -actix-web-codegen = "0.2.0" +actix-web-codegen = "0.2.2" actix-http = "2.0.0-alpha.4" awc = { version = "2.0.0-alpha.2", default-features = false } diff --git a/actix-web-codegen/CHANGES.md b/actix-web-codegen/CHANGES.md index da2faee38..b2e80591f 100644 --- a/actix-web-codegen/CHANGES.md +++ b/actix-web-codegen/CHANGES.md @@ -1,8 +1,10 @@ # Changes -## [Unreleased] - 2020-xx-xx +## [0.2.2] - 2020-05-23 -* Bump minimum supported Rust version to 1.40 +* Add resource middleware on actix-web-codegen [#1467] + +[#1467]: https://github.com/actix/actix-web/pull/1467 ## [0.2.1] - 2020-02-25 diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index d57022d4d..3b2cdad3b 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web-codegen" -version = "0.2.1" +version = "0.2.2" description = "Actix web proc macros" readme = "README.md" authors = ["Nikolay Kim "] @@ -12,9 +12,9 @@ workspace = ".." proc-macro = true [dependencies] -quote = "^1" -syn = { version = "^1", features = ["full", "parsing"] } -proc-macro2 = "^1" +quote = "1" +syn = { version = "1", features = ["full", "parsing"] } +proc-macro2 = "1" [dev-dependencies] actix-rt = "1.0.0" From 75a34dc8bcce09762c9b50acfc110022a3636cb1 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sat, 23 May 2020 18:47:08 +0900 Subject: [PATCH 151/157] files: Bump up to 0.3.0-alpha.1 --- actix-files/CHANGES.md | 8 ++++++-- actix-files/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index 22c98d7c5..abf143997 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -1,9 +1,13 @@ # Changes -## [Unreleased] - 2020-xx-xx +## [0.3.0-alpha.1] - 2020-05-23 +* Update `actix-web` and `actix-http` dependencies to alpha +* Fix some typos in the docs * Bump minimum supported Rust version to 1.40 -* Support sending Content-Length when Content-Range is specified #1384 +* Support sending Content-Length when Content-Range is specified [#1384] + +[#1384]: https://github.com/actix/actix-web/pull/1384 ## [0.2.1] - 2019-12-22 diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index f84a70fe5..356c7a413 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.2.1" +version = "0.3.0-alpha.1" authors = ["Nikolay Kim "] description = "Static files support for actix web." readme = "README.md" From 8a106c07b4eb48f183762a80d2482ce24ff0188c Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 25 May 2020 16:45:34 +0900 Subject: [PATCH 152/157] Tweak codegen metadata --- actix-web-codegen/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 3b2cdad3b..60480a7a1 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -3,6 +3,9 @@ name = "actix-web-codegen" version = "0.2.2" description = "Actix web proc macros" readme = "README.md" +homepage = "https://actix.rs" +repository = "https://github.com/actix/actix-web" +documentation = "https://docs.rs/actix-web-codegen" authors = ["Nikolay Kim "] license = "MIT/Apache-2.0" edition = "2018" From 482f74e4095274701babb71557d36114f3a9fe04 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 25 May 2020 19:12:20 +0900 Subject: [PATCH 153/157] multipart: Bump up to 0.3.0-alpha.1 --- actix-multipart/CHANGES.md | 6 ++++-- actix-multipart/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/actix-multipart/CHANGES.md b/actix-multipart/CHANGES.md index e7bea62c0..df3cecf71 100644 --- a/actix-multipart/CHANGES.md +++ b/actix-multipart/CHANGES.md @@ -1,10 +1,12 @@ # Changes -## [Unreleased] - 2020-xx-xx +## [0.3.0-alpha.1] - 2020-05-25 + +* Update `actix-web` to 3.0.0-alpha.3 * Bump minimum supported Rust version to 1.40 -## [0.2.1] - 2020-01-xx +* Minimize `futures` dependencies * Remove the unused `time` dependency diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index e12c07bf1..c5f315d75 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-multipart" -version = "0.2.0" +version = "0.3.0-alpha.1" authors = ["Nikolay Kim "] description = "Multipart support for actix web framework." readme = "README.md" From 322e7c15d137dc5b65c3a75bc444d7e90728655f Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 31 May 2020 05:50:32 +0900 Subject: [PATCH 154/157] Remove actix-framed from workspace --- Cargo.toml | 1 - actix-framed/Cargo.toml | 37 ----- actix-framed/LICENSE-APACHE | 201 --------------------------- actix-framed/LICENSE-MIT | 25 ---- actix-framed/README.md | 9 +- actix-framed/changes.md | 28 ---- actix-framed/src/app.rs | 221 ------------------------------ actix-framed/src/helpers.rs | 98 ------------- actix-framed/src/lib.rs | 17 --- actix-framed/src/request.rs | 172 ----------------------- actix-framed/src/route.rs | 159 --------------------- actix-framed/src/service.rs | 156 --------------------- actix-framed/src/state.rs | 29 ---- actix-framed/src/test.rs | 155 --------------------- actix-framed/tests/test_server.rs | 161 ---------------------- 15 files changed, 2 insertions(+), 1467 deletions(-) delete mode 100644 actix-framed/Cargo.toml delete mode 100644 actix-framed/LICENSE-APACHE delete mode 100644 actix-framed/LICENSE-MIT delete mode 100644 actix-framed/changes.md delete mode 100644 actix-framed/src/app.rs delete mode 100644 actix-framed/src/helpers.rs delete mode 100644 actix-framed/src/lib.rs delete mode 100644 actix-framed/src/request.rs delete mode 100644 actix-framed/src/route.rs delete mode 100644 actix-framed/src/service.rs delete mode 100644 actix-framed/src/state.rs delete mode 100644 actix-framed/src/test.rs delete mode 100644 actix-framed/tests/test_server.rs diff --git a/Cargo.toml b/Cargo.toml index 74aa284e4..4c3b11e28 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,6 @@ members = [ "awc", "actix-http", "actix-files", - "actix-framed", "actix-multipart", "actix-web-actors", "actix-web-codegen", diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml deleted file mode 100644 index 494dd6f42..000000000 --- a/actix-framed/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "actix-framed" -version = "0.3.0" -authors = ["Nikolay Kim "] -description = "Actix framed app server" -readme = "README.md" -keywords = ["http", "web", "framework", "async", "futures"] -homepage = "https://actix.rs" -repository = "https://github.com/actix/actix-web.git" -documentation = "https://docs.rs/actix-framed/" -categories = ["network-programming", "asynchronous", - "web-programming::http-server", - "web-programming::websocket"] -license = "MIT/Apache-2.0" -edition = "2018" - -[lib] -name = "actix_framed" -path = "src/lib.rs" - -[dependencies] -actix-codec = "0.2.0" -actix-service = "1.0.1" -actix-router = "0.2.1" -actix-rt = "1.0.0" -actix-http = "2.0.0-alpha.4" - -bytes = "0.5.3" -futures-util = { version = "0.3.5", default-features = false } -pin-project = "0.4.6" -log = "0.4" - -[dev-dependencies] -actix-server = "1.0.0" -actix-connect = { version = "2.0.0-alpha.2", features = ["openssl"] } -actix-http-test = { version = "2.0.0-alpha.1", features = ["openssl"] } -actix-utils = "1.0.3" diff --git a/actix-framed/LICENSE-APACHE b/actix-framed/LICENSE-APACHE deleted file mode 100644 index 6cdf2d16c..000000000 --- a/actix-framed/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2017-NOW Nikolay Kim - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/actix-framed/LICENSE-MIT b/actix-framed/LICENSE-MIT deleted file mode 100644 index 0f80296ae..000000000 --- a/actix-framed/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2017 Nikolay Kim - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/actix-framed/README.md b/actix-framed/README.md index 3a5ea0596..a4eaadf21 100644 --- a/actix-framed/README.md +++ b/actix-framed/README.md @@ -1,8 +1,3 @@ -# Framed app for actix web [![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) [![crates.io](https://meritbadge.herokuapp.com/actix-framed)](https://crates.io/crates/actix-framed) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +# Framed app for actix web -## Documentation & community resources - -* [API Documentation](https://docs.rs/actix-framed/) -* [Chat on gitter](https://gitter.im/actix/actix) -* Cargo package: [actix-framed](https://crates.io/crates/actix-framed) -* Minimum supported Rust version: 1.40 or later +**This crate has been deprecated and removed.** diff --git a/actix-framed/changes.md b/actix-framed/changes.md deleted file mode 100644 index 1c5d31fa2..000000000 --- a/actix-framed/changes.md +++ /dev/null @@ -1,28 +0,0 @@ -# Changes - -## [Unreleased] - 2020-xx-xx - -* Bump minimum supported Rust version to 1.40 - -## [0.3.0] - 2019-12-25 - -* Migrate to actix-http 1.0 - -## [0.2.1] - 2019-07-20 - -* Remove unneeded actix-utils dependency - - -## [0.2.0] - 2019-05-12 - -* Update dependencies - - -## [0.1.0] - 2019-04-16 - -* Update tests - - -## [0.1.0-alpha.1] - 2019-04-12 - -* Initial release diff --git a/actix-framed/src/app.rs b/actix-framed/src/app.rs deleted file mode 100644 index 2fddbc039..000000000 --- a/actix-framed/src/app.rs +++ /dev/null @@ -1,221 +0,0 @@ -use std::future::Future; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; - -use actix_codec::{AsyncRead, AsyncWrite, Framed}; -use actix_http::h1::{Codec, SendResponse}; -use actix_http::{Error, Request, Response}; -use actix_router::{Path, Router, Url}; -use actix_service::{IntoServiceFactory, Service, ServiceFactory}; -use futures_util::future::{ok, FutureExt, LocalBoxFuture}; - -use crate::helpers::{BoxedHttpNewService, BoxedHttpService, HttpNewService}; -use crate::request::FramedRequest; -use crate::state::State; - -type BoxedResponse = LocalBoxFuture<'static, Result<(), Error>>; - -pub trait HttpServiceFactory { - type Factory: ServiceFactory; - - fn path(&self) -> &str; - - fn create(self) -> Self::Factory; -} - -/// Application builder -pub struct FramedApp { - state: State, - services: Vec<(String, BoxedHttpNewService>)>, -} - -impl FramedApp { - pub fn new() -> Self { - FramedApp { - state: State::new(()), - services: Vec::new(), - } - } -} - -impl FramedApp { - pub fn with(state: S) -> FramedApp { - FramedApp { - services: Vec::new(), - state: State::new(state), - } - } - - pub fn service(mut self, factory: U) -> Self - where - U: HttpServiceFactory, - U::Factory: ServiceFactory< - Config = (), - Request = FramedRequest, - Response = (), - Error = Error, - InitError = (), - > + 'static, - ::Future: 'static, - ::Service: Service< - Request = FramedRequest, - Response = (), - Error = Error, - Future = LocalBoxFuture<'static, Result<(), Error>>, - >, - { - let path = factory.path().to_string(); - self.services - .push((path, Box::new(HttpNewService::new(factory.create())))); - self - } -} - -impl IntoServiceFactory> for FramedApp -where - T: AsyncRead + AsyncWrite + Unpin + 'static, - S: 'static, -{ - fn into_factory(self) -> FramedAppFactory { - FramedAppFactory { - state: self.state, - services: Rc::new(self.services), - } - } -} - -#[derive(Clone)] -pub struct FramedAppFactory { - state: State, - services: Rc>)>>, -} - -impl ServiceFactory for FramedAppFactory -where - T: AsyncRead + AsyncWrite + Unpin + 'static, - S: 'static, -{ - type Config = (); - type Request = (Request, Framed); - type Response = (); - type Error = Error; - type InitError = (); - type Service = FramedAppService; - type Future = CreateService; - - fn new_service(&self, _: ()) -> Self::Future { - CreateService { - fut: self - .services - .iter() - .map(|(path, service)| { - CreateServiceItem::Future( - Some(path.clone()), - service.new_service(()), - ) - }) - .collect(), - state: self.state.clone(), - } - } -} - -#[doc(hidden)] -pub struct CreateService { - fut: Vec>, - state: State, -} - -enum CreateServiceItem { - Future( - Option, - LocalBoxFuture<'static, Result>, ()>>, - ), - Service(String, BoxedHttpService>), -} - -impl Future for CreateService -where - T: AsyncRead + AsyncWrite + Unpin, -{ - type Output = Result, ()>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let mut done = true; - - // poll http services - for item in &mut self.fut { - let res = match item { - CreateServiceItem::Future(ref mut path, ref mut fut) => { - match Pin::new(fut).poll(cx) { - Poll::Ready(Ok(service)) => { - Some((path.take().unwrap(), service)) - } - Poll::Ready(Err(e)) => return Poll::Ready(Err(e)), - Poll::Pending => { - done = false; - None - } - } - } - CreateServiceItem::Service(_, _) => continue, - }; - - if let Some((path, service)) = res { - *item = CreateServiceItem::Service(path, service); - } - } - - if done { - let router = self - .fut - .drain(..) - .fold(Router::build(), |mut router, item| { - match item { - CreateServiceItem::Service(path, service) => { - router.path(&path, service); - } - CreateServiceItem::Future(_, _) => unreachable!(), - } - router - }); - Poll::Ready(Ok(FramedAppService { - router: router.finish(), - state: self.state.clone(), - })) - } else { - Poll::Pending - } - } -} - -pub struct FramedAppService { - state: State, - router: Router>>, -} - -impl Service for FramedAppService -where - T: AsyncRead + AsyncWrite + Unpin, -{ - type Request = (Request, Framed); - type Response = (); - type Error = Error; - type Future = BoxedResponse; - - fn poll_ready(&mut self, _: &mut Context) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, (req, framed): (Request, Framed)) -> Self::Future { - let mut path = Path::new(Url::new(req.uri().clone())); - - if let Some((srv, _info)) = self.router.recognize_mut(&mut path) { - return srv.call(FramedRequest::new(req, framed, path, self.state.clone())); - } - SendResponse::new(framed, Response::NotFound().finish()) - .then(|_| ok(())) - .boxed_local() - } -} diff --git a/actix-framed/src/helpers.rs b/actix-framed/src/helpers.rs deleted file mode 100644 index d83736a52..000000000 --- a/actix-framed/src/helpers.rs +++ /dev/null @@ -1,98 +0,0 @@ -use std::task::{Context, Poll}; - -use actix_http::Error; -use actix_service::{Service, ServiceFactory}; -use futures_util::future::{FutureExt, LocalBoxFuture}; - -pub(crate) type BoxedHttpService = Box< - dyn Service< - Request = Req, - Response = (), - Error = Error, - Future = LocalBoxFuture<'static, Result<(), Error>>, - >, ->; - -pub(crate) type BoxedHttpNewService = Box< - dyn ServiceFactory< - Config = (), - Request = Req, - Response = (), - Error = Error, - InitError = (), - Service = BoxedHttpService, - Future = LocalBoxFuture<'static, Result, ()>>, - >, ->; - -pub(crate) struct HttpNewService(T); - -impl HttpNewService -where - T: ServiceFactory, - T::Response: 'static, - T::Future: 'static, - T::Service: Service>> + 'static, - ::Future: 'static, -{ - pub fn new(service: T) -> Self { - HttpNewService(service) - } -} - -impl ServiceFactory for HttpNewService -where - T: ServiceFactory, - T::Request: 'static, - T::Future: 'static, - T::Service: Service>> + 'static, - ::Future: 'static, -{ - type Config = (); - type Request = T::Request; - type Response = (); - type Error = Error; - type InitError = (); - type Service = BoxedHttpService; - type Future = LocalBoxFuture<'static, Result>; - - fn new_service(&self, _: ()) -> Self::Future { - let fut = self.0.new_service(()); - - async move { - fut.await.map_err(|_| ()).map(|service| { - let service: BoxedHttpService<_> = - Box::new(HttpServiceWrapper { service }); - service - }) - } - .boxed_local() - } -} - -struct HttpServiceWrapper { - service: T, -} - -impl Service for HttpServiceWrapper -where - T: Service< - Response = (), - Future = LocalBoxFuture<'static, Result<(), Error>>, - Error = Error, - >, - T::Request: 'static, -{ - type Request = T::Request; - type Response = (); - type Error = Error; - type Future = LocalBoxFuture<'static, Result<(), Error>>; - - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { - self.service.poll_ready(cx) - } - - fn call(&mut self, req: Self::Request) -> Self::Future { - self.service.call(req) - } -} diff --git a/actix-framed/src/lib.rs b/actix-framed/src/lib.rs deleted file mode 100644 index 9d2c71250..000000000 --- a/actix-framed/src/lib.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![allow(clippy::type_complexity, clippy::new_without_default, dead_code)] -mod app; -mod helpers; -mod request; -mod route; -mod service; -mod state; -pub mod test; - -// re-export for convenience -pub use actix_http::{http, Error, HttpMessage, Response, ResponseError}; - -pub use self::app::{FramedApp, FramedAppService}; -pub use self::request::FramedRequest; -pub use self::route::FramedRoute; -pub use self::service::{SendError, VerifyWebSockets}; -pub use self::state::State; diff --git a/actix-framed/src/request.rs b/actix-framed/src/request.rs deleted file mode 100644 index 9a6cc8e39..000000000 --- a/actix-framed/src/request.rs +++ /dev/null @@ -1,172 +0,0 @@ -use std::cell::{Ref, RefMut}; - -use actix_codec::Framed; -use actix_http::http::{HeaderMap, Method, Uri, Version}; -use actix_http::{h1::Codec, Extensions, Request, RequestHead}; -use actix_router::{Path, Url}; - -use crate::state::State; - -pub struct FramedRequest { - req: Request, - framed: Framed, - state: State, - pub(crate) path: Path, -} - -impl FramedRequest { - pub fn new( - req: Request, - framed: Framed, - path: Path, - state: State, - ) -> Self { - Self { - req, - framed, - state, - path, - } - } -} - -impl FramedRequest { - /// Split request into a parts - pub fn into_parts(self) -> (Request, Framed, State) { - (self.req, self.framed, self.state) - } - - /// This method returns reference to the request head - #[inline] - pub fn head(&self) -> &RequestHead { - self.req.head() - } - - /// This method returns mutable reference to the request head. - /// panics if multiple references of http request exists. - #[inline] - pub fn head_mut(&mut self) -> &mut RequestHead { - self.req.head_mut() - } - - /// Shared application state - #[inline] - pub fn state(&self) -> &S { - self.state.get_ref() - } - - /// Request's uri. - #[inline] - pub fn uri(&self) -> &Uri { - &self.head().uri - } - - /// Read the Request method. - #[inline] - pub fn method(&self) -> &Method { - &self.head().method - } - - /// Read the Request Version. - #[inline] - pub fn version(&self) -> Version { - self.head().version - } - - #[inline] - /// Returns request's headers. - pub fn headers(&self) -> &HeaderMap { - &self.head().headers - } - - /// The target path of this Request. - #[inline] - pub fn path(&self) -> &str { - self.head().uri.path() - } - - /// The query string in the URL. - /// - /// E.g., id=10 - #[inline] - pub fn query_string(&self) -> &str { - if let Some(query) = self.uri().query().as_ref() { - query - } else { - "" - } - } - - /// Get a reference to the Path parameters. - /// - /// Params is a container for url parameters. - /// A variable segment is specified in the form `{identifier}`, - /// where the identifier can be used later in a request handler to - /// access the matched value for that segment. - #[inline] - pub fn match_info(&self) -> &Path { - &self.path - } - - /// Request extensions - #[inline] - pub fn extensions(&self) -> Ref { - self.head().extensions() - } - - /// Mutable reference to a the request's extensions - #[inline] - pub fn extensions_mut(&self) -> RefMut { - self.head().extensions_mut() - } -} - -#[cfg(test)] -mod tests { - use std::convert::TryFrom; - - use actix_http::http::{HeaderName, HeaderValue}; - use actix_http::test::{TestBuffer, TestRequest}; - - use super::*; - - #[test] - fn test_request() { - let buf = TestBuffer::empty(); - let framed = Framed::new(buf, Codec::default()); - let req = TestRequest::with_uri("/index.html?q=1") - .header("content-type", "test") - .finish(); - let path = Path::new(Url::new(req.uri().clone())); - - let mut freq = FramedRequest::new(req, framed, path, State::new(10u8)); - assert_eq!(*freq.state(), 10); - assert_eq!(freq.version(), Version::HTTP_11); - assert_eq!(freq.method(), Method::GET); - assert_eq!(freq.path(), "/index.html"); - assert_eq!(freq.query_string(), "q=1"); - assert_eq!( - freq.headers() - .get("content-type") - .unwrap() - .to_str() - .unwrap(), - "test" - ); - - freq.head_mut().headers.insert( - HeaderName::try_from("x-hdr").unwrap(), - HeaderValue::from_static("test"), - ); - assert_eq!( - freq.headers().get("x-hdr").unwrap().to_str().unwrap(), - "test" - ); - - freq.extensions_mut().insert(100usize); - assert_eq!(*freq.extensions().get::().unwrap(), 100usize); - - let (_, _, state) = freq.into_parts(); - assert_eq!(*state, 10); - } -} diff --git a/actix-framed/src/route.rs b/actix-framed/src/route.rs deleted file mode 100644 index 7b9004b64..000000000 --- a/actix-framed/src/route.rs +++ /dev/null @@ -1,159 +0,0 @@ -use std::fmt; -use std::future::Future; -use std::marker::PhantomData; -use std::task::{Context, Poll}; - -use actix_codec::{AsyncRead, AsyncWrite}; -use actix_http::{http::Method, Error}; -use actix_service::{Service, ServiceFactory}; -use futures_util::future::{ok, FutureExt, LocalBoxFuture, Ready}; -use log::error; - -use crate::app::HttpServiceFactory; -use crate::request::FramedRequest; - -/// Resource route definition -/// -/// Route uses builder-like pattern for configuration. -/// If handler is not explicitly set, default *404 Not Found* handler is used. -pub struct FramedRoute { - handler: F, - pattern: String, - methods: Vec, - state: PhantomData<(Io, S, R, E)>, -} - -impl FramedRoute { - pub fn new(pattern: &str) -> Self { - FramedRoute { - handler: (), - pattern: pattern.to_string(), - methods: Vec::new(), - state: PhantomData, - } - } - - pub fn get(path: &str) -> FramedRoute { - FramedRoute::new(path).method(Method::GET) - } - - pub fn post(path: &str) -> FramedRoute { - FramedRoute::new(path).method(Method::POST) - } - - pub fn put(path: &str) -> FramedRoute { - FramedRoute::new(path).method(Method::PUT) - } - - pub fn delete(path: &str) -> FramedRoute { - FramedRoute::new(path).method(Method::DELETE) - } - - pub fn method(mut self, method: Method) -> Self { - self.methods.push(method); - self - } - - pub fn to(self, handler: F) -> FramedRoute - where - F: FnMut(FramedRequest) -> R, - R: Future> + 'static, - - E: fmt::Debug, - { - FramedRoute { - handler, - pattern: self.pattern, - methods: self.methods, - state: PhantomData, - } - } -} - -impl HttpServiceFactory for FramedRoute -where - Io: AsyncRead + AsyncWrite + 'static, - F: FnMut(FramedRequest) -> R + Clone, - R: Future> + 'static, - E: fmt::Display, -{ - type Factory = FramedRouteFactory; - - fn path(&self) -> &str { - &self.pattern - } - - fn create(self) -> Self::Factory { - FramedRouteFactory { - handler: self.handler, - methods: self.methods, - _t: PhantomData, - } - } -} - -pub struct FramedRouteFactory { - handler: F, - methods: Vec, - _t: PhantomData<(Io, S, R, E)>, -} - -impl ServiceFactory for FramedRouteFactory -where - Io: AsyncRead + AsyncWrite + 'static, - F: FnMut(FramedRequest) -> R + Clone, - R: Future> + 'static, - E: fmt::Display, -{ - type Config = (); - type Request = FramedRequest; - type Response = (); - type Error = Error; - type InitError = (); - type Service = FramedRouteService; - type Future = Ready>; - - fn new_service(&self, _: ()) -> Self::Future { - ok(FramedRouteService { - handler: self.handler.clone(), - methods: self.methods.clone(), - _t: PhantomData, - }) - } -} - -pub struct FramedRouteService { - handler: F, - methods: Vec, - _t: PhantomData<(Io, S, R, E)>, -} - -impl Service for FramedRouteService -where - Io: AsyncRead + AsyncWrite + 'static, - F: FnMut(FramedRequest) -> R + Clone, - R: Future> + 'static, - E: fmt::Display, -{ - type Request = FramedRequest; - type Response = (); - type Error = Error; - type Future = LocalBoxFuture<'static, Result<(), Error>>; - - fn poll_ready(&mut self, _: &mut Context) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: FramedRequest) -> Self::Future { - let fut = (self.handler)(req); - - async move { - let res = fut.await; - if let Err(e) = res { - error!("Error in request handler: {}", e); - } - Ok(()) - } - .boxed_local() - } -} diff --git a/actix-framed/src/service.rs b/actix-framed/src/service.rs deleted file mode 100644 index dd61f298d..000000000 --- a/actix-framed/src/service.rs +++ /dev/null @@ -1,156 +0,0 @@ -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; - -use actix_codec::{AsyncRead, AsyncWrite, Framed}; -use actix_http::body::BodySize; -use actix_http::error::ResponseError; -use actix_http::h1::{Codec, Message}; -use actix_http::ws::{verify_handshake, HandshakeError}; -use actix_http::{Request, Response}; -use actix_service::{Service, ServiceFactory}; -use futures_util::future::{err, ok, Either, Ready}; - -/// Service that verifies incoming request if it is valid websocket -/// upgrade request. In case of error returns `HandshakeError` -pub struct VerifyWebSockets { - _t: PhantomData<(T, C)>, -} - -impl Default for VerifyWebSockets { - fn default() -> Self { - VerifyWebSockets { _t: PhantomData } - } -} - -impl ServiceFactory for VerifyWebSockets { - type Config = C; - type Request = (Request, Framed); - type Response = (Request, Framed); - type Error = (HandshakeError, Framed); - type InitError = (); - type Service = VerifyWebSockets; - type Future = Ready>; - - fn new_service(&self, _: C) -> Self::Future { - ok(VerifyWebSockets { _t: PhantomData }) - } -} - -impl Service for VerifyWebSockets { - type Request = (Request, Framed); - type Response = (Request, Framed); - type Error = (HandshakeError, Framed); - type Future = Ready>; - - fn poll_ready(&mut self, _: &mut Context) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, (req, framed): (Request, Framed)) -> Self::Future { - match verify_handshake(req.head()) { - Err(e) => err((e, framed)), - Ok(_) => ok((req, framed)), - } - } -} - -/// Send http/1 error response -pub struct SendError(PhantomData<(T, R, E, C)>); - -impl Default for SendError -where - T: AsyncRead + AsyncWrite, - E: ResponseError, -{ - fn default() -> Self { - SendError(PhantomData) - } -} - -impl ServiceFactory for SendError -where - T: AsyncRead + AsyncWrite + Unpin + 'static, - R: 'static, - E: ResponseError + 'static, -{ - type Config = C; - type Request = Result)>; - type Response = R; - type Error = (E, Framed); - type InitError = (); - type Service = SendError; - type Future = Ready>; - - fn new_service(&self, _: C) -> Self::Future { - ok(SendError(PhantomData)) - } -} - -impl Service for SendError -where - T: AsyncRead + AsyncWrite + Unpin + 'static, - R: 'static, - E: ResponseError + 'static, -{ - type Request = Result)>; - type Response = R; - type Error = (E, Framed); - type Future = Either)>>, SendErrorFut>; - - fn poll_ready(&mut self, _: &mut Context) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: Result)>) -> Self::Future { - match req { - Ok(r) => Either::Left(ok(r)), - Err((e, framed)) => { - let res = e.error_response().drop_body(); - Either::Right(SendErrorFut { - framed: Some(framed), - res: Some((res, BodySize::Empty).into()), - err: Some(e), - _t: PhantomData, - }) - } - } - } -} - -#[pin_project::pin_project] -pub struct SendErrorFut { - res: Option, BodySize)>>, - framed: Option>, - err: Option, - _t: PhantomData, -} - -impl Future for SendErrorFut -where - E: ResponseError, - T: AsyncRead + AsyncWrite + Unpin, -{ - type Output = Result)>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { - if let Some(res) = self.res.take() { - if self.framed.as_mut().unwrap().write(res).is_err() { - return Poll::Ready(Err(( - self.err.take().unwrap(), - self.framed.take().unwrap(), - ))); - } - } - match self.framed.as_mut().unwrap().flush(cx) { - Poll::Ready(Ok(_)) => { - Poll::Ready(Err((self.err.take().unwrap(), self.framed.take().unwrap()))) - } - Poll::Ready(Err(_)) => { - Poll::Ready(Err((self.err.take().unwrap(), self.framed.take().unwrap()))) - } - Poll::Pending => Poll::Pending, - } - } -} diff --git a/actix-framed/src/state.rs b/actix-framed/src/state.rs deleted file mode 100644 index 600a639ca..000000000 --- a/actix-framed/src/state.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::ops::Deref; -use std::sync::Arc; - -/// Application state -pub struct State(Arc); - -impl State { - pub fn new(state: S) -> State { - State(Arc::new(state)) - } - - pub fn get_ref(&self) -> &S { - self.0.as_ref() - } -} - -impl Deref for State { - type Target = S; - - fn deref(&self) -> &S { - self.0.as_ref() - } -} - -impl Clone for State { - fn clone(&self) -> State { - State(self.0.clone()) - } -} diff --git a/actix-framed/src/test.rs b/actix-framed/src/test.rs deleted file mode 100644 index b8029531e..000000000 --- a/actix-framed/src/test.rs +++ /dev/null @@ -1,155 +0,0 @@ -//! Various helpers for Actix applications to use during testing. -use std::convert::TryFrom; -use std::future::Future; - -use actix_codec::Framed; -use actix_http::h1::Codec; -use actix_http::http::header::{Header, HeaderName, IntoHeaderValue}; -use actix_http::http::{Error as HttpError, Method, Uri, Version}; -use actix_http::test::{TestBuffer, TestRequest as HttpTestRequest}; -use actix_router::{Path, Url}; - -use crate::{FramedRequest, State}; - -/// Test `Request` builder. -pub struct TestRequest { - req: HttpTestRequest, - path: Path, - state: State, -} - -impl Default for TestRequest<()> { - fn default() -> TestRequest { - TestRequest { - req: HttpTestRequest::default(), - path: Path::new(Url::new(Uri::default())), - state: State::new(()), - } - } -} - -impl TestRequest<()> { - /// Create TestRequest and set request uri - pub fn with_uri(path: &str) -> Self { - Self::get().uri(path) - } - - /// Create TestRequest and set header - pub fn with_hdr(hdr: H) -> Self { - Self::default().set(hdr) - } - - /// Create TestRequest and set header - pub fn with_header(key: K, value: V) -> Self - where - HeaderName: TryFrom, - >::Error: Into, - V: IntoHeaderValue, - { - Self::default().header(key, value) - } - - /// Create TestRequest and set method to `Method::GET` - pub fn get() -> Self { - Self::default().method(Method::GET) - } - - /// Create TestRequest and set method to `Method::POST` - pub fn post() -> Self { - Self::default().method(Method::POST) - } -} - -impl TestRequest { - /// Create TestRequest and set request uri - pub fn with_state(state: S) -> TestRequest { - let req = TestRequest::get(); - TestRequest { - state: State::new(state), - req: req.req, - path: req.path, - } - } - - /// Set HTTP version of this request - pub fn version(mut self, ver: Version) -> Self { - self.req.version(ver); - self - } - - /// Set HTTP method of this request - pub fn method(mut self, meth: Method) -> Self { - self.req.method(meth); - self - } - - /// Set HTTP Uri of this request - pub fn uri(mut self, path: &str) -> Self { - self.req.uri(path); - self - } - - /// Set a header - pub fn set(mut self, hdr: H) -> Self { - self.req.set(hdr); - self - } - - /// Set a header - pub fn header(mut self, key: K, value: V) -> Self - where - HeaderName: TryFrom, - >::Error: Into, - V: IntoHeaderValue, - { - self.req.header(key, value); - self - } - - /// Set request path pattern parameter - pub fn param(mut self, name: &'static str, value: &'static str) -> Self { - self.path.add_static(name, value); - self - } - - /// Complete request creation and generate `Request` instance - pub fn finish(mut self) -> FramedRequest { - let req = self.req.finish(); - self.path.get_mut().update(req.uri()); - let framed = Framed::new(TestBuffer::empty(), Codec::default()); - FramedRequest::new(req, framed, self.path, self.state) - } - - /// This method generates `FramedRequest` instance and executes async handler - pub async fn run(self, f: F) -> Result - where - F: FnOnce(FramedRequest) -> R, - R: Future>, - { - f(self.finish()).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test() { - let req = TestRequest::with_uri("/index.html") - .header("x-test", "test") - .param("test", "123") - .finish(); - - assert_eq!(*req.state(), ()); - assert_eq!(req.version(), Version::HTTP_11); - assert_eq!(req.method(), Method::GET); - assert_eq!(req.path(), "/index.html"); - assert_eq!(req.query_string(), ""); - assert_eq!( - req.headers().get("x-test").unwrap().to_str().unwrap(), - "test" - ); - assert_eq!(&req.match_info()["test"], "123"); - } -} diff --git a/actix-framed/tests/test_server.rs b/actix-framed/tests/test_server.rs deleted file mode 100644 index ec6897861..000000000 --- a/actix-framed/tests/test_server.rs +++ /dev/null @@ -1,161 +0,0 @@ -use actix_codec::{AsyncRead, AsyncWrite}; -use actix_http::{body, http::StatusCode, ws, Error, HttpService, Response}; -use actix_http_test::test_server; -use actix_service::{pipeline_factory, IntoServiceFactory, ServiceFactory}; -use actix_utils::framed::Dispatcher; -use bytes::Bytes; -use futures_util::{future, SinkExt, StreamExt}; - -use actix_framed::{FramedApp, FramedRequest, FramedRoute, SendError, VerifyWebSockets}; - -async fn ws_service( - req: FramedRequest, -) -> Result<(), Error> { - let (req, mut framed, _) = req.into_parts(); - let res = ws::handshake(req.head()).unwrap().message_body(()); - - framed - .send((res, body::BodySize::None).into()) - .await - .unwrap(); - Dispatcher::new(framed.into_framed(ws::Codec::new()), service) - .await - .unwrap(); - - Ok(()) -} - -async fn service(msg: ws::Frame) -> Result { - let msg = match msg { - ws::Frame::Ping(msg) => ws::Message::Pong(msg), - ws::Frame::Text(text) => { - ws::Message::Text(String::from_utf8_lossy(&text).to_string()) - } - ws::Frame::Binary(bin) => ws::Message::Binary(bin), - ws::Frame::Close(reason) => ws::Message::Close(reason), - _ => panic!(), - }; - Ok(msg) -} - -#[actix_rt::test] -async fn test_simple() { - let mut srv = test_server(|| { - HttpService::build() - .upgrade( - FramedApp::new().service(FramedRoute::get("/index.html").to(ws_service)), - ) - .finish(|_| future::ok::<_, Error>(Response::NotFound())) - .tcp() - }) - .await; - - assert!(srv.ws_at("/test").await.is_err()); - - // client service - let mut framed = srv.ws_at("/index.html").await.unwrap(); - framed - .send(ws::Message::Text("text".to_string())) - .await - .unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Text(Bytes::from_static(b"text")) - ); - - framed - .send(ws::Message::Binary("text".into())) - .await - .unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Binary(Bytes::from_static(b"text")) - ); - - framed.send(ws::Message::Ping("text".into())).await.unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Pong("text".to_string().into()) - ); - - framed - .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) - .await - .unwrap(); - - let (item, _) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Close(Some(ws::CloseCode::Normal.into())) - ); -} - -#[actix_rt::test] -async fn test_service() { - let mut srv = test_server(|| { - pipeline_factory(actix_http::h1::OneRequest::new().map_err(|_| ())).and_then( - pipeline_factory( - pipeline_factory(VerifyWebSockets::default()) - .then(SendError::default()) - .map_err(|_| ()), - ) - .and_then( - FramedApp::new() - .service(FramedRoute::get("/index.html").to(ws_service)) - .into_factory() - .map_err(|_| ()), - ), - ) - }) - .await; - - // non ws request - let res = srv.get("/index.html").send().await.unwrap(); - assert_eq!(res.status(), StatusCode::BAD_REQUEST); - - // not found - assert!(srv.ws_at("/test").await.is_err()); - - // client service - let mut framed = srv.ws_at("/index.html").await.unwrap(); - framed - .send(ws::Message::Text("text".to_string())) - .await - .unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Text(Bytes::from_static(b"text")) - ); - - framed - .send(ws::Message::Binary("text".into())) - .await - .unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Binary(Bytes::from_static(b"text")) - ); - - framed.send(ws::Message::Ping("text".into())).await.unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Pong("text".to_string().into()) - ); - - framed - .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) - .await - .unwrap(); - - let (item, _) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Close(Some(ws::CloseCode::Normal.into())) - ); -} From 621ebec01adc87bdb31b5c1116773be8413bf53a Mon Sep 17 00:00:00 2001 From: Stephen Stack Date: Tue, 2 Jun 2020 12:04:49 -0500 Subject: [PATCH 155/157] Fix typo in timeout error display (#1552) --- actix-http/src/client/error.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actix-http/src/client/error.rs b/actix-http/src/client/error.rs index e4653a31a..ba697bca4 100644 --- a/actix-http/src/client/error.rs +++ b/actix-http/src/client/error.rs @@ -39,7 +39,7 @@ pub enum ConnectError { H2(h2::Error), /// Connecting took too long - #[display(fmt = "Timeout out while establishing connection")] + #[display(fmt = "Timeout while establishing connection")] Timeout, /// Connector has been disconnected @@ -110,7 +110,7 @@ pub enum SendRequestError { #[display(fmt = "{}", _0)] H2(h2::Error), /// Response took too long - #[display(fmt = "Timeout out while waiting for response")] + #[display(fmt = "Timeout while waiting for response")] Timeout, /// Tunnels are not supported for http2 connection #[display(fmt = "Tunnels are not supported for http2 connection")] From 5286b8aed742b5ca5c9fd55dc4af3bd30463514a Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 31 May 2020 16:32:59 +0900 Subject: [PATCH 156/157] Remove AppVeyor config --- .appveyor.yml | 41 ---------------------------------------- actix-http/.appveyor.yml | 41 ---------------------------------------- test-server/Cargo.toml | 2 +- 3 files changed, 1 insertion(+), 83 deletions(-) delete mode 100644 .appveyor.yml delete mode 100644 actix-http/.appveyor.yml diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 2f0a4a7dd..000000000 --- a/.appveyor.yml +++ /dev/null @@ -1,41 +0,0 @@ -environment: - global: - PROJECT_NAME: actix-web - matrix: - # Stable channel - - TARGET: i686-pc-windows-msvc - CHANNEL: stable - - TARGET: x86_64-pc-windows-gnu - CHANNEL: stable - - TARGET: x86_64-pc-windows-msvc - CHANNEL: stable - # Nightly channel - - TARGET: i686-pc-windows-msvc - CHANNEL: nightly - - TARGET: x86_64-pc-windows-gnu - CHANNEL: nightly - - TARGET: x86_64-pc-windows-msvc - CHANNEL: nightly - -# Install Rust and Cargo -# (Based on from https://github.com/rust-lang/libc/blob/master/appveyor.yml) -install: - - ps: >- - If ($Env:TARGET -eq 'x86_64-pc-windows-gnu') { - $Env:PATH += ';C:\msys64\mingw64\bin' - } ElseIf ($Env:TARGET -eq 'i686-pc-windows-gnu') { - $Env:PATH += ';C:\MinGW\bin' - } - - curl -sSf -o rustup-init.exe https://win.rustup.rs - - rustup-init.exe --default-host %TARGET% --default-toolchain %CHANNEL% -y - - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin - - rustc -Vv - - cargo -V - -# 'cargo test' takes care of building for us, so disable Appveyor's build stage. -build: false - -# Equivalent to Travis' `script` phase -test_script: - - cargo clean - - cargo test --no-default-features --features="flate2-rust" diff --git a/actix-http/.appveyor.yml b/actix-http/.appveyor.yml deleted file mode 100644 index 780fdd6b5..000000000 --- a/actix-http/.appveyor.yml +++ /dev/null @@ -1,41 +0,0 @@ -environment: - global: - PROJECT_NAME: actix-http - matrix: - # Stable channel - - TARGET: i686-pc-windows-msvc - CHANNEL: stable - - TARGET: x86_64-pc-windows-gnu - CHANNEL: stable - - TARGET: x86_64-pc-windows-msvc - CHANNEL: stable - # Nightly channel - - TARGET: i686-pc-windows-msvc - CHANNEL: nightly - - TARGET: x86_64-pc-windows-gnu - CHANNEL: nightly - - TARGET: x86_64-pc-windows-msvc - CHANNEL: nightly - -# Install Rust and Cargo -# (Based on from https://github.com/rust-lang/libc/blob/master/appveyor.yml) -install: - - ps: >- - If ($Env:TARGET -eq 'x86_64-pc-windows-gnu') { - $Env:PATH += ';C:\msys64\mingw64\bin' - } ElseIf ($Env:TARGET -eq 'i686-pc-windows-gnu') { - $Env:PATH += ';C:\MinGW\bin' - } - - curl -sSf -o rustup-init.exe https://win.rustup.rs - - rustup-init.exe --default-host %TARGET% --default-toolchain %CHANNEL% -y - - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin - - rustc -Vv - - cargo -V - -# 'cargo test' takes care of building for us, so disable Appveyor's build stage. -build: false - -# Equivalent to Travis' `script` phase -test_script: - - cargo clean - - cargo test diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 8a8c0c8f0..f90cef0dd 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -12,7 +12,7 @@ categories = ["network-programming", "asynchronous", "web-programming::http-server", "web-programming::websocket"] license = "MIT/Apache-2.0" -exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] +exclude = [".gitignore", ".cargo/config"] edition = "2018" workspace = ".." From 6c5c4ea2308fe3e78668cc8abdbfb41cf6d37db8 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 6 Jun 2020 06:44:14 +0900 Subject: [PATCH 157/157] Remove uses of pin_project::project attribute pin-project will deprecate the project attribute due to some unfixable limitations. Refs: https://github.com/taiki-e/pin-project/issues/225 --- Cargo.toml | 2 +- actix-http/Cargo.toml | 2 +- actix-http/src/body.rs | 28 ++++++++++-------------- actix-http/src/client/connection.rs | 34 ++++++++++------------------- actix-http/src/encoding/encoder.rs | 14 ++++++------ actix-http/src/h1/dispatcher.rs | 26 +++++++++------------- actix-http/src/h2/dispatcher.rs | 8 +++---- actix-http/src/service.rs | 12 +++++----- actix-web-actors/Cargo.toml | 2 +- src/responder.rs | 10 ++++----- 10 files changed, 55 insertions(+), 83 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4c3b11e28..8c6d461d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,7 +90,7 @@ fxhash = "0.2.1" log = "0.4" mime = "0.3" socket2 = "0.3" -pin-project = "0.4.6" +pin-project = "0.4.17" regex = "1.3" serde = { version = "1.0", features=["derive"] } serde_json = "1.0" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index edd8201d5..d2ae7698e 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -70,7 +70,7 @@ language-tags = "0.2" log = "0.4" mime = "0.3" percent-encoding = "2.1" -pin-project = "0.4.6" +pin-project = "0.4.17" rand = "0.7" regex = "1.3" serde = "1.0" diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index 9d6fb26ee..0b01aa8ce 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -6,7 +6,7 @@ use std::{fmt, mem}; use bytes::{Bytes, BytesMut}; use futures_core::Stream; use futures_util::ready; -use pin_project::{pin_project, project}; +use pin_project::pin_project; use crate::error::Error; @@ -70,7 +70,7 @@ impl MessageBody for Box { } } -#[pin_project] +#[pin_project(project = ResponseBodyProj)] pub enum ResponseBody { Body(#[pin] B), Other(#[pin] Body), @@ -109,15 +109,13 @@ impl MessageBody for ResponseBody { } } - #[project] fn poll_next( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { - #[project] match self.project() { - ResponseBody::Body(body) => body.poll_next(cx), - ResponseBody::Other(body) => body.poll_next(cx), + ResponseBodyProj::Body(body) => body.poll_next(cx), + ResponseBodyProj::Other(body) => body.poll_next(cx), } } } @@ -125,20 +123,18 @@ impl MessageBody for ResponseBody { impl Stream for ResponseBody { type Item = Result; - #[project] fn poll_next( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - #[project] match self.project() { - ResponseBody::Body(body) => body.poll_next(cx), - ResponseBody::Other(body) => body.poll_next(cx), + ResponseBodyProj::Body(body) => body.poll_next(cx), + ResponseBodyProj::Other(body) => body.poll_next(cx), } } } -#[pin_project] +#[pin_project(project = BodyProj)] /// Represents various types of http message body. pub enum Body { /// Empty response. `Content-Length` header is not set. @@ -173,16 +169,14 @@ impl MessageBody for Body { } } - #[project] fn poll_next( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { - #[project] match self.project() { - Body::None => Poll::Ready(None), - Body::Empty => Poll::Ready(None), - Body::Bytes(ref mut bin) => { + BodyProj::None => Poll::Ready(None), + BodyProj::Empty => Poll::Ready(None), + BodyProj::Bytes(ref mut bin) => { let len = bin.len(); if len == 0 { Poll::Ready(None) @@ -190,7 +184,7 @@ impl MessageBody for Body { Poll::Ready(Some(Ok(mem::take(bin)))) } } - Body::Message(ref mut body) => Pin::new(body.as_mut()).poll_next(cx), + BodyProj::Message(ref mut body) => Pin::new(body.as_mut()).poll_next(cx), } } } diff --git a/actix-http/src/client/connection.rs b/actix-http/src/client/connection.rs index c1362df85..eecf2ee6f 100644 --- a/actix-http/src/client/connection.rs +++ b/actix-http/src/client/connection.rs @@ -7,7 +7,7 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed}; use bytes::{Buf, Bytes}; use futures_util::future::{err, Either, FutureExt, LocalBoxFuture, Ready}; use h2::client::SendRequest; -use pin_project::{pin_project, project}; +use pin_project::pin_project; use crate::body::MessageBody; use crate::h1::ClientCodec; @@ -205,7 +205,7 @@ where } } -#[pin_project] +#[pin_project(project = EitherIoProj)] pub enum EitherIo { A(#[pin] A), B(#[pin] B), @@ -216,16 +216,14 @@ where A: AsyncRead, B: AsyncRead, { - #[project] fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - #[project] match self.project() { - EitherIo::A(val) => val.poll_read(cx, buf), - EitherIo::B(val) => val.poll_read(cx, buf), + EitherIoProj::A(val) => val.poll_read(cx, buf), + EitherIoProj::B(val) => val.poll_read(cx, buf), } } @@ -245,41 +243,34 @@ where A: AsyncWrite, B: AsyncWrite, { - #[project] fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - #[project] match self.project() { - EitherIo::A(val) => val.poll_write(cx, buf), - EitherIo::B(val) => val.poll_write(cx, buf), + EitherIoProj::A(val) => val.poll_write(cx, buf), + EitherIoProj::B(val) => val.poll_write(cx, buf), } } - #[project] fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - #[project] match self.project() { - EitherIo::A(val) => val.poll_flush(cx), - EitherIo::B(val) => val.poll_flush(cx), + EitherIoProj::A(val) => val.poll_flush(cx), + EitherIoProj::B(val) => val.poll_flush(cx), } } - #[project] fn poll_shutdown( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - #[project] match self.project() { - EitherIo::A(val) => val.poll_shutdown(cx), - EitherIo::B(val) => val.poll_shutdown(cx), + EitherIoProj::A(val) => val.poll_shutdown(cx), + EitherIoProj::B(val) => val.poll_shutdown(cx), } } - #[project] fn poll_write_buf( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -288,10 +279,9 @@ where where Self: Sized, { - #[project] match self.project() { - EitherIo::A(val) => val.poll_write_buf(cx, buf), - EitherIo::B(val) => val.poll_write_buf(cx, buf), + EitherIoProj::A(val) => val.poll_write_buf(cx, buf), + EitherIoProj::B(val) => val.poll_write_buf(cx, buf), } } } diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index ef69aa039..eb1821285 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -9,7 +9,7 @@ use brotli2::write::BrotliEncoder; use bytes::Bytes; use flate2::write::{GzEncoder, ZlibEncoder}; use futures_core::ready; -use pin_project::{pin_project, project}; +use pin_project::pin_project; use crate::body::{Body, BodySize, MessageBody, ResponseBody}; use crate::http::header::{ContentEncoding, CONTENT_ENCODING}; @@ -79,7 +79,7 @@ impl Encoder { } } -#[pin_project] +#[pin_project(project = EncoderBodyProj)] enum EncoderBody { Bytes(Bytes), Stream(#[pin] B), @@ -95,22 +95,22 @@ impl MessageBody for EncoderBody { } } - #[project] fn poll_next( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { - #[project] match self.project() { - EncoderBody::Bytes(b) => { + EncoderBodyProj::Bytes(b) => { if b.is_empty() { Poll::Ready(None) } else { Poll::Ready(Some(Ok(std::mem::take(b)))) } } - EncoderBody::Stream(b) => b.poll_next(cx), - EncoderBody::BoxedStream(ref mut b) => Pin::new(b.as_mut()).poll_next(cx), + EncoderBodyProj::Stream(b) => b.poll_next(cx), + EncoderBodyProj::BoxedStream(ref mut b) => { + Pin::new(b.as_mut()).poll_next(cx) + } } } } diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index c95000bf9..e16d3536f 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -58,7 +58,7 @@ where inner: DispatcherState, } -#[pin_project] +#[pin_project(project = DispatcherStateProj)] enum DispatcherState where S: Service, @@ -73,7 +73,7 @@ where Upgrade(Pin>), } -#[pin_project] +#[pin_project(project = InnerDispatcherProj)] struct InnerDispatcher where S: Service, @@ -112,7 +112,7 @@ enum DispatcherMessage { Error(Response<()>), } -#[pin_project] +#[pin_project(project = StateProj)] enum State where S: Service, @@ -296,7 +296,6 @@ where /// /// true - got WouldBlock /// false - didn't get WouldBlock - #[pin_project::project] fn poll_flush( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -307,8 +306,7 @@ where let len = self.write_buf.len(); let mut written = 0; - #[project] - let InnerDispatcher { io, write_buf, .. } = self.project(); + let InnerDispatcherProj { io, write_buf, .. } = self.project(); let mut io = Pin::new(io.as_mut().unwrap()); while written < len { match io.as_mut().poll_write(cx, &write_buf[written..]) { @@ -366,16 +364,14 @@ where .extend_from_slice(b"HTTP/1.1 100 Continue\r\n\r\n"); } - #[pin_project::project] fn poll_response( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Result { loop { let mut this = self.as_mut().project(); - #[project] let state = match this.state.project() { - State::None => match this.messages.pop_front() { + StateProj::None => match this.messages.pop_front() { Some(DispatcherMessage::Item(req)) => { Some(self.as_mut().handle_request(req, cx)?) } @@ -388,7 +384,7 @@ where } None => None, }, - State::ExpectCall(fut) => match fut.as_mut().poll(cx) { + StateProj::ExpectCall(fut) => match fut.as_mut().poll(cx) { Poll::Ready(Ok(req)) => { self.as_mut().send_continue(); this = self.as_mut().project(); @@ -403,7 +399,7 @@ where } Poll::Pending => None, }, - State::ServiceCall(fut) => match fut.as_mut().poll(cx) { + StateProj::ServiceCall(fut) => match fut.as_mut().poll(cx) { Poll::Ready(Ok(res)) => { let (res, body) = res.into().replace_body(()); let state = self.as_mut().send_response(res, body)?; @@ -418,7 +414,7 @@ where } Poll::Pending => None, }, - State::SendPayload(mut stream) => { + StateProj::SendPayload(mut stream) => { loop { if this.write_buf.len() < HW_BUFFER_SIZE { match stream.as_mut().poll_next(cx) { @@ -724,13 +720,11 @@ where { type Output = Result<(), DispatchError>; - #[pin_project::project] #[inline] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.as_mut().project(); - #[project] match this.inner.project() { - DispatcherState::Normal(mut inner) => { + DispatcherStateProj::Normal(mut inner) => { inner.as_mut().poll_keepalive(cx)?; if inner.flags.contains(Flags::SHUTDOWN) { @@ -850,7 +844,7 @@ where } } } - DispatcherState::Upgrade(fut) => fut.as_mut().poll(cx).map_err(|e| { + DispatcherStateProj::Upgrade(fut) => fut.as_mut().poll(cx).map_err(|e| { error!("Upgrade handler error: {}", e); DispatchError::Upgrade }), diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index a189697a2..33fb3a814 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -165,7 +165,7 @@ struct ServiceResponse { _t: PhantomData<(I, E)>, } -#[pin_project::pin_project] +#[pin_project::pin_project(project = ServiceResponseStateProj)] enum ServiceResponseState { ServiceCall(#[pin] F, Option>), SendPayload(SendStream, #[pin] ResponseBody), @@ -245,13 +245,11 @@ where { type Output = (); - #[pin_project::project] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.as_mut().project(); - #[project] match this.state.project() { - ServiceResponseState::ServiceCall(call, send) => match call.poll(cx) { + ServiceResponseStateProj::ServiceCall(call, send) => match call.poll(cx) { Poll::Ready(Ok(res)) => { let (res, body) = res.into().replace_body(()); @@ -305,7 +303,7 @@ where } } }, - ServiceResponseState::SendPayload(ref mut stream, ref mut body) => loop { + ServiceResponseStateProj::SendPayload(ref mut stream, ref mut body) => loop { loop { if let Some(ref mut buffer) = this.buffer { match stream.poll_capacity(cx) { diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index 51de95135..94cdbc828 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -10,7 +10,7 @@ use bytes::Bytes; use futures_core::{ready, Future}; use futures_util::future::ok; use h2::server::{self, Handshake}; -use pin_project::{pin_project, project}; +use pin_project::pin_project; use crate::body::MessageBody; use crate::builder::HttpServiceBuilder; @@ -574,7 +574,7 @@ where } } -#[pin_project] +#[pin_project(project = StateProj)] enum State where S: Service, @@ -650,16 +650,14 @@ where U: Service), Response = ()>, U::Error: fmt::Display, { - #[project] fn poll( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - #[project] match self.as_mut().project() { - State::H1(disp) => disp.poll(cx), - State::H2(disp) => disp.poll(cx), - State::H2Handshake(ref mut data) => { + StateProj::H1(disp) => disp.poll(cx), + StateProj::H2(disp) => disp.poll(cx), + StateProj::H2Handshake(ref mut data) => { let conn = if let Some(ref mut item) = data { match Pin::new(&mut item.0).poll(cx) { Poll::Ready(Ok(conn)) => conn, diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 20fd3743d..8db7a35ef 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -23,7 +23,7 @@ actix-codec = "0.2.0" bytes = "0.5.2" futures-channel = { version = "0.3.5", default-features = false } futures-core = { version = "0.3.5", default-features = false } -pin-project = "0.4.6" +pin-project = "0.4.17" [dev-dependencies] actix-rt = "1.0.0" diff --git a/src/responder.rs b/src/responder.rs index 367c9fccf..e102d23e1 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -12,7 +12,7 @@ use actix_http::{Error, Response, ResponseBuilder}; use bytes::{Bytes, BytesMut}; use futures_util::future::{err, ok, Either as EitherFuture, Ready}; use futures_util::ready; -use pin_project::{pin_project, project}; +use pin_project::pin_project; use crate::request::HttpRequest; @@ -379,7 +379,7 @@ where } } -#[pin_project] +#[pin_project(project = EitherResponderProj)] pub enum EitherResponder where A: Responder, @@ -396,14 +396,12 @@ where { type Output = Result; - #[project] fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - #[project] match self.project() { - EitherResponder::A(fut) => { + EitherResponderProj::A(fut) => { Poll::Ready(ready!(fut.poll(cx)).map_err(|e| e.into())) } - EitherResponder::B(fut) => { + EitherResponderProj::B(fut) => { Poll::Ready(ready!(fut.poll(cx).map_err(|e| e.into()))) } }