Merge branch 'master' into expose-handshake-timeout

This commit is contained in:
Rob Ede 2022-06-27 02:40:05 +01:00 committed by GitHub
commit 5ad3b6f622
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 420 additions and 403 deletions

View File

@ -16,7 +16,7 @@ jobs:
- { name: macOS, os: macos-latest, triple: x86_64-apple-darwin } - { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
- { name: Windows, os: windows-2022, triple: x86_64-pc-windows-msvc } - { name: Windows, os: windows-2022, triple: x86_64-pc-windows-msvc }
version: version:
- 1.54.0 # MSRV - 1.57.0 # MSRV
- stable - stable
name: ${{ matrix.target.name }} / ${{ matrix.version }} name: ${{ matrix.target.name }} / ${{ matrix.version }}

View File

@ -1,9 +1,14 @@
# Changes # Changes
## Unreleased - 2021-xx-xx ## Unreleased - 2022-xx-xx
- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.
## 0.6.1 - 2022-06-11
- Add `NamedFile::{modified, metadata, content_type, content_disposition, encoding}()` getters. [#2021] - Add `NamedFile::{modified, metadata, content_type, content_disposition, encoding}()` getters. [#2021]
- Update `tokio-uring` dependency to `0.3`. - Update `tokio-uring` dependency to `0.3`.
- Audio files now use `Content-Disposition: inline` instead of `attachment`. [#2645] - Audio files now use `Content-Disposition: inline` instead of `attachment`. [#2645]
- Minimum supported Rust version (MSRV) is now 1.56 due to transitive `hashbrown` dependency.
[#2021]: https://github.com/actix/actix-web/pull/2021 [#2021]: https://github.com/actix/actix-web/pull/2021
[#2645]: https://github.com/actix/actix-web/pull/2645 [#2645]: https://github.com/actix/actix-web/pull/2645

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-files" name = "actix-files"
version = "0.6.0" version = "0.6.1"
authors = [ authors = [
"Nikolay Kim <fafhrd91@gmail.com>", "Nikolay Kim <fafhrd91@gmail.com>",
"fakeshadow <24548779@qq.com>", "fakeshadow <24548779@qq.com>",
@ -47,5 +47,5 @@ actix-server = { version = "2.1", optional = true } # ensure matching tokio-urin
[dev-dependencies] [dev-dependencies]
actix-rt = "2.7" actix-rt = "2.7"
actix-test = "0.1.0-beta.13" actix-test = "0.1.0-beta.13"
actix-web = "4.0.0" actix-web = "4"
tempfile = "3.2" tempfile = "3.2"

View File

@ -3,11 +3,11 @@
> Static file serving for Actix Web > Static file serving for Actix Web
[![crates.io](https://img.shields.io/crates/v/actix-files?label=latest)](https://crates.io/crates/actix-files) [![crates.io](https://img.shields.io/crates/v/actix-files?label=latest)](https://crates.io/crates/actix-files)
[![Documentation](https://docs.rs/actix-files/badge.svg?version=0.6.0)](https://docs.rs/actix-files/0.6.0) [![Documentation](https://docs.rs/actix-files/badge.svg?version=0.6.1)](https://docs.rs/actix-files/0.6.1)
[![Version](https://img.shields.io/badge/rustc-1.54+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.54.0.html) ![Version](https://img.shields.io/badge/rustc-1.57+-ab6000.svg)
![License](https://img.shields.io/crates/l/actix-files.svg) ![License](https://img.shields.io/crates/l/actix-files.svg)
<br /> <br />
[![dependency status](https://deps.rs/crate/actix-files/0.6.0/status.svg)](https://deps.rs/crate/actix-files/0.6.0) [![dependency status](https://deps.rs/crate/actix-files/0.6.1/status.svg)](https://deps.rs/crate/actix-files/0.6.1)
[![Download](https://img.shields.io/crates/d/actix-files.svg)](https://crates.io/crates/actix-files) [![Download](https://img.shields.io/crates/d/actix-files.svg)](https://crates.io/crates/actix-files)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)

View File

@ -1,6 +1,7 @@
# Changes # Changes
## Unreleased - 2021-xx-xx ## Unreleased - 2022-xx-xx
- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.
## 3.0.0-beta.13 - 2022-02-16 ## 3.0.0-beta.13 - 2022-02-16

View File

@ -4,7 +4,7 @@
[![crates.io](https://img.shields.io/crates/v/actix-http-test?label=latest)](https://crates.io/crates/actix-http-test) [![crates.io](https://img.shields.io/crates/v/actix-http-test?label=latest)](https://crates.io/crates/actix-http-test)
[![Documentation](https://docs.rs/actix-http-test/badge.svg?version=3.0.0-beta.13)](https://docs.rs/actix-http-test/3.0.0-beta.13) [![Documentation](https://docs.rs/actix-http-test/badge.svg?version=3.0.0-beta.13)](https://docs.rs/actix-http-test/3.0.0-beta.13)
[![Version](https://img.shields.io/badge/rustc-1.54+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.54.0.html) ![Version](https://img.shields.io/badge/rustc-1.57+-ab6000.svg)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http-test) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http-test)
<br> <br>
[![Dependency Status](https://deps.rs/crate/actix-http-test/3.0.0-beta.13/status.svg)](https://deps.rs/crate/actix-http-test/3.0.0-beta.13) [![Dependency Status](https://deps.rs/crate/actix-http-test/3.0.0-beta.13/status.svg)](https://deps.rs/crate/actix-http-test/3.0.0-beta.13)

View File

@ -1,6 +1,25 @@
# Changes # Changes
## Unreleased - 2021-xx-xx ## Unreleased - 2022-xx-xx
### Fixed
- Websocket parser no longer throws endless overflow errors after receiving an oversized frame. [#2790]
### Changed
- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.
[#2790]: https://github.com/actix/actix-web/pull/2790
## 3.1.0 - 2022-06-11
### Changed
- Minimum supported Rust version (MSRV) is now 1.56 due to transitive `hashbrown` dependency.
### Fixed
- Revert broken fix in [#2624] that caused erroneous 500 error responses. Temporarily re-introduces [#2357] bug. [#2779]
[#2357]: https://github.com/actix/actix-web/issues/2357
[#2624]: https://github.com/actix/actix-web/issues/2624
[#2779]: https://github.com/actix/actix-web/issues/2779
## 3.0.4 - 2022-03-09 ## 3.0.4 - 2022-03-09

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-http" name = "actix-http"
version = "3.0.4" version = "3.1.0"
authors = [ authors = [
"Nikolay Kim <fafhrd91@gmail.com>", "Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>", "Rob Ede <robjtede@icloud.com>",
@ -37,7 +37,7 @@ ws = [
"local-channel", "local-channel",
"base64", "base64",
"rand", "rand",
"sha-1", "sha1",
] ]
# TLS via OpenSSL # TLS via OpenSSL
@ -86,7 +86,7 @@ h2 = { version = "0.3.9", optional = true }
local-channel = { version = "0.1", optional = true } local-channel = { version = "0.1", optional = true }
base64 = { version = "0.13", optional = true } base64 = { version = "0.13", optional = true }
rand = { version = "0.8", optional = true } rand = { version = "0.8", optional = true }
sha-1 = { version = "0.10", optional = true } sha1 = { version = "0.10", optional = true }
# openssl/rustls # openssl/rustls
actix-tls = { version = "3", default-features = false, optional = true } actix-tls = { version = "3", default-features = false, optional = true }
@ -100,7 +100,7 @@ zstd = { version = "0.11", optional = true }
actix-http-test = { version = "3.0.0-beta.13", features = ["openssl"] } actix-http-test = { version = "3.0.0-beta.13", features = ["openssl"] }
actix-server = "2" actix-server = "2"
actix-tls = { version = "3", features = ["openssl"] } actix-tls = { version = "3", features = ["openssl"] }
actix-web = "4.0.0" actix-web = "4"
async-stream = "0.3" async-stream = "0.3"
criterion = { version = "0.3", features = ["html_reports"] } criterion = { version = "0.3", features = ["html_reports"] }

View File

@ -3,11 +3,11 @@
> HTTP primitives for the Actix ecosystem. > HTTP primitives for the Actix ecosystem.
[![crates.io](https://img.shields.io/crates/v/actix-http?label=latest)](https://crates.io/crates/actix-http) [![crates.io](https://img.shields.io/crates/v/actix-http?label=latest)](https://crates.io/crates/actix-http)
[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.0.4)](https://docs.rs/actix-http/3.0.4) [![Documentation](https://docs.rs/actix-http/badge.svg?version=3.1.0)](https://docs.rs/actix-http/3.1.0)
[![Version](https://img.shields.io/badge/rustc-1.54+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.54.0.html) ![Version](https://img.shields.io/badge/rustc-1.57+-ab6000.svg)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http.svg) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http.svg)
<br /> <br />
[![dependency status](https://deps.rs/crate/actix-http/3.0.4/status.svg)](https://deps.rs/crate/actix-http/3.0.4) [![dependency status](https://deps.rs/crate/actix-http/3.1.0/status.svg)](https://deps.rs/crate/actix-http/3.1.0)
[![Download](https://img.shields.io/crates/d/actix-http.svg)](https://crates.io/crates/actix-http) [![Download](https://img.shields.io/crates/d/actix-http.svg)](https://crates.io/crates/actix-http)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)

View File

@ -15,14 +15,14 @@ use bitflags::bitflags;
use bytes::{Buf, BytesMut}; use bytes::{Buf, BytesMut};
use futures_core::ready; use futures_core::ready;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use tracing::{debug, error, trace}; use tracing::{error, trace};
use crate::{ use crate::{
body::{BodySize, BoxBody, MessageBody}, body::{BodySize, BoxBody, MessageBody},
config::ServiceConfig, config::ServiceConfig,
error::{DispatchError, ParseError, PayloadError}, error::{DispatchError, ParseError, PayloadError},
service::HttpFlow, service::HttpFlow,
ConnectionType, Error, Extensions, OnConnectData, Request, Response, StatusCode, Error, Extensions, OnConnectData, Request, Response, StatusCode,
}; };
use super::{ use super::{
@ -691,74 +691,12 @@ where
let can_not_read = !self.can_read(cx); let can_not_read = !self.can_read(cx);
// limit amount of non-processed requests // limit amount of non-processed requests
if pipeline_queue_full { if pipeline_queue_full || can_not_read {
return Ok(false); return Ok(false);
} }
let mut this = self.as_mut().project(); let mut this = self.as_mut().project();
if can_not_read {
debug!("cannot read request payload");
if let Some(sender) = &this.payload {
// ...maybe handler does not want to read any more payload...
if let PayloadStatus::Dropped = sender.need_read(cx) {
debug!("handler dropped payload early; attempt to clean connection");
// ...in which case poll request payload a few times
loop {
match this.codec.decode(this.read_buf)? {
Some(msg) => {
match msg {
// payload decoded did not yield EOF yet
Message::Chunk(Some(_)) => {
// if non-clean connection, next loop iter will detect empty
// read buffer and close connection
}
// connection is in clean state for next request
Message::Chunk(None) => {
debug!("connection successfully cleaned");
// reset dispatcher state
let _ = this.payload.take();
this.state.set(State::None);
// break out of payload decode loop
break;
}
// Either whole payload is read and loop is broken or more data
// was expected in which case connection is closed. In both
// situations dispatcher cannot get here.
Message::Item(_) => {
unreachable!("dispatcher is in payload receive state")
}
}
}
// not enough info to decide if connection is going to be clean or not
None => {
error!(
"handler did not read whole payload and dispatcher could not \
drain read buf; return 500 and close connection"
);
this.flags.insert(Flags::SHUTDOWN);
let mut res = Response::internal_server_error().drop_body();
res.head_mut().set_connection_type(ConnectionType::Close);
this.messages.push_back(DispatcherMessage::Error(res));
*this.error = Some(DispatchError::HandlerDroppedPayload);
return Ok(true);
}
}
}
}
} else {
// can_not_read and no request payload
return Ok(false);
}
}
let mut updated = false; let mut updated = false;
// decode from read buf as many full requests as possible // decode from read buf as many full requests as possible
@ -904,10 +842,7 @@ where
if timer.as_mut().poll(cx).is_ready() { if timer.as_mut().poll(cx).is_ready() {
// timeout on first request (slow request) return 408 // timeout on first request (slow request) return 408
trace!( trace!("timed out on slow request; replying with 408 and closing connection");
"timed out on slow request; \
replying with 408 and closing connection"
);
let _ = self.as_mut().send_error_response( let _ = self.as_mut().send_error_response(
Response::with_body(StatusCode::REQUEST_TIMEOUT, ()), Response::with_body(StatusCode::REQUEST_TIMEOUT, ()),

View File

@ -783,6 +783,9 @@ async fn upgrade_handling() {
.await; .await;
} }
// fix in #2624 reverted temporarily
// complete fix tracked in #2745
#[ignore]
#[actix_rt::test] #[actix_rt::test]
async fn handler_drop_payload() { async fn handler_drop_payload() {
let _ = env_logger::try_init(); let _ = env_logger::try_init();

View File

@ -17,7 +17,6 @@ impl Parser {
fn parse_metadata( fn parse_metadata(
src: &[u8], src: &[u8],
server: bool, server: bool,
max_size: usize,
) -> Result<Option<(usize, bool, OpCode, usize, Option<[u8; 4]>)>, ProtocolError> { ) -> Result<Option<(usize, bool, OpCode, usize, Option<[u8; 4]>)>, ProtocolError> {
let chunk_len = src.len(); let chunk_len = src.len();
@ -60,20 +59,12 @@ impl Parser {
return Ok(None); return Ok(None);
} }
let len = u64::from_be_bytes(TryFrom::try_from(&src[idx..idx + 8]).unwrap()); let len = u64::from_be_bytes(TryFrom::try_from(&src[idx..idx + 8]).unwrap());
if len > max_size as u64 {
return Err(ProtocolError::Overflow);
}
idx += 8; idx += 8;
len as usize len as usize
} else { } else {
len as usize len as usize
}; };
// check for max allowed size
if length > max_size {
return Err(ProtocolError::Overflow);
}
let mask = if server { let mask = if server {
if chunk_len < idx + 4 { if chunk_len < idx + 4 {
return Ok(None); return Ok(None);
@ -98,11 +89,10 @@ impl Parser {
max_size: usize, max_size: usize,
) -> Result<Option<(bool, OpCode, Option<BytesMut>)>, ProtocolError> { ) -> Result<Option<(bool, OpCode, Option<BytesMut>)>, ProtocolError> {
// try to parse ws frame metadata // try to parse ws frame metadata
let (idx, finished, opcode, length, mask) = let (idx, finished, opcode, length, mask) = match Parser::parse_metadata(src, server)? {
match Parser::parse_metadata(src, server, max_size)? { None => return Ok(None),
None => return Ok(None), Some(res) => res,
Some(res) => res, };
};
// not enough data // not enough data
if src.len() < idx + length { if src.len() < idx + length {
@ -112,6 +102,13 @@ impl Parser {
// remove prefix // remove prefix
src.advance(idx); src.advance(idx);
// check for max allowed size
if length > max_size {
// drop the payload
src.advance(length);
return Err(ProtocolError::Overflow);
}
// no need for body // no need for body
if length == 0 { if length == 0 {
return Ok(Some((finished, opcode, None))); return Ok(Some((finished, opcode, None)));
@ -339,6 +336,30 @@ mod tests {
} }
} }
#[test]
fn test_parse_frame_max_size_recoverability() {
let mut buf = BytesMut::new();
// The first text frame with length == 2, payload doesn't matter.
buf.extend(&[0b0000_0001u8, 0b0000_0010u8, 0b0000_0000u8, 0b0000_0000u8]);
// Next binary frame with length == 2 and payload == `[0x1111_1111u8, 0x1111_1111u8]`.
buf.extend(&[0b0000_0010u8, 0b0000_0010u8, 0b1111_1111u8, 0b1111_1111u8]);
assert_eq!(buf.len(), 8);
assert!(matches!(
Parser::parse(&mut buf, false, 1),
Err(ProtocolError::Overflow)
));
assert_eq!(buf.len(), 4);
let frame = extract(Parser::parse(&mut buf, false, 2));
assert!(!frame.finished);
assert_eq!(frame.opcode, OpCode::Binary);
assert_eq!(
frame.payload,
Bytes::from(vec![0b1111_1111u8, 0b1111_1111u8])
);
assert_eq!(buf.len(), 0);
}
#[test] #[test]
fn test_ping_frame() { fn test_ping_frame() {
let mut buf = BytesMut::new(); let mut buf = BytesMut::new();

View File

@ -1,6 +1,7 @@
# Changes # Changes
## Unreleased - 2021-xx-xx ## Unreleased - 2022-xx-xx
- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.
## 0.4.0 - 2022-02-25 ## 0.4.0 - 2022-02-25

View File

@ -15,7 +15,7 @@ path = "src/lib.rs"
[dependencies] [dependencies]
actix-utils = "3" actix-utils = "3"
actix-web = { version = "4.0.0", default-features = false } actix-web = { version = "4", default-features = false }
bytes = "1" bytes = "1"
derive_more = "0.99.5" derive_more = "0.99.5"

View File

@ -4,7 +4,7 @@
[![crates.io](https://img.shields.io/crates/v/actix-multipart?label=latest)](https://crates.io/crates/actix-multipart) [![crates.io](https://img.shields.io/crates/v/actix-multipart?label=latest)](https://crates.io/crates/actix-multipart)
[![Documentation](https://docs.rs/actix-multipart/badge.svg?version=0.4.0)](https://docs.rs/actix-multipart/0.4.0) [![Documentation](https://docs.rs/actix-multipart/badge.svg?version=0.4.0)](https://docs.rs/actix-multipart/0.4.0)
[![Version](https://img.shields.io/badge/rustc-1.54+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.54.0.html) ![Version](https://img.shields.io/badge/rustc-1.57+-ab6000.svg)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-multipart.svg) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-multipart.svg)
<br /> <br />
[![dependency status](https://deps.rs/crate/actix-multipart/0.4.0/status.svg)](https://deps.rs/crate/actix-multipart/0.4.0) [![dependency status](https://deps.rs/crate/actix-multipart/0.4.0/status.svg)](https://deps.rs/crate/actix-multipart/0.4.0)

View File

@ -1,6 +1,7 @@
# Changes # Changes
## Unreleased - 2021-xx-xx ## Unreleased - 2022-xx-xx
- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.
## 0.5.0 - 2022-02-22 ## 0.5.0 - 2022-02-22

View File

@ -21,7 +21,6 @@ default = ["http"]
[dependencies] [dependencies]
bytestring = ">=0.1.5, <2" bytestring = ">=0.1.5, <2"
firestorm = "0.5"
http = { version = "0.2.3", optional = true } http = { version = "0.2.3", optional = true }
regex = "1.5" regex = "1.5"
serde = "1" serde = "1"
@ -29,7 +28,6 @@ tracing = { version = "0.1.30", default-features = false, features = ["log"] }
[dev-dependencies] [dev-dependencies]
criterion = { version = "0.3", features = ["html_reports"] } criterion = { version = "0.3", features = ["html_reports"] }
firestorm = { version = "0.5", features = ["enable_system_time"] }
http = "0.2.5" http = "0.2.5"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
percent-encoding = "2.1" percent-encoding = "2.1"

View File

@ -1,169 +0,0 @@
macro_rules! register {
(brackets) => {{
register!(finish => "{p1}", "{p2}", "{p3}", "{p4}")
}};
(finish => $p1:literal, $p2:literal, $p3:literal, $p4:literal) => {{
let arr = [
concat!("/authorizations"),
concat!("/authorizations/", $p1),
concat!("/applications/", $p1, "/tokens/", $p2),
concat!("/events"),
concat!("/repos/", $p1, "/", $p2, "/events"),
concat!("/networks/", $p1, "/", $p2, "/events"),
concat!("/orgs/", $p1, "/events"),
concat!("/users/", $p1, "/received_events"),
concat!("/users/", $p1, "/received_events/public"),
concat!("/users/", $p1, "/events"),
concat!("/users/", $p1, "/events/public"),
concat!("/users/", $p1, "/events/orgs/", $p2),
concat!("/feeds"),
concat!("/notifications"),
concat!("/repos/", $p1, "/", $p2, "/notifications"),
concat!("/notifications/threads/", $p1),
concat!("/notifications/threads/", $p1, "/subscription"),
concat!("/repos/", $p1, "/", $p2, "/stargazers"),
concat!("/users/", $p1, "/starred"),
concat!("/user/starred"),
concat!("/user/starred/", $p1, "/", $p2),
concat!("/repos/", $p1, "/", $p2, "/subscribers"),
concat!("/users/", $p1, "/subscriptions"),
concat!("/user/subscriptions"),
concat!("/repos/", $p1, "/", $p2, "/subscription"),
concat!("/user/subscriptions/", $p1, "/", $p2),
concat!("/users/", $p1, "/gists"),
concat!("/gists"),
concat!("/gists/", $p1),
concat!("/gists/", $p1, "/star"),
concat!("/repos/", $p1, "/", $p2, "/git/blobs/", $p3),
concat!("/repos/", $p1, "/", $p2, "/git/commits/", $p3),
concat!("/repos/", $p1, "/", $p2, "/git/refs"),
concat!("/repos/", $p1, "/", $p2, "/git/tags/", $p3),
concat!("/repos/", $p1, "/", $p2, "/git/trees/", $p3),
concat!("/issues"),
concat!("/user/issues"),
concat!("/orgs/", $p1, "/issues"),
concat!("/repos/", $p1, "/", $p2, "/issues"),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3),
concat!("/repos/", $p1, "/", $p2, "/assignees"),
concat!("/repos/", $p1, "/", $p2, "/assignees/", $p3),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3, "/comments"),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3, "/events"),
concat!("/repos/", $p1, "/", $p2, "/labels"),
concat!("/repos/", $p1, "/", $p2, "/labels/", $p3),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3, "/labels"),
concat!("/repos/", $p1, "/", $p2, "/milestones/", $p3, "/labels"),
concat!("/repos/", $p1, "/", $p2, "/milestones/"),
concat!("/repos/", $p1, "/", $p2, "/milestones/", $p3),
concat!("/emojis"),
concat!("/gitignore/templates"),
concat!("/gitignore/templates/", $p1),
concat!("/meta"),
concat!("/rate_limit"),
concat!("/users/", $p1, "/orgs"),
concat!("/user/orgs"),
concat!("/orgs/", $p1),
concat!("/orgs/", $p1, "/members"),
concat!("/orgs/", $p1, "/members", $p2),
concat!("/orgs/", $p1, "/public_members"),
concat!("/orgs/", $p1, "/public_members/", $p2),
concat!("/orgs/", $p1, "/teams"),
concat!("/teams/", $p1),
concat!("/teams/", $p1, "/members"),
concat!("/teams/", $p1, "/members", $p2),
concat!("/teams/", $p1, "/repos"),
concat!("/teams/", $p1, "/repos/", $p2, "/", $p3),
concat!("/user/teams"),
concat!("/repos/", $p1, "/", $p2, "/pulls"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/commits"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/files"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/merge"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/comments"),
concat!("/user/repos"),
concat!("/users/", $p1, "/repos"),
concat!("/orgs/", $p1, "/repos"),
concat!("/repositories"),
concat!("/repos/", $p1, "/", $p2),
concat!("/repos/", $p1, "/", $p2, "/contributors"),
concat!("/repos/", $p1, "/", $p2, "/languages"),
concat!("/repos/", $p1, "/", $p2, "/teams"),
concat!("/repos/", $p1, "/", $p2, "/tags"),
concat!("/repos/", $p1, "/", $p2, "/branches"),
concat!("/repos/", $p1, "/", $p2, "/branches/", $p3),
concat!("/repos/", $p1, "/", $p2, "/collaborators"),
concat!("/repos/", $p1, "/", $p2, "/collaborators/", $p3),
concat!("/repos/", $p1, "/", $p2, "/comments"),
concat!("/repos/", $p1, "/", $p2, "/commits/", $p3, "/comments"),
concat!("/repos/", $p1, "/", $p2, "/commits"),
concat!("/repos/", $p1, "/", $p2, "/commits/", $p3),
concat!("/repos/", $p1, "/", $p2, "/readme"),
concat!("/repos/", $p1, "/", $p2, "/keys"),
concat!("/repos/", $p1, "/", $p2, "/keys", $p3),
concat!("/repos/", $p1, "/", $p2, "/downloads"),
concat!("/repos/", $p1, "/", $p2, "/downloads", $p3),
concat!("/repos/", $p1, "/", $p2, "/forks"),
concat!("/repos/", $p1, "/", $p2, "/hooks"),
concat!("/repos/", $p1, "/", $p2, "/hooks", $p3),
concat!("/repos/", $p1, "/", $p2, "/releases"),
concat!("/repos/", $p1, "/", $p2, "/releases/", $p3),
concat!("/repos/", $p1, "/", $p2, "/releases/", $p3, "/assets"),
concat!("/repos/", $p1, "/", $p2, "/stats/contributors"),
concat!("/repos/", $p1, "/", $p2, "/stats/commit_activity"),
concat!("/repos/", $p1, "/", $p2, "/stats/code_frequency"),
concat!("/repos/", $p1, "/", $p2, "/stats/participation"),
concat!("/repos/", $p1, "/", $p2, "/stats/punch_card"),
concat!("/repos/", $p1, "/", $p2, "/statuses/", $p3),
concat!("/search/repositories"),
concat!("/search/code"),
concat!("/search/issues"),
concat!("/search/users"),
concat!("/legacy/issues/search/", $p1, "/", $p2, "/", $p3, "/", $p4),
concat!("/legacy/repos/search/", $p1),
concat!("/legacy/user/search/", $p1),
concat!("/legacy/user/email/", $p1),
concat!("/users/", $p1),
concat!("/user"),
concat!("/users"),
concat!("/user/emails"),
concat!("/users/", $p1, "/followers"),
concat!("/user/followers"),
concat!("/users/", $p1, "/following"),
concat!("/user/following"),
concat!("/user/following/", $p1),
concat!("/users/", $p1, "/following", $p2),
concat!("/users/", $p1, "/keys"),
concat!("/user/keys"),
concat!("/user/keys/", $p1),
];
arr.to_vec()
}};
}
static PATHS: [&str; 5] = [
"/authorizations",
"/user/repos",
"/repos/rust-lang/rust/stargazers",
"/orgs/rust-lang/public_members/nikomatsakis",
"/repos/rust-lang/rust/releases/1.51.0",
];
fn main() {
let mut router = actix_router::Router::<bool>::build();
for route in register!(brackets) {
router.path(route, true);
}
let actix = router.finish();
if firestorm::enabled() {
firestorm::bench("target", || {
for &route in &PATHS {
let mut path = actix_router::Path::new(route);
actix.recognize(&mut path).unwrap();
}
})
.unwrap();
}
}

View File

@ -1,7 +1,6 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::ops::{DerefMut, Index}; use std::ops::{DerefMut, Index};
use firestorm::profile_method;
use serde::de; use serde::de;
use crate::{de::PathDeserializer, Resource, ResourcePath}; use crate::{de::PathDeserializer, Resource, ResourcePath};
@ -52,7 +51,6 @@ impl<T: ResourcePath> Path<T> {
/// Returns full path as a string. /// Returns full path as a string.
#[inline] #[inline]
pub fn as_str(&self) -> &str { pub fn as_str(&self) -> &str {
profile_method!(as_str);
self.path.path() self.path.path()
} }
@ -61,7 +59,6 @@ impl<T: ResourcePath> Path<T> {
/// Returns empty string if no more is to be processed. /// Returns empty string if no more is to be processed.
#[inline] #[inline]
pub fn unprocessed(&self) -> &str { pub fn unprocessed(&self) -> &str {
profile_method!(unprocessed);
// clamp skip to path length // clamp skip to path length
let skip = (self.skip as usize).min(self.as_str().len()); let skip = (self.skip as usize).min(self.as_str().len());
&self.path.path()[skip..] &self.path.path()[skip..]
@ -72,8 +69,6 @@ impl<T: ResourcePath> Path<T> {
#[deprecated(since = "0.6.0", note = "Use `.as_str()` or `.unprocessed()`.")] #[deprecated(since = "0.6.0", note = "Use `.as_str()` or `.unprocessed()`.")]
#[inline] #[inline]
pub fn path(&self) -> &str { pub fn path(&self) -> &str {
profile_method!(path);
let skip = self.skip as usize; let skip = self.skip as usize;
let path = self.path.path(); let path = self.path.path();
if skip <= path.len() { if skip <= path.len() {
@ -86,8 +81,6 @@ impl<T: ResourcePath> Path<T> {
/// Set new path. /// Set new path.
#[inline] #[inline]
pub fn set(&mut self, path: T) { pub fn set(&mut self, path: T) {
profile_method!(set);
self.skip = 0; self.skip = 0;
self.path = path; self.path = path;
self.segments.clear(); self.segments.clear();
@ -96,8 +89,6 @@ impl<T: ResourcePath> Path<T> {
/// Reset state. /// Reset state.
#[inline] #[inline]
pub fn reset(&mut self) { pub fn reset(&mut self) {
profile_method!(reset);
self.skip = 0; self.skip = 0;
self.segments.clear(); self.segments.clear();
} }
@ -105,13 +96,10 @@ impl<T: ResourcePath> Path<T> {
/// Skip first `n` chars in path. /// Skip first `n` chars in path.
#[inline] #[inline]
pub fn skip(&mut self, n: u16) { pub fn skip(&mut self, n: u16) {
profile_method!(skip);
self.skip += n; self.skip += n;
} }
pub(crate) fn add(&mut self, name: impl Into<Cow<'static, str>>, value: PathItem) { pub(crate) fn add(&mut self, name: impl Into<Cow<'static, str>>, value: PathItem) {
profile_method!(add);
match value { match value {
PathItem::Static(s) => self.segments.push((name.into(), PathItem::Static(s))), PathItem::Static(s) => self.segments.push((name.into(), PathItem::Static(s))),
PathItem::Segment(begin, end) => self.segments.push(( PathItem::Segment(begin, end) => self.segments.push((
@ -127,8 +115,6 @@ impl<T: ResourcePath> Path<T> {
name: impl Into<Cow<'static, str>>, name: impl Into<Cow<'static, str>>,
value: impl Into<Cow<'static, str>>, value: impl Into<Cow<'static, str>>,
) { ) {
profile_method!(add_static);
self.segments self.segments
.push((name.into(), PathItem::Static(value.into()))); .push((name.into(), PathItem::Static(value.into())));
} }
@ -147,8 +133,6 @@ impl<T: ResourcePath> Path<T> {
/// Get matched parameter by name without type conversion /// Get matched parameter by name without type conversion
pub fn get(&self, name: &str) -> Option<&str> { pub fn get(&self, name: &str) -> Option<&str> {
profile_method!(get);
for (seg_name, val) in self.segments.iter() { for (seg_name, val) in self.segments.iter() {
if name == seg_name { if name == seg_name {
return match val { return match val {
@ -167,8 +151,6 @@ impl<T: ResourcePath> Path<T> {
/// ///
/// If keyed parameter is not available empty string is used as default value. /// If keyed parameter is not available empty string is used as default value.
pub fn query(&self, key: &str) -> &str { pub fn query(&self, key: &str) -> &str {
profile_method!(query);
if let Some(s) = self.get(key) { if let Some(s) = self.get(key) {
s s
} else { } else {
@ -186,7 +168,6 @@ impl<T: ResourcePath> Path<T> {
/// Try to deserialize matching parameters to a specified type `U` /// Try to deserialize matching parameters to a specified type `U`
pub fn load<'de, U: serde::Deserialize<'de>>(&'de self) -> Result<U, de::value::Error> { pub fn load<'de, U: serde::Deserialize<'de>>(&'de self) -> Result<U, de::value::Error> {
profile_method!(load);
de::Deserialize::deserialize(PathDeserializer::new(self)) de::Deserialize::deserialize(PathDeserializer::new(self))
} }
} }

View File

@ -5,7 +5,6 @@ use std::{
mem, mem,
}; };
use firestorm::{profile_fn, profile_method, profile_section};
use regex::{escape, Regex, RegexSet}; use regex::{escape, Regex, RegexSet};
use tracing::error; use tracing::error;
@ -272,7 +271,6 @@ impl ResourceDef {
/// assert!(!resource.is_match("/foo")); /// assert!(!resource.is_match("/foo"));
/// ``` /// ```
pub fn new<T: IntoPatterns>(paths: T) -> Self { pub fn new<T: IntoPatterns>(paths: T) -> Self {
profile_method!(new);
Self::construct(paths, false) Self::construct(paths, false)
} }
@ -300,7 +298,6 @@ impl ResourceDef {
/// assert!(!resource.is_match("/foo")); /// assert!(!resource.is_match("/foo"));
/// ``` /// ```
pub fn prefix<T: IntoPatterns>(paths: T) -> Self { pub fn prefix<T: IntoPatterns>(paths: T) -> Self {
profile_method!(prefix);
ResourceDef::construct(paths, true) ResourceDef::construct(paths, true)
} }
@ -325,7 +322,6 @@ impl ResourceDef {
/// assert!(!resource.is_match("user/123")); /// assert!(!resource.is_match("user/123"));
/// ``` /// ```
pub fn root_prefix(path: &str) -> Self { pub fn root_prefix(path: &str) -> Self {
profile_method!(root_prefix);
ResourceDef::prefix(insert_slash(path).into_owned()) ResourceDef::prefix(insert_slash(path).into_owned())
} }
@ -549,8 +545,6 @@ impl ResourceDef {
/// ``` /// ```
#[inline] #[inline]
pub fn is_match(&self, path: &str) -> bool { pub fn is_match(&self, path: &str) -> bool {
profile_method!(is_match);
// this function could be expressed as: // this function could be expressed as:
// `self.find_match(path).is_some()` // `self.find_match(path).is_some()`
// but this skips some checks and uses potentially faster regex methods // but this skips some checks and uses potentially faster regex methods
@ -598,8 +592,6 @@ impl ResourceDef {
/// assert_eq!(resource.find_match("/profile/1234"), Some(13)); /// assert_eq!(resource.find_match("/profile/1234"), Some(13));
/// ``` /// ```
pub fn find_match(&self, path: &str) -> Option<usize> { pub fn find_match(&self, path: &str) -> Option<usize> {
profile_method!(find_match);
match &self.pat_type { match &self.pat_type {
PatternType::Static(pattern) => self.static_match(pattern, path), PatternType::Static(pattern) => self.static_match(pattern, path),
@ -634,7 +626,6 @@ impl ResourceDef {
/// assert_eq!(path.unprocessed(), ""); /// assert_eq!(path.unprocessed(), "");
/// ``` /// ```
pub fn capture_match_info<R: Resource>(&self, resource: &mut R) -> bool { pub fn capture_match_info<R: Resource>(&self, resource: &mut R) -> bool {
profile_method!(capture_match_info);
self.capture_match_info_fn(resource, |_| true) self.capture_match_info_fn(resource, |_| true)
} }
@ -680,53 +671,35 @@ impl ResourceDef {
R: Resource, R: Resource,
F: FnOnce(&R) -> bool, F: FnOnce(&R) -> bool,
{ {
profile_method!(capture_match_info_fn);
let mut segments = <[PathItem; MAX_DYNAMIC_SEGMENTS]>::default(); let mut segments = <[PathItem; MAX_DYNAMIC_SEGMENTS]>::default();
let path = resource.resource_path(); let path = resource.resource_path();
let path_str = path.unprocessed(); let path_str = path.unprocessed();
let (matched_len, matched_vars) = match &self.pat_type { let (matched_len, matched_vars) = match &self.pat_type {
PatternType::Static(pattern) => { PatternType::Static(pattern) => match self.static_match(pattern, path_str) {
profile_section!(pattern_static_or_prefix); Some(len) => (len, None),
None => return false,
match self.static_match(pattern, path_str) { },
Some(len) => (len, None),
None => return false,
}
}
PatternType::Dynamic(re, names) => { PatternType::Dynamic(re, names) => {
profile_section!(pattern_dynamic); let captures = match re.captures(path.unprocessed()) {
Some(captures) => captures,
let captures = { _ => return false,
profile_section!(pattern_dynamic_regex_exec);
match re.captures(path.unprocessed()) {
Some(captures) => captures,
_ => return false,
}
}; };
{ for (no, name) in names.iter().enumerate() {
profile_section!(pattern_dynamic_extract_captures); if let Some(m) = captures.name(name) {
segments[no] = PathItem::Segment(m.start() as u16, m.end() as u16);
for (no, name) in names.iter().enumerate() { } else {
if let Some(m) = captures.name(name) { error!("Dynamic path match but not all segments found: {}", name);
segments[no] = PathItem::Segment(m.start() as u16, m.end() as u16); return false;
} else {
error!("Dynamic path match but not all segments found: {}", name);
return false;
}
} }
}; }
(captures[1].len(), Some(names)) (captures[1].len(), Some(names))
} }
PatternType::DynamicSet(re, params) => { PatternType::DynamicSet(re, params) => {
profile_section!(pattern_dynamic_set);
let path = path.unprocessed(); let path = path.unprocessed();
let (pattern, names) = match re.matches(path).into_iter().next() { let (pattern, names) = match re.matches(path).into_iter().next() {
Some(idx) => &params[idx], Some(idx) => &params[idx],
@ -809,7 +782,6 @@ impl ResourceDef {
I: IntoIterator, I: IntoIterator,
I::Item: AsRef<str>, I::Item: AsRef<str>,
{ {
profile_method!(resource_path_from_iter);
let mut iter = values.into_iter(); let mut iter = values.into_iter();
self.build_resource_path(path, |_| iter.next()) self.build_resource_path(path, |_| iter.next())
} }
@ -845,7 +817,6 @@ impl ResourceDef {
V: AsRef<str>, V: AsRef<str>,
S: BuildHasher, S: BuildHasher,
{ {
profile_method!(resource_path_from_map);
self.build_resource_path(path, |name| values.get(name)) self.build_resource_path(path, |name| values.get(name))
} }
@ -866,8 +837,6 @@ impl ResourceDef {
} }
fn construct<T: IntoPatterns>(paths: T, is_prefix: bool) -> Self { fn construct<T: IntoPatterns>(paths: T, is_prefix: bool) -> Self {
profile_method!(construct);
let patterns = paths.patterns(); let patterns = paths.patterns();
let (pat_type, segments) = match &patterns { let (pat_type, segments) = match &patterns {
Patterns::Single(pattern) => ResourceDef::parse(pattern, is_prefix, false), Patterns::Single(pattern) => ResourceDef::parse(pattern, is_prefix, false),
@ -926,8 +895,6 @@ impl ResourceDef {
/// # Panics /// # Panics
/// Panics if given patterns does not contain a dynamic segment. /// Panics if given patterns does not contain a dynamic segment.
fn parse_param(pattern: &str) -> (PatternSegment, String, &str, bool) { fn parse_param(pattern: &str) -> (PatternSegment, String, &str, bool) {
profile_method!(parse_param);
const DEFAULT_PATTERN: &str = "[^/]+"; const DEFAULT_PATTERN: &str = "[^/]+";
const DEFAULT_PATTERN_TAIL: &str = ".*"; const DEFAULT_PATTERN_TAIL: &str = ".*";
@ -997,8 +964,6 @@ impl ResourceDef {
is_prefix: bool, is_prefix: bool,
force_dynamic: bool, force_dynamic: bool,
) -> (PatternType, Vec<PatternSegment>) { ) -> (PatternType, Vec<PatternSegment>) {
profile_method!(parse);
if !force_dynamic && pattern.find('{').is_none() && !pattern.ends_with('*') { if !force_dynamic && pattern.find('{').is_none() && !pattern.ends_with('*') {
// pattern is static // pattern is static
return ( return (
@ -1131,8 +1096,6 @@ impl From<String> for ResourceDef {
} }
pub(crate) fn insert_slash(path: &str) -> Cow<'_, str> { pub(crate) fn insert_slash(path: &str) -> Cow<'_, str> {
profile_fn!(insert_slash);
if !path.is_empty() && !path.starts_with('/') { if !path.is_empty() && !path.starts_with('/') {
let mut new_path = String::with_capacity(path.len() + 1); let mut new_path = String::with_capacity(path.len() + 1);
new_path.push('/'); new_path.push('/');

View File

@ -1,5 +1,3 @@
use firestorm::profile_method;
use crate::{IntoPatterns, Resource, ResourceDef}; use crate::{IntoPatterns, Resource, ResourceDef};
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
@ -30,7 +28,6 @@ impl<T, U> Router<T, U> {
where where
R: Resource, R: Resource,
{ {
profile_method!(recognize);
self.recognize_fn(resource, |_, _| true) self.recognize_fn(resource, |_, _| true)
} }
@ -39,7 +36,6 @@ impl<T, U> Router<T, U> {
where where
R: Resource, R: Resource,
{ {
profile_method!(recognize_mut);
self.recognize_mut_fn(resource, |_, _| true) self.recognize_mut_fn(resource, |_, _| true)
} }
@ -55,8 +51,6 @@ impl<T, U> Router<T, U> {
R: Resource, R: Resource,
F: FnMut(&R, &U) -> bool, F: FnMut(&R, &U) -> bool,
{ {
profile_method!(recognize_checked);
for (rdef, val, ctx) in self.routes.iter() { for (rdef, val, ctx) in self.routes.iter() {
if rdef.capture_match_info_fn(resource, |res| check(res, ctx)) { if rdef.capture_match_info_fn(resource, |res| check(res, ctx)) {
return Some((val, ResourceId(rdef.id()))); return Some((val, ResourceId(rdef.id())));
@ -77,8 +71,6 @@ impl<T, U> Router<T, U> {
R: Resource, R: Resource,
F: FnMut(&R, &U) -> bool, F: FnMut(&R, &U) -> bool,
{ {
profile_method!(recognize_mut_checked);
for (rdef, val, ctx) in self.routes.iter_mut() { for (rdef, val, ctx) in self.routes.iter_mut() {
if rdef.capture_match_info_fn(resource, |res| check(res, ctx)) { if rdef.capture_match_info_fn(resource, |res| check(res, ctx)) {
return Some((val, ResourceId(rdef.id()))); return Some((val, ResourceId(rdef.id())));
@ -104,7 +96,6 @@ impl<T, U> RouterBuilder<T, U> {
val: T, val: T,
ctx: U, ctx: U,
) -> (&mut ResourceDef, &mut T, &mut U) { ) -> (&mut ResourceDef, &mut T, &mut U) {
profile_method!(push);
self.routes.push((rdef, val, ctx)); self.routes.push((rdef, val, ctx));
self.routes self.routes
.last_mut() .last_mut()
@ -131,7 +122,6 @@ where
path: impl IntoPatterns, path: impl IntoPatterns,
val: T, val: T,
) -> (&mut ResourceDef, &mut T, &mut U) { ) -> (&mut ResourceDef, &mut T, &mut U) {
profile_method!(path);
self.push(ResourceDef::new(path), val, U::default()) self.push(ResourceDef::new(path), val, U::default())
} }
@ -141,13 +131,11 @@ where
prefix: impl IntoPatterns, prefix: impl IntoPatterns,
val: T, val: T,
) -> (&mut ResourceDef, &mut T, &mut U) { ) -> (&mut ResourceDef, &mut T, &mut U) {
profile_method!(prefix);
self.push(ResourceDef::prefix(prefix), val, U::default()) self.push(ResourceDef::prefix(prefix), val, U::default())
} }
/// Registers resource for [`ResourceDef`]. /// Registers resource for [`ResourceDef`].
pub fn rdef(&mut self, rdef: ResourceDef, val: T) -> (&mut ResourceDef, &mut T, &mut U) { pub fn rdef(&mut self, rdef: ResourceDef, val: T) -> (&mut ResourceDef, &mut T, &mut U) {
profile_method!(rdef);
self.push(rdef, val, U::default()) self.push(rdef, val, U::default())
} }
} }

View File

@ -1,6 +1,7 @@
# Changes # Changes
## Unreleased - 2021-xx-xx ## Unreleased - 2022-xx-xx
- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.
## 0.1.0-beta.13 - 2022-02-16 ## 0.1.0-beta.13 - 2022-02-16

View File

@ -1,6 +1,7 @@
# Changes # Changes
## Unreleased - 2021-xx-xx ## Unreleased - 2022-xx-xx
- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.
## 4.1.0 - 2022-03-02 ## 4.1.0 - 2022-03-02

View File

@ -29,6 +29,9 @@ tokio = { version = "1.13.1", features = ["sync"] }
actix-rt = "2.2" actix-rt = "2.2"
actix-test = "0.1.0-beta.13" actix-test = "0.1.0-beta.13"
awc = { version = "3", default-features = false } awc = { version = "3", default-features = false }
actix-web = { version = "4", features = ["macros"] }
mime = "0.3"
env_logger = "0.9" env_logger = "0.9"
futures-util = { version = "0.3.7", default-features = false } futures-util = { version = "0.3.7", default-features = false }

View File

@ -4,7 +4,7 @@
[![crates.io](https://img.shields.io/crates/v/actix-web-actors?label=latest)](https://crates.io/crates/actix-web-actors) [![crates.io](https://img.shields.io/crates/v/actix-web-actors?label=latest)](https://crates.io/crates/actix-web-actors)
[![Documentation](https://docs.rs/actix-web-actors/badge.svg?version=4.1.0)](https://docs.rs/actix-web-actors/4.1.0) [![Documentation](https://docs.rs/actix-web-actors/badge.svg?version=4.1.0)](https://docs.rs/actix-web-actors/4.1.0)
[![Version](https://img.shields.io/badge/rustc-1.54+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.54.0.html) ![Version](https://img.shields.io/badge/rustc-1.57+-ab6000.svg)
![License](https://img.shields.io/crates/l/actix-web-actors.svg) ![License](https://img.shields.io/crates/l/actix-web-actors.svg)
<br /> <br />
[![dependency status](https://deps.rs/crate/actix-web-actors/4.1.0/status.svg)](https://deps.rs/crate/actix-web-actors/4.1.0) [![dependency status](https://deps.rs/crate/actix-web-actors/4.1.0/status.svg)](https://deps.rs/crate/actix-web-actors/4.1.0)

View File

@ -14,6 +14,58 @@ use futures_core::Stream;
use tokio::sync::oneshot::Sender; use tokio::sync::oneshot::Sender;
/// Execution context for HTTP actors /// Execution context for HTTP actors
///
/// # Example
///
/// A demonstration of [server-sent events](https://developer.mozilla.org/docs/Web/API/Server-sent_events) using actors:
///
/// ```no_run
/// use std::time::Duration;
///
/// use actix::{Actor, AsyncContext};
/// use actix_web::{get, http::header, App, HttpResponse, HttpServer};
/// use actix_web_actors::HttpContext;
/// use bytes::Bytes;
///
/// struct MyActor {
/// count: usize,
/// }
///
/// impl Actor for MyActor {
/// type Context = HttpContext<Self>;
///
/// fn started(&mut self, ctx: &mut Self::Context) {
/// ctx.run_later(Duration::from_millis(100), Self::write);
/// }
/// }
///
/// impl MyActor {
/// fn write(&mut self, ctx: &mut HttpContext<Self>) {
/// self.count += 1;
/// if self.count > 3 {
/// ctx.write_eof()
/// } else {
/// ctx.write(Bytes::from(format!("event: count\ndata: {}\n\n", self.count)));
/// ctx.run_later(Duration::from_millis(100), Self::write);
/// }
/// }
/// }
///
/// #[get("/")]
/// async fn index() -> HttpResponse {
/// HttpResponse::Ok()
/// .insert_header(header::ContentType(mime::TEXT_EVENT_STREAM))
/// .streaming(HttpContext::create(MyActor { count: 0 }))
/// }
///
/// #[actix_web::main]
/// async fn main() -> std::io::Result<()> {
/// HttpServer::new(|| App::new().service(index))
/// .bind(("127.0.0.1", 8080))?
/// .run()
/// .await
/// }
/// ```
pub struct HttpContext<A> pub struct HttpContext<A>
where where
A: Actor<Context = HttpContext<A>>, A: Actor<Context = HttpContext<A>>,
@ -210,7 +262,7 @@ mod tests {
type Context = HttpContext<Self>; type Context = HttpContext<Self>;
fn started(&mut self, ctx: &mut Self::Context) { fn started(&mut self, ctx: &mut Self::Context) {
ctx.run_later(Duration::from_millis(100), |slf, ctx| slf.write(ctx)); ctx.run_later(Duration::from_millis(100), Self::write);
} }
} }
@ -221,7 +273,7 @@ mod tests {
ctx.write_eof() ctx.write_eof()
} else { } else {
ctx.write(Bytes::from(format!("LINE-{}", self.count))); ctx.write(Bytes::from(format!("LINE-{}", self.count)));
ctx.run_later(Duration::from_millis(100), |slf, ctx| slf.write(ctx)); ctx.run_later(Duration::from_millis(100), Self::write);
} }
} }
} }

View File

@ -1,4 +1,59 @@
//! Actix actors support for Actix Web. //! Actix actors support for Actix Web.
//!
//! # Examples
//!
//! ```no_run
//! use actix::{Actor, StreamHandler};
//! use actix_web::{get, web, App, Error, HttpRequest, HttpResponse, HttpServer};
//! use actix_web_actors::ws;
//!
//! /// Define Websocket actor
//! struct MyWs;
//!
//! impl Actor for MyWs {
//! type Context = ws::WebsocketContext<Self>;
//! }
//!
//! /// Handler for ws::Message message
//! impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWs {
//! fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
//! match msg {
//! Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),
//! Ok(ws::Message::Text(text)) => ctx.text(text),
//! Ok(ws::Message::Binary(bin)) => ctx.binary(bin),
//! _ => (),
//! }
//! }
//! }
//!
//! #[get("/ws")]
//! async fn index(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
//! ws::start(MyWs, &req, stream)
//! }
//!
//! #[actix_web::main]
//! async fn main() -> std::io::Result<()> {
//! HttpServer::new(|| App::new().service(index))
//! .bind(("127.0.0.1", 8080))?
//! .run()
//! .await
//! }
//! ```
//!
//! # Documentation & Community Resources
//! In addition to this API documentation, several other resources are available:
//!
//! * [Website & User Guide](https://actix.rs/)
//! * [Documentation for `actix_web`](actix_web)
//! * [Examples Repository](https://github.com/actix/examples)
//! * [Community Chat on Discord](https://discord.gg/NWpN5mmg3x)
//!
//! To get started navigating the API docs, you may consider looking at the following pages first:
//!
//! * [`ws`]: This module provides actor support for WebSockets.
//!
//! * [`HttpContext`]: This struct provides actor support for streaming HTTP responses.
//!
#![deny(rust_2018_idioms, nonstandard_style)] #![deny(rust_2018_idioms, nonstandard_style)]
#![warn(future_incompatible)] #![warn(future_incompatible)]

View File

@ -1,4 +1,60 @@
//! Websocket integration. //! Websocket integration.
//!
//! # Examples
//!
//! ```no_run
//! use actix::{Actor, StreamHandler};
//! use actix_web::{get, web, App, Error, HttpRequest, HttpResponse, HttpServer};
//! use actix_web_actors::ws;
//!
//! /// Define Websocket actor
//! struct MyWs;
//!
//! impl Actor for MyWs {
//! type Context = ws::WebsocketContext<Self>;
//! }
//!
//! /// Handler for ws::Message message
//! impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWs {
//! fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
//! match msg {
//! Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),
//! Ok(ws::Message::Text(text)) => ctx.text(text),
//! Ok(ws::Message::Binary(bin)) => ctx.binary(bin),
//! _ => (),
//! }
//! }
//! }
//!
//! #[get("/ws")]
//! async fn websocket(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
//! ws::start(MyWs, &req, stream)
//! }
//!
//! const MAX_FRAME_SIZE: usize = 16_384; // 16KiB
//!
//! #[get("/custom-ws")]
//! async fn custom_websocket(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
//! // Create a Websocket session with a specific max frame size, and protocols.
//! ws::WsResponseBuilder::new(MyWs, &req, stream)
//! .frame_size(MAX_FRAME_SIZE)
//! .protocols(&["A", "B"])
//! .start()
//! }
//!
//! #[actix_web::main]
//! async fn main() -> std::io::Result<()> {
//! HttpServer::new(|| {
//! App::new()
//! .service(websocket)
//! .service(custom_websocket)
//! })
//! .bind(("127.0.0.1", 8080))?
//! .run()
//! .await
//! }
//! ```
//!
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
@ -41,20 +97,51 @@ use tokio::sync::oneshot;
/// ///
/// # Examples /// # Examples
/// ///
/// Create a Websocket session response with default configuration. /// ```no_run
/// ```ignore /// # use actix::{Actor, StreamHandler};
/// WsResponseBuilder::new(WsActor, &req, stream).start() /// # use actix_web::{get, web, App, Error, HttpRequest, HttpResponse, HttpServer};
/// ``` /// # use actix_web_actors::ws;
/// #
/// # struct MyWs;
/// #
/// # impl Actor for MyWs {
/// # type Context = ws::WebsocketContext<Self>;
/// # }
/// #
/// # /// Handler for ws::Message message
/// # impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWs {
/// # fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {}
/// # }
/// #
/// #[get("/ws")]
/// async fn websocket(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
/// ws::WsResponseBuilder::new(MyWs, &req, stream).start()
/// }
/// ///
/// Create a Websocket session with a specific max frame size, [`Codec`], and protocols.
/// ```ignore
/// const MAX_FRAME_SIZE: usize = 16_384; // 16KiB /// const MAX_FRAME_SIZE: usize = 16_384; // 16KiB
/// ///
/// ws::WsResponseBuilder::new(WsActor, &req, stream) /// #[get("/custom-ws")]
/// .codec(Codec::new()) /// async fn custom_websocket(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
/// .protocols(&["A", "B"]) /// // Create a Websocket session with a specific max frame size, codec, and protocols.
/// .frame_size(MAX_FRAME_SIZE) /// ws::WsResponseBuilder::new(MyWs, &req, stream)
/// .start() /// .codec(actix_http::ws::Codec::new())
/// // This will overwrite the codec's max frame-size
/// .frame_size(MAX_FRAME_SIZE)
/// .protocols(&["A", "B"])
/// .start()
/// }
/// #
/// # #[actix_web::main]
/// # async fn main() -> std::io::Result<()> {
/// # HttpServer::new(|| {
/// # App::new()
/// # .service(websocket)
/// # .service(custom_websocket)
/// # })
/// # .bind(("127.0.0.1", 8080))?
/// # .run()
/// # .await
/// # }
/// ``` /// ```
pub struct WsResponseBuilder<'a, A, T> pub struct WsResponseBuilder<'a, A, T>
where where

View File

@ -1,6 +1,14 @@
# Changes # Changes
## Unreleased - 2021-xx-xx ## Unreleased - 2022-xx-xx
- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.
## 4.0.1 - 2022-06-11
- Fix support for guard paths in route handler macros. [#2771]
- Minimum supported Rust version (MSRV) is now 1.56 due to transitive `hashbrown` dependency.
[#2771]: https://github.com/actix/actix-web/pull/2771
## 4.0.0 - 2022-02-24 ## 4.0.0 - 2022-02-24

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-web-codegen" name = "actix-web-codegen"
version = "4.0.0" version = "4.0.1"
description = "Routing and runtime macros for Actix Web" description = "Routing and runtime macros for Actix Web"
homepage = "https://actix.rs" homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-web.git" repository = "https://github.com/actix/actix-web.git"

View File

@ -3,11 +3,11 @@
> Routing and runtime macros for Actix Web. > Routing and runtime macros for Actix Web.
[![crates.io](https://img.shields.io/crates/v/actix-web-codegen?label=latest)](https://crates.io/crates/actix-web-codegen) [![crates.io](https://img.shields.io/crates/v/actix-web-codegen?label=latest)](https://crates.io/crates/actix-web-codegen)
[![Documentation](https://docs.rs/actix-web-codegen/badge.svg?version=4.0.0)](https://docs.rs/actix-web-codegen/4.0.0) [![Documentation](https://docs.rs/actix-web-codegen/badge.svg?version=4.0.1)](https://docs.rs/actix-web-codegen/4.0.1)
[![Version](https://img.shields.io/badge/rustc-1.54+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.54.0.html) ![Version](https://img.shields.io/badge/rustc-1.57+-ab6000.svg)
![License](https://img.shields.io/crates/l/actix-web-codegen.svg) ![License](https://img.shields.io/crates/l/actix-web-codegen.svg)
<br /> <br />
[![dependency status](https://deps.rs/crate/actix-web-codegen/4.0.0/status.svg)](https://deps.rs/crate/actix-web-codegen/4.0.0) [![dependency status](https://deps.rs/crate/actix-web-codegen/4.0.1/status.svg)](https://deps.rs/crate/actix-web-codegen/4.0.1)
[![Download](https://img.shields.io/crates/d/actix-web-codegen.svg)](https://crates.io/crates/actix-web-codegen) [![Download](https://img.shields.io/crates/d/actix-web-codegen.svg)](https://crates.io/crates/actix-web-codegen)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)

View File

@ -4,7 +4,7 @@ use actix_router::ResourceDef;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2}; use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{format_ident, quote, ToTokens, TokenStreamExt}; use quote::{format_ident, quote, ToTokens, TokenStreamExt};
use syn::{parse_macro_input, AttributeArgs, Ident, LitStr, NestedMeta}; use syn::{parse_macro_input, AttributeArgs, Ident, LitStr, NestedMeta, Path};
enum ResourceType { enum ResourceType {
Async, Async,
@ -77,7 +77,7 @@ impl TryFrom<&syn::LitStr> for MethodType {
struct Args { struct Args {
path: syn::LitStr, path: syn::LitStr,
resource_name: Option<syn::LitStr>, resource_name: Option<syn::LitStr>,
guards: Vec<Ident>, guards: Vec<Path>,
wrappers: Vec<syn::Type>, wrappers: Vec<syn::Type>,
methods: HashSet<MethodType>, methods: HashSet<MethodType>,
} }
@ -121,7 +121,7 @@ impl Args {
} }
} else if nv.path.is_ident("guard") { } else if nv.path.is_ident("guard") {
if let syn::Lit::Str(lit) = nv.lit { if let syn::Lit::Str(lit) = nv.lit {
guards.push(Ident::new(&lit.value(), Span::call_site())); guards.push(lit.parse::<Path>()?);
} else { } else {
return Err(syn::Error::new_spanned( return Err(syn::Error::new_spanned(
nv.lit, nv.lit,

View File

@ -96,6 +96,21 @@ async fn custom_resource_name_test<'a>(req: actix_web::HttpRequest) -> impl Resp
HttpResponse::Ok() HttpResponse::Ok()
} }
mod guard_module {
use actix_web::{guard::GuardContext, http::header};
pub fn guard(ctx: &GuardContext) -> bool {
ctx.header::<header::Accept>()
.map(|h| h.preference() == "image/*")
.unwrap_or(false)
}
}
#[get("/test/guard", guard = "guard_module::guard")]
async fn guard_test() -> impl Responder {
HttpResponse::Ok()
}
pub struct ChangeStatusCode; pub struct ChangeStatusCode;
impl<S, B> Transform<S, ServiceRequest> for ChangeStatusCode impl<S, B> Transform<S, ServiceRequest> for ChangeStatusCode
@ -187,6 +202,7 @@ async fn test_body() {
.service(test_handler) .service(test_handler)
.service(route_test) .service(route_test)
.service(custom_resource_name_test) .service(custom_resource_name_test)
.service(guard_test)
}); });
let request = srv.request(http::Method::GET, srv.url("/test")); let request = srv.request(http::Method::GET, srv.url("/test"));
let response = request.send().await.unwrap(); let response = request.send().await.unwrap();
@ -245,6 +261,12 @@ async fn test_body() {
let request = srv.request(http::Method::GET, srv.url("/custom_resource_name")); let request = srv.request(http::Method::GET, srv.url("/custom_resource_name"));
let response = request.send().await.unwrap(); let response = request.send().await.unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
let request = srv
.request(http::Method::GET, srv.url("/test/guard"))
.insert_header(("Accept", "image/*"));
let response = request.send().await.unwrap();
assert!(response.status().is_success());
} }
#[actix_rt::test] #[actix_rt::test]

View File

@ -1,4 +1,4 @@
#[rustversion::stable(1.54)] // MSRV #[rustversion::stable(1.57)] // MSRV
#[test] #[test]
fn compile_macros() { fn compile_macros() {
let t = trybuild::TestCases::new(); let t = trybuild::TestCases::new();

View File

@ -1,11 +1,13 @@
error: HTTP method defined more than once: `GET` error: HTTP method defined more than once: `GET`
--> $DIR/route-duplicate-method-fail.rs:3:35 --> tests/trybuild/route-duplicate-method-fail.rs:3:35
| |
3 | #[route("/", method="GET", method="GET")] 3 | #[route("/", method="GET", method="GET")]
| ^^^^^ | ^^^^^
error[E0277]: the trait bound `fn() -> impl std::future::Future {index}: HttpServiceFactory` is not satisfied error[E0277]: the trait bound `fn() -> impl std::future::Future {index}: HttpServiceFactory` is not satisfied
--> $DIR/route-duplicate-method-fail.rs:12:55 --> tests/trybuild/route-duplicate-method-fail.rs:12:55
| |
12 | let srv = actix_test::start(|| App::new().service(index)); 12 | let srv = actix_test::start(|| App::new().service(index));
| ^^^^^ the trait `HttpServiceFactory` is not implemented for `fn() -> impl std::future::Future {index}` | ------- ^^^^^ the trait `HttpServiceFactory` is not implemented for `fn() -> impl std::future::Future {index}`
| |
| required by a bound introduced by this call

View File

@ -10,4 +10,6 @@ error[E0277]: the trait bound `fn() -> impl std::future::Future {index}: HttpSer
--> tests/trybuild/route-missing-method-fail.rs:12:55 --> tests/trybuild/route-missing-method-fail.rs:12:55
| |
12 | let srv = actix_test::start(|| App::new().service(index)); 12 | let srv = actix_test::start(|| App::new().service(index));
| ^^^^^ the trait `HttpServiceFactory` is not implemented for `fn() -> impl std::future::Future {index}` | ------- ^^^^^ the trait `HttpServiceFactory` is not implemented for `fn() -> impl std::future::Future {index}`
| |
| required by a bound introduced by this call

View File

@ -1,11 +1,13 @@
error: Unexpected HTTP method: `UNEXPECTED` error: Unexpected HTTP method: `UNEXPECTED`
--> $DIR/route-unexpected-method-fail.rs:3:21 --> tests/trybuild/route-unexpected-method-fail.rs:3:21
| |
3 | #[route("/", method="UNEXPECTED")] 3 | #[route("/", method="UNEXPECTED")]
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error[E0277]: the trait bound `fn() -> impl std::future::Future {index}: HttpServiceFactory` is not satisfied error[E0277]: the trait bound `fn() -> impl std::future::Future {index}: HttpServiceFactory` is not satisfied
--> $DIR/route-unexpected-method-fail.rs:12:55 --> tests/trybuild/route-unexpected-method-fail.rs:12:55
| |
12 | let srv = actix_test::start(|| App::new().service(index)); 12 | let srv = actix_test::start(|| App::new().service(index));
| ^^^^^ the trait `HttpServiceFactory` is not implemented for `fn() -> impl std::future::Future {index}` | ------- ^^^^^ the trait `HttpServiceFactory` is not implemented for `fn() -> impl std::future::Future {index}`
| |
| required by a bound introduced by this call

View File

@ -1,6 +1,13 @@
# Changelog # Changelog
## Unreleased - 2021-xx-xx ## Unreleased - 2022-xx-xx
- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.
### Added
- Add `ServiceRequest::{parts, request}()` getter methods. [#2786]
[#2786]: https://github.com/actix/actix-web/pull/2786
## 4.1.0 - 2022-06-11
### Added ### Added
- Add `ServiceRequest::extract()` to make it easier to use extractors when writing middlewares. [#2647] - Add `ServiceRequest::extract()` to make it easier to use extractors when writing middlewares. [#2647]
- Add `Route::wrap()` to allow individual routes to use middleware. [#2725] - Add `Route::wrap()` to allow individual routes to use middleware. [#2725]
@ -8,6 +15,9 @@
- Implement `ResponseError` for `std::convert::Infallible` - Implement `ResponseError` for `std::convert::Infallible`
- Add `tls_handshake_timeout` server configuration option to configure TLS handshake timeout [#2752] - Add `tls_handshake_timeout` server configuration option to configure TLS handshake timeout [#2752]
### Changed
- Minimum supported Rust version (MSRV) is now 1.56 due to transitive `hashbrown` dependency.
### Fixed ### Fixed
- Clear connection-level data on `HttpRequest` drop. [#2742] - Clear connection-level data on `HttpRequest` drop. [#2742]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-web" name = "actix-web"
version = "4.0.1" version = "4.1.0"
authors = [ authors = [
"Nikolay Kim <fafhrd91@gmail.com>", "Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>", "Rob Ede <robjtede@icloud.com>",

View File

@ -6,10 +6,10 @@
<p> <p>
[![crates.io](https://img.shields.io/crates/v/actix-web?label=latest)](https://crates.io/crates/actix-web) [![crates.io](https://img.shields.io/crates/v/actix-web?label=latest)](https://crates.io/crates/actix-web)
[![Documentation](https://docs.rs/actix-web/badge.svg?version=4.0.1)](https://docs.rs/actix-web/4.0.1) [![Documentation](https://docs.rs/actix-web/badge.svg?version=4.1.0)](https://docs.rs/actix-web/4.1.0)
![MSRV](https://img.shields.io/badge/rustc-1.54+-ab6000.svg) ![MSRV](https://img.shields.io/badge/rustc-1.57+-ab6000.svg)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-web.svg) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-web.svg)
[![Dependency Status](https://deps.rs/crate/actix-web/4.0.1/status.svg)](https://deps.rs/crate/actix-web/4.0.1) [![Dependency Status](https://deps.rs/crate/actix-web/4.1.0/status.svg)](https://deps.rs/crate/actix-web/4.1.0)
<br /> <br />
[![CI](https://github.com/actix/actix-web/actions/workflows/ci.yml/badge.svg)](https://github.com/actix/actix-web/actions/workflows/ci.yml) [![CI](https://github.com/actix/actix-web/actions/workflows/ci.yml/badge.svg)](https://github.com/actix/actix-web/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web)
@ -33,7 +33,7 @@
- SSL support using OpenSSL or Rustls - SSL support using OpenSSL or Rustls
- Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/)) - Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/))
- Integrates with the [`awc` HTTP client](https://docs.rs/awc/) - Integrates with the [`awc` HTTP client](https://docs.rs/awc/)
- Runs on stable Rust 1.54+ - Runs on stable Rust 1.57+
## Documentation ## Documentation
@ -79,6 +79,7 @@ async fn main() -> std::io::Result<()> {
- [Application State](https://github.com/actix/examples/tree/master/basics/state) - [Application State](https://github.com/actix/examples/tree/master/basics/state)
- [JSON Handling](https://github.com/actix/examples/tree/master/json/json) - [JSON Handling](https://github.com/actix/examples/tree/master/json/json)
- [Multipart Streams](https://github.com/actix/examples/tree/master/forms/multipart) - [Multipart Streams](https://github.com/actix/examples/tree/master/forms/multipart)
- [MongoDB Integration](https://github.com/actix/examples/tree/master/databases/mongodb)
- [Diesel Integration](https://github.com/actix/examples/tree/master/databases/diesel) - [Diesel Integration](https://github.com/actix/examples/tree/master/databases/diesel)
- [SQLite Integration](https://github.com/actix/examples/tree/master/databases/sqlite) - [SQLite Integration](https://github.com/actix/examples/tree/master/databases/sqlite)
- [Postgres Integration](https://github.com/actix/examples/tree/master/databases/postgres) - [Postgres Integration](https://github.com/actix/examples/tree/master/databases/postgres)

View File

@ -60,7 +60,7 @@ where
/// [`HttpRequest::app_data`](crate::HttpRequest::app_data) method at runtime. /// [`HttpRequest::app_data`](crate::HttpRequest::app_data) method at runtime.
/// ///
/// # [`Data<T>`] /// # [`Data<T>`]
/// Any [`Data<T>`] type added here can utilize it's extractor implementation in handlers. /// Any [`Data<T>`] type added here can utilize its extractor implementation in handlers.
/// Types not wrapped in `Data<T>` cannot use this extractor. See [its docs](Data<T>) for more /// Types not wrapped in `Data<T>` cannot use this extractor. See [its docs](Data<T>) for more
/// about its usage and patterns. /// about its usage and patterns.
/// ///

View File

@ -257,7 +257,7 @@ impl ServiceFactory<ServiceRequest> for AppRoutingFactory {
type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>; type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
// construct all services factory future with it's resource def and guards. // construct all services factory future with its resource def and guards.
let factory_fut = join_all(self.services.iter().map(|(path, factory, guards)| { let factory_fut = join_all(self.services.iter().map(|(path, factory, guards)| {
let path = path.clone(); let path = path.clone();
let guards = guards.borrow_mut().take().unwrap_or_default(); let guards = guards.borrow_mut().take().unwrap_or_default();

View File

@ -153,6 +153,16 @@ impl AppConfig {
} }
impl Default for AppConfig { impl Default for AppConfig {
/// Returns the default AppConfig.
/// Note: The included socket address is "127.0.0.1".
///
/// 127.0.0.1: non-routable meta address that denotes an unknown, invalid or non-applicable target.
/// If you need a service only accessed by itself, use a loopback address.
/// A loopback address for IPv4 is any loopback address that begins with "127".
/// Loopback addresses should be only used to test your application locally.
/// The default configuration provides a loopback address.
///
/// 0.0.0.0: if configured to use this special address, the application will listen to any IP address configured on the machine.
fn default() -> Self { fn default() -> Self {
AppConfig::new( AppConfig::new(
false, false,

View File

@ -254,7 +254,7 @@ impl Guard for AllGuard {
} }
} }
/// Wraps a guard and inverts the outcome of it's `Guard` implementation. /// Wraps a guard and inverts the outcome of its `Guard` implementation.
/// ///
/// # Examples /// # Examples
/// The handler below will be called for any request method apart from `GET`. /// The handler below will be called for any request method apart from `GET`.
@ -459,7 +459,7 @@ impl Guard for HostGuard {
return scheme == req_host_uri_scheme; return scheme == req_host_uri_scheme;
} }
// TODO: is the the correct behavior? // TODO: is this the correct behavior?
// falls through if scheme cannot be determined // falls through if scheme cannot be determined
} }

View File

@ -37,7 +37,7 @@ use crate::{
/// Thanks to Rust's type system, Actix Web can infer the function parameter types. During the /// Thanks to Rust's type system, Actix Web can infer the function parameter types. During the
/// extraction step, the parameter types are described as a tuple type, [`from_request`] is run on /// extraction step, the parameter types are described as a tuple type, [`from_request`] is run on
/// that tuple, and the `Handler::call` implementation for that particular function arity /// that tuple, and the `Handler::call` implementation for that particular function arity
/// destructures the tuple into it's component types and calls your handler function with them. /// destructures the tuple into its component types and calls your handler function with them.
/// ///
/// In pseudo-code the process looks something like this: /// In pseudo-code the process looks something like this:
/// ```ignore /// ```ignore

View File

@ -343,7 +343,7 @@ mod response_fut_impl {
// Future is only implemented for BoxBody payload type because it's the most useful for making // Future is only implemented for BoxBody payload type because it's the most useful for making
// simple handlers without async blocks. Making it generic over all MessageBody types requires a // simple handlers without async blocks. Making it generic over all MessageBody types requires a
// future impl on Response which would cause it's body field to be, undesirably, Option<B>. // future impl on Response which would cause its body field to be, undesirably, Option<B>.
// //
// This impl is not particularly efficient due to the Response construction and should probably // This impl is not particularly efficient due to the Response construction and should probably
// not be invoked if performance is important. Prefer an async fn/block in such cases. // not be invoked if performance is important. Prefer an async fn/block in such cases.

View File

@ -95,6 +95,18 @@ impl ServiceRequest {
(&mut self.req, &mut self.payload) (&mut self.req, &mut self.payload)
} }
/// Returns immutable accessors to inner parts.
#[inline]
pub fn parts(&self) -> (&HttpRequest, &Payload) {
(&self.req, &self.payload)
}
/// Returns immutable accessor to inner [`HttpRequest`].
#[inline]
pub fn request(&self) -> &HttpRequest {
&self.req
}
/// Derives a type from this request using an [extractor](crate::FromRequest). /// Derives a type from this request using an [extractor](crate::FromRequest).
/// ///
/// Returns the `T` extractor's `Future` type which can be `await`ed. This is particularly handy /// Returns the `T` extractor's `Future` type which can be `await`ed. This is particularly handy

View File

@ -1,6 +1,8 @@
# Changes # Changes
## Unreleased - 2021-xx-xx ## Unreleased - 2022-xx-xx
### Changed
- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.
## 3.0.0 - 2022-03-07 ## 3.0.0 - 2022-03-07

View File

@ -1 +1 @@
msrv = "1.54" msrv = "1.57"