mirror of
https://github.com/fafhrd91/actix-web
synced 2025-07-15 22:34:47 +02:00
Compare commits
38 Commits
http-v3.0.
...
http-v3.0.
Author | SHA1 | Date | |
---|---|---|---|
|
5611b98c0d | ||
|
dce9438518 | ||
|
be986d96b3 | ||
|
8ddb24b49b | ||
|
87f627cd5d | ||
|
03456b8a33 | ||
|
8c2fad3164 | ||
|
62fbd225bc | ||
|
0fa4d999d9 | ||
|
da4c849f62 | ||
|
49cd303c3b | ||
|
955c3ac0c4 | ||
|
56e5c19b85 | ||
|
3f03af1c59 | ||
|
25c0673278 | ||
|
e7a05f9892 | ||
|
2f13e5f675 | ||
|
9f964751f6 | ||
|
fcca515387 | ||
|
075932d823 | ||
|
cb379c0e0c | ||
|
d4a5d450de | ||
|
542200cbc2 | ||
|
d0c08dbb7d | ||
|
d0b5fb18d2 | ||
|
12fb3412a5 | ||
|
2665357a0c | ||
|
693271e571 | ||
|
10ef9b0751 | ||
|
ce00c88963 | ||
|
75e6ffb057 | ||
|
ad38973767 | ||
|
1c1d6477ef | ||
|
53509a5361 | ||
|
a6f27baff1 | ||
|
218e34ee17 | ||
|
11bfa84926 | ||
|
5aa6f713c7 |
@@ -1,4 +1,4 @@
|
||||
# Changes
|
||||
# Changelog
|
||||
|
||||
Changelogs are kept separately for each crate in this repo.
|
||||
|
||||
|
@@ -1,6 +1,13 @@
|
||||
# Changes
|
||||
|
||||
## Unreleased - 2021-xx-xx
|
||||
- Add support for streaming audio files by setting the `content-disposition` header `inline` instead of `attachement`. [#2645]
|
||||
|
||||
[#2645]: https://github.com/actix/actix-web/pull/2645
|
||||
|
||||
|
||||
## 0.6.0 - 2022-02-25
|
||||
- No significant changes since `0.6.0-beta.16`.
|
||||
|
||||
|
||||
## 0.6.0-beta.16 - 2022-01-31
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "actix-files"
|
||||
version = "0.6.0-beta.16"
|
||||
version = "0.6.0"
|
||||
authors = [
|
||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||
"fakeshadow <24548779@qq.com>",
|
||||
@@ -22,10 +22,10 @@ path = "src/lib.rs"
|
||||
experimental-io-uring = ["actix-web/experimental-io-uring", "tokio-uring"]
|
||||
|
||||
[dependencies]
|
||||
actix-http = "3.0.0-rc.4"
|
||||
actix-http = "3"
|
||||
actix-service = "2"
|
||||
actix-utils = "3"
|
||||
actix-web = { version = "4.0.0-rc.3", default-features = false }
|
||||
actix-web = { version = "4", default-features = false }
|
||||
|
||||
askama_escape = "0.10"
|
||||
bitflags = "1"
|
||||
@@ -44,5 +44,5 @@ tokio-uring = { version = "0.2", optional = true, features = ["bytes"] }
|
||||
[dev-dependencies]
|
||||
actix-rt = "2.2"
|
||||
actix-test = "0.1.0-beta.13"
|
||||
actix-web = "4.0.0-rc.3"
|
||||
actix-web = "4.0.0"
|
||||
tempfile = "3.2"
|
||||
|
@@ -3,11 +3,11 @@
|
||||
> Static file serving for Actix Web
|
||||
|
||||
[](https://crates.io/crates/actix-files)
|
||||
[](https://docs.rs/actix-files/0.6.0-beta.16)
|
||||
[](https://docs.rs/actix-files/0.6.0)
|
||||
[](https://blog.rust-lang.org/2021/05/06/Rust-1.54.0.html)
|
||||

|
||||
<br />
|
||||
[](https://deps.rs/crate/actix-files/0.6.0-beta.16)
|
||||
[](https://deps.rs/crate/actix-files/0.6.0)
|
||||
[](https://crates.io/crates/actix-files)
|
||||
[](https://discord.gg/NWpN5mmg3x)
|
||||
|
||||
|
@@ -81,7 +81,7 @@ async fn chunked_read_file_callback(
|
||||
) -> Result<(File, Bytes), Error> {
|
||||
use io::{Read as _, Seek as _};
|
||||
|
||||
let res = actix_web::rt::task::spawn_blocking(move || {
|
||||
let res = actix_web::web::block(move || {
|
||||
let mut buf = Vec::with_capacity(max_bytes);
|
||||
|
||||
file.seek(io::SeekFrom::Start(offset))?;
|
||||
@@ -94,8 +94,7 @@ async fn chunked_read_file_callback(
|
||||
Ok((file, Bytes::from(buf)))
|
||||
}
|
||||
})
|
||||
.await
|
||||
.map_err(|_| actix_web::error::BlockingError)??;
|
||||
.await??;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
@@ -96,18 +96,18 @@ impl NamedFile {
|
||||
///
|
||||
/// # Examples
|
||||
/// ```ignore
|
||||
/// use std::{
|
||||
/// io::{self, Write as _},
|
||||
/// env,
|
||||
/// fs::File
|
||||
/// };
|
||||
/// use actix_files::NamedFile;
|
||||
/// use std::io::{self, Write};
|
||||
/// use std::env;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut file = File::create("foo.txt")?;
|
||||
/// file.write_all(b"Hello, world!")?;
|
||||
/// let named_file = NamedFile::from_file(file, "bar.txt")?;
|
||||
/// # std::fs::remove_file("foo.txt");
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// let mut file = File::create("foo.txt")?;
|
||||
/// file.write_all(b"Hello, world!")?;
|
||||
/// let named_file = NamedFile::from_file(file, "bar.txt")?;
|
||||
/// # std::fs::remove_file("foo.txt");
|
||||
/// Ok(())
|
||||
/// ```
|
||||
pub fn from_file<P: AsRef<Path>>(file: File, path: P) -> io::Result<NamedFile> {
|
||||
let path = path.as_ref().to_path_buf();
|
||||
@@ -128,7 +128,7 @@ impl NamedFile {
|
||||
let ct = from_path(&path).first_or_octet_stream();
|
||||
|
||||
let disposition = match ct.type_() {
|
||||
mime::IMAGE | mime::TEXT | mime::VIDEO => DispositionType::Inline,
|
||||
mime::IMAGE | mime::TEXT | mime::AUDIO | mime::VIDEO => DispositionType::Inline,
|
||||
mime::APPLICATION => match ct.subtype() {
|
||||
mime::JAVASCRIPT | mime::JSON => DispositionType::Inline,
|
||||
name if name == "wasm" => DispositionType::Inline,
|
||||
|
@@ -29,13 +29,13 @@ default = []
|
||||
openssl = ["tls-openssl", "awc/openssl"]
|
||||
|
||||
[dependencies]
|
||||
actix-service = "2.0.0"
|
||||
actix-service = "2"
|
||||
actix-codec = "0.5"
|
||||
actix-tls = "3"
|
||||
actix-utils = "3.0.0"
|
||||
actix-utils = "3"
|
||||
actix-rt = "2.2"
|
||||
actix-server = "2"
|
||||
awc = { version = "3.0.0-beta.21", default-features = false }
|
||||
awc = { version = "3", default-features = false }
|
||||
|
||||
base64 = "0.13"
|
||||
bytes = "1"
|
||||
@@ -51,5 +51,5 @@ tls-openssl = { version = "0.10.9", package = "openssl", optional = true }
|
||||
tokio = { version = "1.8.4", features = ["sync"] }
|
||||
|
||||
[dev-dependencies]
|
||||
actix-web = { version = "4.0.0-rc.3", default-features = false, features = ["cookies"] }
|
||||
actix-http = "3.0.0-rc.4"
|
||||
actix-web = { version = "4", default-features = false, features = ["cookies"] }
|
||||
actix-http = "3"
|
||||
|
@@ -3,7 +3,316 @@
|
||||
## Unreleased - 2021-xx-xx
|
||||
|
||||
|
||||
## 3.0.4 - 2022-03-09
|
||||
### Fixed
|
||||
- Document on docs.rs with `ws` feature enabled.
|
||||
|
||||
|
||||
## 3.0.3 - 2022-03-08
|
||||
### Fixed
|
||||
- Allow spaces between header name and colon when parsing responses. [#2684]
|
||||
|
||||
[#2684]: https://github.com/actix/actix-web/issues/2684
|
||||
|
||||
|
||||
## 3.0.2 - 2022-03-05
|
||||
### Fixed
|
||||
- Fix encoding camel-case header names with more than one hyphen. [#2683]
|
||||
|
||||
[#2683]: https://github.com/actix/actix-web/issues/2683
|
||||
|
||||
|
||||
## 3.0.1 - 2022-03-04
|
||||
- Fix panic in H1 dispatcher when pipelining is used with keep-alive. [#2678]
|
||||
|
||||
[#2678]: https://github.com/actix/actix-web/issues/2678
|
||||
|
||||
## 3.0.0 - 2022-02-25
|
||||
### Dependencies
|
||||
- Updated `actix-*` to Tokio v1-based versions. [#1813]
|
||||
- Updated `bytes` to `1.0`. [#1813]
|
||||
- Updated `h2` to `0.3`. [#1813]
|
||||
- Updated `rustls` to `0.20.0`. [#2414]
|
||||
- Updated `language-tags` to `0.3`.
|
||||
- Updated `tokio` to `1`.
|
||||
|
||||
### Added
|
||||
- Crate Features:
|
||||
- `ws`; disabled by default. [#2618]
|
||||
- `http2`; disabled by default. [#2618]
|
||||
- `compress-brotli`; disabled by default. [#2618]
|
||||
- `compress-gzip`; disabled by default. [#2618]
|
||||
- `compress-zstd`; disabled by default. [#2618]
|
||||
- Functions:
|
||||
- `body::to_bytes` for async collecting message body into Bytes. [#2158]
|
||||
- Traits:
|
||||
- `TryIntoHeaderPair`; allows using typed and untyped headers in the same methods. [#1869]
|
||||
- Types:
|
||||
- `body::BoxBody`; a boxed message body with boxed errors. [#2183]
|
||||
- `body::EitherBody` enum. [#2468]
|
||||
- `body::None` struct. [#2468]
|
||||
- Re-export `http` crate's `Error` type as `error::HttpError`. [#2171]
|
||||
- Variants:
|
||||
- `ContentEncoding::Zstd` along with . [#2244]
|
||||
- `Protocol::Http3` for future compatibility and also mark `#[non_exhaustive]`. [00ba8d55]
|
||||
- Methods:
|
||||
- `ContentEncoding::to_header_value()`. [#2501]
|
||||
- `header::QualityItem::{max, min}()`. [#2486]
|
||||
- `header::QualityItem::zero()` that uses `Quality::ZERO`. [#2501]
|
||||
- `HeaderMap::drain()` as an efficient draining iterator. [#1964]
|
||||
- `HeaderMap::len_keys()` has the behavior of the old `len` method. [#1964]
|
||||
- `MessageBody::boxed` trait method for wrapping boxing types efficiently. [#2520]
|
||||
- `MessageBody::try_into_bytes` trait method, with default implementation, for optimizations on body types that complete in exactly one poll. [#2522]
|
||||
- `Request::conn_data()`. [#2491]
|
||||
- `Request::take_conn_data()`. [#2491]
|
||||
- `Request::take_req_data()`. [#2487]
|
||||
- `Response::{ok, bad_request, not_found, internal_server_error}()`. [#2159]
|
||||
- `Response::into_body()` that consumes response and returns body type. [#2201]
|
||||
- `Response::map_into_boxed_body()`. [#2468]
|
||||
- `ResponseBuilder::append_header()` method which allows using typed and untyped headers. [#1869]
|
||||
- `ResponseBuilder::insert_header()` method which allows using typed and untyped headers. [#1869]
|
||||
- `ResponseHead::set_camel_case_headers()`. [#2587]
|
||||
- `TestRequest::insert_header()` method which allows using typed and untyped headers. [#1869]
|
||||
- Implementations:
|
||||
- Implement `Clone for ws::HandshakeError`. [#2468]
|
||||
- Implement `Clone` for `body::AnyBody<S> where S: Clone`. [#2448]
|
||||
- Implement `Clone` for `RequestHead`. [#2487]
|
||||
- Implement `Clone` for `ResponseHead`. [#2585]
|
||||
- Implement `Copy` for `QualityItem<T> where T: Copy`. [#2501]
|
||||
- Implement `Default` for `ContentEncoding`. [#1912]
|
||||
- Implement `Default` for `HttpServiceBuilder`. [#2611]
|
||||
- Implement `Default` for `KeepAlive`. [#2611]
|
||||
- Implement `Default` for `Response`. [#2201]
|
||||
- Implement `Default` for `ws::Codec`. [#1920]
|
||||
- Implement `Display` for `header::Quality`. [#2486]
|
||||
- Implement `Eq` for `header::ContentEncoding`. [#2501]
|
||||
- Implement `ExactSizeIterator` and `FusedIterator` for all `HeaderMap` iterators. [#2470]
|
||||
- Implement `From<Duration>` for `KeepAlive`. [#2611]
|
||||
- Implement `From<Option<Duration>>` for `KeepAlive`. [#2611]
|
||||
- Implement `From<Vec<u8>>` for `Response<Vec<u8>>`. [#2625]
|
||||
- Implement `FromStr` for `ContentEncoding`. [#1912]
|
||||
- Implement `Header` for `ContentEncoding`. [#1912]
|
||||
- Implement `IntoHeaderValue` for `ContentEncoding`. [#1912]
|
||||
- Implement `IntoIterator` for `HeaderMap`. [#1964]
|
||||
- Implement `MessageBody` for `bytestring::ByteString`. [#2468]
|
||||
- Implement `MessageBody` for `Pin<Box<T>> where T: MessageBody`. [#2152]
|
||||
- Misc:
|
||||
- Re-export `StatusCode`, `Method`, `Version` and `Uri` at the crate root. [#2171]
|
||||
- Re-export `ContentEncoding` and `ConnectionType` at the crate root. [#2171]
|
||||
- `Quality::ZERO` associated constant equivalent to `q=0`. [#2501]
|
||||
- `header::Quality::{MAX, MIN}` associated constants equivalent to `q=1` and `q=0.001`, respectively. [#2486]
|
||||
- Timeout for canceling HTTP/2 server side connection handshake. Configurable with `ServiceConfig::client_timeout`; defaults to 5 seconds. [#2483]
|
||||
- `#[must_use]` for `ws::Codec` to prevent subtle bugs. [#1920]
|
||||
|
||||
### Changed
|
||||
- Traits:
|
||||
- Rename `IntoHeaderValue => TryIntoHeaderValue`. [#2510]
|
||||
- `MessageBody` now has an associated `Error` type. [#2183]
|
||||
- Types:
|
||||
- `Protocol` enum is now marked `#[non_exhaustive]`.
|
||||
- `error::DispatcherError` enum is now marked `#[non_exhaustive]`. [#2624]
|
||||
- `ContentEncoding` is now marked `#[non_exhaustive]`. [#2377]
|
||||
- Error enums are marked `#[non_exhaustive]`. [#2161]
|
||||
- Rename `PayloadStream` to `BoxedPayloadStream`. [#2545]
|
||||
- The body type parameter of `Response` no longer has a default. [#2152]
|
||||
- Enum Variants:
|
||||
- Rename `ContentEncoding::{Br => Brotli}`. [#2501]
|
||||
- `Payload` inner fields are now named. [#2545]
|
||||
- `ws::Message::Text` now contains a `bytestring::ByteString`. [#1864]
|
||||
- Methods:
|
||||
- Rename `ServiceConfig::{client_timer_expire => client_request_deadline}`. [#2611]
|
||||
- Rename `ServiceConfig::{client_disconnect_timer => client_disconnect_deadline}`. [#2611]
|
||||
- Rename `h1::Codec::{keepalive => keep_alive}`. [#2611]
|
||||
- Rename `h1::Codec::{keepalive_enabled => keep_alive_enabled}`. [#2611]
|
||||
- Rename `h1::ClientCodec::{keepalive => keep_alive}`. [#2611]
|
||||
- Rename `h1::ClientPayloadCodec::{keepalive => keep_alive}`. [#2611]
|
||||
- Rename `header::EntityTag::{weak => new_weak, strong => new_strong}`. [#2565]
|
||||
- Rename `TryIntoHeaderValue::{try_into => try_into_value}` to avoid ambiguity with std `TryInto` trait. [#1894]
|
||||
- Deadline methods in `ServiceConfig` now return `std::time::Instant`s instead of Tokio's wrapper type. [#2611]
|
||||
- Places in `Response` where `ResponseBody<B>` was received or returned now simply use `B`. [#2201]
|
||||
- `encoding::Encoder::response` now returns `AnyBody<Encoder<B>>`. [#2448]
|
||||
- `Extensions::insert` returns replaced item. [#1904]
|
||||
- `HeaderMap::get_all` now returns a `std::slice::Iter`. [#2527]
|
||||
- `HeaderMap::insert` now returns iterator of removed values. [#1964]
|
||||
- `HeaderMap::len` now returns number of values instead of number of keys. [#1964]
|
||||
- `HeaderMap::remove` now returns iterator of removed values. [#1964]
|
||||
- `ResponseBuilder::body(B)` now returns `Response<EitherBody<B>>`. [#2468]
|
||||
- `ResponseBuilder::content_type` now takes an `impl TryIntoHeaderValue` to support using typed `mime` types. [#1894]
|
||||
- `ResponseBuilder::finish()` now returns `Response<EitherBody<()>>`. [#2468]
|
||||
- `ResponseBuilder::json` now takes `impl Serialize`. [#2052]
|
||||
- `ResponseBuilder::message_body` now returns a `Result`. [#2201]∑
|
||||
- `ServiceConfig::keep_alive` now returns a `KeepAlive`. [#2611]
|
||||
- `ws::hash_key` now returns array. [#2035]
|
||||
- Trait Implementations:
|
||||
- Implementation of `Stream` for `Payload` no longer requires the `Stream` variant be `Unpin`. [#2545]
|
||||
- Implementation of `Future` for `h1::SendResponse` no longer requires the body type be `Unpin`. [#2545]
|
||||
- Implementation of `Stream` for `encoding::Decoder` no longer requires the stream type be `Unpin`. [#2545]
|
||||
- Implementation of `From` for error types now return a `Response<BoxBody>`. [#2468]
|
||||
- Misc:
|
||||
- `header` module is now public. [#2171]
|
||||
- `uri` module is now public. [#2171]
|
||||
- Request-local data container is no longer part of a `RequestHead`. Instead it is a distinct part of a `Request`. [#2487]
|
||||
- All error trait bounds in server service builders have changed from `Into<Error>` to `Into<Response<BoxBody>>`. [#2253]
|
||||
- All error trait bounds in message body and stream impls changed from `Into<Error>` to `Into<Box<dyn std::error::Error>>`. [#2253]
|
||||
- Guarantee ordering of `header::GetAll` iterator to be same as insertion order. [#2467]
|
||||
- Connection data set through the `on_connect_ext` callbacks is now accessible only from the new `Request::conn_data()` method. [#2491]
|
||||
- Brotli (de)compression support is now provided by the `brotli` crate. [#2538]
|
||||
- Minimum supported Rust version (MSRV) is now 1.54.
|
||||
|
||||
### Fixed
|
||||
- A `Vary` header is now correctly sent along with compressed content. [#2501]
|
||||
- HTTP/1.1 dispatcher correctly uses client request timeout. [#2611]
|
||||
- Fixed issue where handlers that took payload but then dropped without reading it to EOF it would cause keep-alive connections to become stuck. [#2624]
|
||||
- `ContentEncoding`'s `Identity` variant can now be parsed from a string. [#2501]
|
||||
- `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuration parameter. [#2226]
|
||||
- Remove unnecessary `Into<Error>` bound on `Encoder` body types. [#2375]
|
||||
- Remove unnecessary `Unpin` bound on `ResponseBuilder::streaming`. [#2253]
|
||||
- `BodyStream` and `SizedStream` are no longer restricted to `Unpin` types. [#2152]
|
||||
- Fixed slice creation pointing to potential uninitialized data on h1 encoder. [#2364]
|
||||
- Fixed quality parse error in Accept-Encoding header. [#2344]
|
||||
|
||||
### Removed
|
||||
- Crate Features:
|
||||
- `compress` feature. [#2065]
|
||||
- `cookies` feature. [#2065]
|
||||
- `trust-dns` feature. [#2425]
|
||||
- `actors` optional feature and trait implementation for `actix` types. [#1969]
|
||||
- Functions:
|
||||
- `header::qitem` helper. Replaced with `header::QualityItem::max`. [#2486]
|
||||
- Types:
|
||||
- `body::Body`; replaced with `EitherBody` and `BoxBody`. [#2468]
|
||||
- `body::ResponseBody`. [#2446]
|
||||
- `ConnectError::SslHandshakeError` and re-export of `HandshakeError`. Due to the removal of this type from `tokio-openssl` crate. OpenSSL handshake error now returns `ConnectError::SslError`. [#1813]
|
||||
- `error::Canceled` re-export. [#1994]
|
||||
- `error::Result` type alias. [#2201]
|
||||
- `error::BlockingError` [#2660]
|
||||
- `InternalError` and all the error types it constructed were moved up to `actix-web`. [#2215]
|
||||
- Typed HTTP headers; they have moved up to `actix-web`. [2094]
|
||||
- Re-export of `http` crate's `HeaderMap` types in addition to ours. [#2171]
|
||||
- Enum Variants:
|
||||
- `body::BodySize::Empty`; an empty body can now only be represented as a `Sized(0)` variant. [#2446]
|
||||
- `ContentEncoding::Auto`. [#2501]
|
||||
- `EncoderError::Boxed`. [#2446]
|
||||
- Methods:
|
||||
- `ContentEncoding::is_compression()`. [#2501]
|
||||
- `h1::Payload::readany()`. [#2545]
|
||||
- `HttpMessage::cookie[s]()` trait methods. [#2065]
|
||||
- `HttpServiceBuilder::new()`; use `default` instead. [#2611]
|
||||
- `on_connect` (previously deprecated) methods have been removed; use `on_connect_ext`. [#1857]
|
||||
- `Response::build_from()`. [#2159]
|
||||
- `Response::error()` [#2205]
|
||||
- `Response::take_body()` and old `Response::into_body()` method that casted body type. [#2201]
|
||||
- `Response`'s status code builders. [#2159]
|
||||
- `ResponseBuilder::{if_true, if_some}()` (previously deprecated). [#2148]
|
||||
- `ResponseBuilder::{set, set_header}()`; use `ResponseBuilder::insert_header()`. [#1869]
|
||||
- `ResponseBuilder::extensions[_mut]()`. [#2585]
|
||||
- `ResponseBuilder::header()`; use `ResponseBuilder::append_header()`. [#1869]
|
||||
- `ResponseBuilder::json()`. [#2148]
|
||||
- `ResponseBuilder::json2()`. [#1903]
|
||||
- `ResponseBuilder::streaming()`. [#2468]
|
||||
- `ResponseHead::extensions[_mut]()`. [#2585]
|
||||
- `ServiceConfig::{client_timer, keep_alive_timer}()`. [#2611]
|
||||
- `TestRequest::with_hdr()`; use `TestRequest::default().insert_header()`. [#1869]
|
||||
- `TestRequest::with_header()`; use `TestRequest::default().insert_header()`. [#1869]
|
||||
- Trait implementations:
|
||||
- Implementation of `Copy` for `ws::Codec`. [#1920]
|
||||
- Implementation of `From<Option<usize>> for KeepAlive`; use `Duration`s instead. [#2611]
|
||||
- Implementation of `From<serde_json::Value>` for `Body`. [#2148]
|
||||
- Implementation of `From<usize> for KeepAlive`; use `Duration`s instead. [#2611]
|
||||
- Implementation of `Future` for `Response`. [#2201]
|
||||
- Implementation of `Future` for `ResponseBuilder`. [#2468]
|
||||
- Implementation of `Into<Error>` for `Response<Body>`. [#2215]
|
||||
- Implementation of `Into<Error>` for `ResponseBuilder`. [#2215]
|
||||
- Implementation of `ResponseError` for `actix_utils::timeout::TimeoutError`. [#2127]
|
||||
- Implementation of `ResponseError` for `CookieParseError`. [#2065]
|
||||
- Implementation of `TryFrom<u16>` for `header::Quality`. [#2486]
|
||||
- Misc:
|
||||
- `http` module; most everything it contained is exported at the crate root. [#2488]
|
||||
- `cookies` module (re-export). [#2065]
|
||||
- `client` module. Connector types now live in `awc`. [#2425]
|
||||
- `error` field from `Response`. [#2205]
|
||||
- `downcast` and `downcast_get_type_id` macros. [#2291]
|
||||
- Down-casting for `MessageBody` types; use standard `Any` trait. [#2183]
|
||||
|
||||
|
||||
[#1813]: https://github.com/actix/actix-web/pull/1813
|
||||
[#1845]: https://github.com/actix/actix-web/pull/1845
|
||||
[#1857]: https://github.com/actix/actix-web/pull/1857
|
||||
[#1864]: https://github.com/actix/actix-web/pull/1864
|
||||
[#1869]: https://github.com/actix/actix-web/pull/1869
|
||||
[#1878]: https://github.com/actix/actix-web/pull/1878
|
||||
[#1894]: https://github.com/actix/actix-web/pull/1894
|
||||
[#1903]: https://github.com/actix/actix-web/pull/1903
|
||||
[#1904]: https://github.com/actix/actix-web/pull/1904
|
||||
[#1912]: https://github.com/actix/actix-web/pull/1912
|
||||
[#1920]: https://github.com/actix/actix-web/pull/1920
|
||||
[#1964]: https://github.com/actix/actix-web/pull/1964
|
||||
[#1969]: https://github.com/actix/actix-web/pull/1969
|
||||
[#1981]: https://github.com/actix/actix-web/pull/1981
|
||||
[#1994]: https://github.com/actix/actix-web/pull/1994
|
||||
[#2035]: https://github.com/actix/actix-web/pull/2035
|
||||
[#2052]: https://github.com/actix/actix-web/pull/2052
|
||||
[#2065]: https://github.com/actix/actix-web/pull/2065
|
||||
[#2094]: https://github.com/actix/actix-web/pull/2094
|
||||
[#2127]: https://github.com/actix/actix-web/pull/2127
|
||||
[#2148]: https://github.com/actix/actix-web/pull/2148
|
||||
[#2152]: https://github.com/actix/actix-web/pull/2152
|
||||
[#2158]: https://github.com/actix/actix-web/pull/2158
|
||||
[#2159]: https://github.com/actix/actix-web/pull/2159
|
||||
[#2161]: https://github.com/actix/actix-web/pull/2161
|
||||
[#2171]: https://github.com/actix/actix-web/pull/2171
|
||||
[#2183]: https://github.com/actix/actix-web/pull/2183
|
||||
[#2196]: https://github.com/actix/actix-web/pull/2196
|
||||
[#2201]: https://github.com/actix/actix-web/pull/2201
|
||||
[#2205]: https://github.com/actix/actix-web/pull/2205
|
||||
[#2215]: https://github.com/actix/actix-web/pull/2215
|
||||
[#2244]: https://github.com/actix/actix-web/pull/2244
|
||||
[#2250]: https://github.com/actix/actix-web/pull/2250
|
||||
[#2253]: https://github.com/actix/actix-web/pull/2253
|
||||
[#2291]: https://github.com/actix/actix-web/pull/2291
|
||||
[#2344]: https://github.com/actix/actix-web/pull/2344
|
||||
[#2364]: https://github.com/actix/actix-web/pull/2364
|
||||
[#2375]: https://github.com/actix/actix-web/pull/2375
|
||||
[#2377]: https://github.com/actix/actix-web/pull/2377
|
||||
[#2414]: https://github.com/actix/actix-web/pull/2414
|
||||
[#2425]: https://github.com/actix/actix-web/pull/2425
|
||||
[#2442]: https://github.com/actix/actix-web/pull/2442
|
||||
[#2446]: https://github.com/actix/actix-web/pull/2446
|
||||
[#2448]: https://github.com/actix/actix-web/pull/2448
|
||||
[#2456]: https://github.com/actix/actix-web/pull/2456
|
||||
[#2467]: https://github.com/actix/actix-web/pull/2467
|
||||
[#2468]: https://github.com/actix/actix-web/pull/2468
|
||||
[#2470]: https://github.com/actix/actix-web/pull/2470
|
||||
[#2474]: https://github.com/actix/actix-web/pull/2474
|
||||
[#2483]: https://github.com/actix/actix-web/pull/2483
|
||||
[#2486]: https://github.com/actix/actix-web/pull/2486
|
||||
[#2487]: https://github.com/actix/actix-web/pull/2487
|
||||
[#2488]: https://github.com/actix/actix-web/pull/2488
|
||||
[#2491]: https://github.com/actix/actix-web/pull/2491
|
||||
[#2497]: https://github.com/actix/actix-web/pull/2497
|
||||
[#2501]: https://github.com/actix/actix-web/pull/2501
|
||||
[#2510]: https://github.com/actix/actix-web/pull/2510
|
||||
[#2520]: https://github.com/actix/actix-web/pull/2520
|
||||
[#2522]: https://github.com/actix/actix-web/pull/2522
|
||||
[#2527]: https://github.com/actix/actix-web/pull/2527
|
||||
[#2538]: https://github.com/actix/actix-web/pull/2538
|
||||
[#2545]: https://github.com/actix/actix-web/pull/2545
|
||||
[#2565]: https://github.com/actix/actix-web/pull/2565
|
||||
[#2585]: https://github.com/actix/actix-web/pull/2585
|
||||
[#2587]: https://github.com/actix/actix-web/pull/2587
|
||||
[#2611]: https://github.com/actix/actix-web/pull/2611
|
||||
[#2618]: https://github.com/actix/actix-web/pull/2618
|
||||
[#2624]: https://github.com/actix/actix-web/pull/2624
|
||||
[#2625]: https://github.com/actix/actix-web/pull/2625
|
||||
[#2660]: https://github.com/actix/actix-web/pull/2660
|
||||
[00ba8d55]: https://github.com/actix/actix-web/commit/00ba8d55492284581695d824648590715a8bd386
|
||||
|
||||
|
||||
<details>
|
||||
<summary>3.0.0 Pre-Releases</summary>
|
||||
|
||||
## 3.0.0-rc.4 - 2022-02-22
|
||||
### Fixed
|
||||
- Fix h1 dispatcher panic. [1ce58ecb]
|
||||
|
||||
[1ce58ecb]: https://github.com/actix/actix-web/commit/1ce58ecb305c60e51db06e6c913b7a1344e229ca
|
||||
@@ -104,7 +413,7 @@
|
||||
|
||||
|
||||
## 3.0.0-beta.17 - 2021-12-27
|
||||
### Changes
|
||||
### Changed
|
||||
- `HeaderMap::get_all` now returns a `std::slice::Iter`. [#2527]
|
||||
- `Payload` inner fields are now named. [#2545]
|
||||
- `impl Stream` for `Payload` no longer requires the `Stream` variant be `Unpin`. [#2545]
|
||||
@@ -327,7 +636,7 @@
|
||||
- `Response::{ok, bad_request, not_found, internal_server_error}`. [#2159]
|
||||
- Helper `body::to_bytes` for async collecting message body into Bytes. [#2158]
|
||||
|
||||
### Changes
|
||||
### Changed
|
||||
- The type parameter of `Response` no longer has a default. [#2152]
|
||||
- The `Message` variant of `body::Body` is now `Pin<Box<dyn MessageBody>>`. [#2152]
|
||||
- `BodyStream` and `SizedStream` are no longer restricted to Unpin types. [#2152]
|
||||
@@ -460,10 +769,10 @@
|
||||
- Remove `ResponseError` impl for `actix::actors::resolver::ResolverError`
|
||||
due to deprecate of resolver actor. [#1813]
|
||||
- Remove `ConnectError::SslHandshakeError` and re-export of `HandshakeError`.
|
||||
due to the removal of this type from `tokio-openssl` crate. openssl handshake
|
||||
due to the removal of this type from `tokio-openssl` crate. openssl handshake
|
||||
error would return as `ConnectError::SslError`. [#1813]
|
||||
- Remove `actix-threadpool` dependency. Use `actix_rt::task::spawn_blocking`.
|
||||
Due to this change `actix_threadpool::BlockingError` type is moved into
|
||||
Due to this change `actix_threadpool::BlockingError` type is moved into
|
||||
`actix_http::error` module. [#1878]
|
||||
|
||||
[#1813]: https://github.com/actix/actix-web/pull/1813
|
||||
@@ -471,6 +780,8 @@
|
||||
[#1864]: https://github.com/actix/actix-web/pull/1864
|
||||
[#1878]: https://github.com/actix/actix-web/pull/1878
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
## 2.2.2 - 2022-01-21
|
||||
### Changed
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "actix-http"
|
||||
version = "3.0.0-rc.4"
|
||||
version = "3.0.4"
|
||||
authors = [
|
||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||
"Rob Ede <robjtede@icloud.com>",
|
||||
@@ -20,7 +20,7 @@ edition = "2018"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
# features that docs.rs will build with
|
||||
features = ["http2", "openssl", "rustls", "compress-brotli", "compress-gzip", "compress-zstd"]
|
||||
features = ["http2", "ws", "openssl", "rustls", "compress-brotli", "compress-gzip", "compress-zstd"]
|
||||
|
||||
[lib]
|
||||
name = "actix_http"
|
||||
@@ -100,7 +100,7 @@ zstd = { version = "0.10", optional = true }
|
||||
actix-http-test = { version = "3.0.0-beta.13", features = ["openssl"] }
|
||||
actix-server = "2"
|
||||
actix-tls = { version = "3", features = ["openssl"] }
|
||||
actix-web = "4.0.0-rc.3"
|
||||
actix-web = "4.0.0"
|
||||
|
||||
async-stream = "0.3"
|
||||
criterion = { version = "0.3", features = ["html_reports"] }
|
||||
|
@@ -3,11 +3,11 @@
|
||||
> HTTP primitives for the Actix ecosystem.
|
||||
|
||||
[](https://crates.io/crates/actix-http)
|
||||
[](https://docs.rs/actix-http/3.0.0-rc.4)
|
||||
[](https://docs.rs/actix-http/3.0.4)
|
||||
[](https://blog.rust-lang.org/2021/05/06/Rust-1.54.0.html)
|
||||

|
||||
<br />
|
||||
[](https://deps.rs/crate/actix-http/3.0.0-rc.4)
|
||||
[](https://deps.rs/crate/actix-http/3.0.4)
|
||||
[](https://crates.io/crates/actix-http)
|
||||
[](https://discord.gg/NWpN5mmg3x)
|
||||
|
||||
|
@@ -18,7 +18,8 @@ async fn main() -> std::io::Result<()> {
|
||||
HttpService::build()
|
||||
// pass the app to service builder
|
||||
// map_config is used to map App's configuration to ServiceBuilder
|
||||
.finish(map_config(app, |_| AppConfig::default()))
|
||||
// h1 will configure server to only use HTTP/1.1
|
||||
.h1(map_config(app, |_| AppConfig::default()))
|
||||
.tcp()
|
||||
})?
|
||||
.run()
|
||||
|
@@ -19,7 +19,7 @@ use zstd::stream::write::Decoder as ZstdDecoder;
|
||||
|
||||
use crate::{
|
||||
encoding::Writer,
|
||||
error::{BlockingError, PayloadError},
|
||||
error::PayloadError,
|
||||
header::{ContentEncoding, HeaderMap, CONTENT_ENCODING},
|
||||
};
|
||||
|
||||
@@ -47,14 +47,17 @@ where
|
||||
ContentEncoding::Brotli => Some(ContentDecoder::Brotli(Box::new(
|
||||
brotli::DecompressorWriter::new(Writer::new(), 8_096),
|
||||
))),
|
||||
|
||||
#[cfg(feature = "compress-gzip")]
|
||||
ContentEncoding::Deflate => Some(ContentDecoder::Deflate(Box::new(
|
||||
ZlibDecoder::new(Writer::new()),
|
||||
))),
|
||||
|
||||
#[cfg(feature = "compress-gzip")]
|
||||
ContentEncoding::Gzip => Some(ContentDecoder::Gzip(Box::new(GzDecoder::new(
|
||||
Writer::new(),
|
||||
)))),
|
||||
|
||||
#[cfg(feature = "compress-zstd")]
|
||||
ContentEncoding::Zstd => Some(ContentDecoder::Zstd(Box::new(
|
||||
ZstdDecoder::new(Writer::new()).expect(
|
||||
@@ -98,8 +101,12 @@ where
|
||||
|
||||
loop {
|
||||
if let Some(ref mut fut) = this.fut {
|
||||
let (chunk, decoder) =
|
||||
ready!(Pin::new(fut).poll(cx)).map_err(|_| BlockingError)??;
|
||||
let (chunk, decoder) = ready!(Pin::new(fut).poll(cx)).map_err(|_| {
|
||||
PayloadError::Io(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Blocking task was cancelled unexpectedly",
|
||||
))
|
||||
})??;
|
||||
|
||||
*this.decoder = Some(decoder);
|
||||
this.fut.take();
|
||||
@@ -159,10 +166,13 @@ where
|
||||
enum ContentDecoder {
|
||||
#[cfg(feature = "compress-gzip")]
|
||||
Deflate(Box<ZlibDecoder<Writer>>),
|
||||
|
||||
#[cfg(feature = "compress-gzip")]
|
||||
Gzip(Box<GzDecoder<Writer>>),
|
||||
|
||||
#[cfg(feature = "compress-brotli")]
|
||||
Brotli(Box<brotli::DecompressorWriter<Writer>>),
|
||||
|
||||
// We need explicit 'static lifetime here because ZstdDecoder need lifetime
|
||||
// argument, and we use `spawn_blocking` in `Decoder::poll_next` that require `FnOnce() -> R + Send + 'static`
|
||||
#[cfg(feature = "compress-zstd")]
|
||||
|
@@ -23,7 +23,6 @@ use zstd::stream::write::Encoder as ZstdEncoder;
|
||||
use super::Writer;
|
||||
use crate::{
|
||||
body::{self, BodySize, MessageBody},
|
||||
error::BlockingError,
|
||||
header::{self, ContentEncoding, HeaderValue, CONTENT_ENCODING},
|
||||
ResponseHead, StatusCode,
|
||||
};
|
||||
@@ -173,7 +172,12 @@ where
|
||||
|
||||
if let Some(ref mut fut) = this.fut {
|
||||
let mut encoder = ready!(Pin::new(fut).poll(cx))
|
||||
.map_err(|_| EncoderError::Blocking(BlockingError))?
|
||||
.map_err(|_| {
|
||||
EncoderError::Io(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Blocking task was cancelled unexpectedly",
|
||||
))
|
||||
})?
|
||||
.map_err(EncoderError::Io)?;
|
||||
|
||||
let chunk = encoder.take();
|
||||
@@ -400,12 +404,11 @@ fn new_brotli_compressor() -> Box<brotli::CompressorWriter<Writer>> {
|
||||
#[derive(Debug, Display)]
|
||||
#[non_exhaustive]
|
||||
pub enum EncoderError {
|
||||
/// Wrapped body stream error.
|
||||
#[display(fmt = "body")]
|
||||
Body(Box<dyn StdError>),
|
||||
|
||||
#[display(fmt = "blocking")]
|
||||
Blocking(BlockingError),
|
||||
|
||||
/// Generic I/O error.
|
||||
#[display(fmt = "io")]
|
||||
Io(io::Error),
|
||||
}
|
||||
@@ -414,7 +417,6 @@ impl StdError for EncoderError {
|
||||
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||
match self {
|
||||
EncoderError::Body(err) => Some(&**err),
|
||||
EncoderError::Blocking(err) => Some(err),
|
||||
EncoderError::Io(err) => Some(err),
|
||||
}
|
||||
}
|
||||
|
@@ -51,7 +51,7 @@ impl Error {
|
||||
Self::new(Kind::SendResponse)
|
||||
}
|
||||
|
||||
#[allow(unused)] // reserved for future use (TODO: remove allow when being used)
|
||||
#[allow(unused)] // available for future use
|
||||
pub(crate) fn new_io() -> Self {
|
||||
Self::new(Kind::Io)
|
||||
}
|
||||
@@ -108,8 +108,10 @@ pub(crate) enum Kind {
|
||||
|
||||
impl fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// TODO: more detail
|
||||
f.write_str("actix_http::Error")
|
||||
f.debug_struct("actix_http::Error")
|
||||
.field("kind", &self.inner.kind)
|
||||
.field("cause", &self.inner.cause)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,12 +252,6 @@ impl From<ParseError> for Response<BoxBody> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A set of errors that can occur running blocking tasks in thread pool.
|
||||
#[derive(Debug, Display, Error)]
|
||||
#[display(fmt = "Blocking thread pool is gone")]
|
||||
// TODO: non-exhaustive
|
||||
pub struct BlockingError;
|
||||
|
||||
/// A set of errors that can occur during payload parsing.
|
||||
#[derive(Debug, Display)]
|
||||
#[non_exhaustive]
|
||||
@@ -293,13 +289,13 @@ impl std::error::Error for PayloadError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
PayloadError::Incomplete(None) => None,
|
||||
PayloadError::Incomplete(Some(err)) => Some(err as &dyn std::error::Error),
|
||||
PayloadError::Incomplete(Some(err)) => Some(err),
|
||||
PayloadError::EncodingCorrupted => None,
|
||||
PayloadError::Overflow => None,
|
||||
PayloadError::UnknownLength => None,
|
||||
#[cfg(feature = "http2")]
|
||||
PayloadError::Http2Payload(err) => Some(err as &dyn std::error::Error),
|
||||
PayloadError::Io(err) => Some(err as &dyn std::error::Error),
|
||||
PayloadError::Http2Payload(err) => Some(err),
|
||||
PayloadError::Io(err) => Some(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -323,15 +319,6 @@ impl From<io::Error> for PayloadError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BlockingError> for PayloadError {
|
||||
fn from(_: BlockingError) -> Self {
|
||||
PayloadError::Io(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Operation is canceled",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PayloadError> for Error {
|
||||
fn from(err: PayloadError) -> Self {
|
||||
Self::new_payload().with_cause(err)
|
||||
@@ -386,7 +373,6 @@ pub enum DispatchError {
|
||||
impl StdError for DispatchError {
|
||||
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||
match self {
|
||||
// TODO: error source extraction?
|
||||
DispatchError::Service(_res) => None,
|
||||
DispatchError::Body(err) => Some(&**err),
|
||||
DispatchError::Io(err) => Some(err),
|
||||
|
@@ -293,22 +293,35 @@ impl MessageType for ResponseHead {
|
||||
let mut headers: [HeaderIndex; MAX_HEADERS] = EMPTY_HEADER_INDEX_ARRAY;
|
||||
|
||||
let (len, ver, status, h_len) = {
|
||||
let mut parsed: [httparse::Header<'_>; MAX_HEADERS] = EMPTY_HEADER_ARRAY;
|
||||
// SAFETY:
|
||||
// Create an uninitialized array of `MaybeUninit`. The `assume_init` is safe because the
|
||||
// type we are claiming to have initialized here is a bunch of `MaybeUninit`s, which
|
||||
// do not require initialization.
|
||||
let mut parsed = unsafe {
|
||||
MaybeUninit::<[MaybeUninit<httparse::Header<'_>>; MAX_HEADERS]>::uninit()
|
||||
.assume_init()
|
||||
};
|
||||
|
||||
let mut res = httparse::Response::new(&mut parsed);
|
||||
match res.parse(src)? {
|
||||
let mut res = httparse::Response::new(&mut []);
|
||||
|
||||
let mut config = httparse::ParserConfig::default();
|
||||
config.allow_spaces_after_header_name_in_responses(true);
|
||||
|
||||
match config.parse_response_with_uninit_headers(&mut res, src, &mut parsed)? {
|
||||
httparse::Status::Complete(len) => {
|
||||
let version = if res.version.unwrap() == 1 {
|
||||
Version::HTTP_11
|
||||
} else {
|
||||
Version::HTTP_10
|
||||
};
|
||||
|
||||
let status = StatusCode::from_u16(res.code.unwrap())
|
||||
.map_err(|_| ParseError::Status)?;
|
||||
HeaderIndex::record(src, res.headers, &mut headers);
|
||||
|
||||
(len, version, status, res.headers.len())
|
||||
}
|
||||
|
||||
httparse::Status::Partial => {
|
||||
return if src.len() >= MAX_BUFFER_SIZE {
|
||||
error!("MAX_BUFFER_SIZE unprocessed data reached, closing");
|
||||
@@ -360,9 +373,6 @@ pub(crate) const EMPTY_HEADER_INDEX: HeaderIndex = HeaderIndex {
|
||||
pub(crate) const EMPTY_HEADER_INDEX_ARRAY: [HeaderIndex; MAX_HEADERS] =
|
||||
[EMPTY_HEADER_INDEX; MAX_HEADERS];
|
||||
|
||||
pub(crate) const EMPTY_HEADER_ARRAY: [httparse::Header<'static>; MAX_HEADERS] =
|
||||
[httparse::EMPTY_HEADER; MAX_HEADERS];
|
||||
|
||||
impl HeaderIndex {
|
||||
pub(crate) fn record(
|
||||
bytes: &[u8],
|
||||
|
@@ -375,8 +375,6 @@ where
|
||||
DispatchError::Io(err)
|
||||
})?;
|
||||
|
||||
this.flags.set(Flags::KEEP_ALIVE, this.codec.keep_alive());
|
||||
|
||||
Ok(size)
|
||||
}
|
||||
|
||||
@@ -459,7 +457,12 @@ where
|
||||
}
|
||||
|
||||
// all messages are dealt with
|
||||
None => return Ok(PollResponse::DoNothing),
|
||||
None => {
|
||||
// start keep-alive if last request allowed it
|
||||
this.flags.set(Flags::KEEP_ALIVE, this.codec.keep_alive());
|
||||
|
||||
return Ok(PollResponse::DoNothing);
|
||||
}
|
||||
},
|
||||
|
||||
StateProj::ServiceCall { fut } => {
|
||||
@@ -757,6 +760,7 @@ where
|
||||
|
||||
let mut updated = false;
|
||||
|
||||
// decode from read buf as many full requests as possible
|
||||
loop {
|
||||
match this.codec.decode(this.read_buf) {
|
||||
Ok(Some(msg)) => {
|
||||
|
@@ -517,6 +517,7 @@ unsafe fn write_camel_case(value: &[u8], buf: *mut u8, len: usize) {
|
||||
if let Some(c @ b'a'..=b'z') = iter.next() {
|
||||
buffer[index] = c & 0b1101_1111;
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
|
||||
index += 1;
|
||||
@@ -528,7 +529,7 @@ mod tests {
|
||||
use std::rc::Rc;
|
||||
|
||||
use bytes::Bytes;
|
||||
use http::header::AUTHORIZATION;
|
||||
use http::header::{AUTHORIZATION, UPGRADE_INSECURE_REQUESTS};
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
@@ -559,6 +560,9 @@ mod tests {
|
||||
head.headers
|
||||
.insert(CONTENT_TYPE, HeaderValue::from_static("plain/text"));
|
||||
|
||||
head.headers
|
||||
.insert(UPGRADE_INSECURE_REQUESTS, HeaderValue::from_static("1"));
|
||||
|
||||
let mut head = RequestHeadType::Owned(head);
|
||||
|
||||
let _ = head.encode_headers(
|
||||
@@ -574,6 +578,7 @@ mod tests {
|
||||
assert!(data.contains("Connection: close\r\n"));
|
||||
assert!(data.contains("Content-Type: plain/text\r\n"));
|
||||
assert!(data.contains("Date: date\r\n"));
|
||||
assert!(data.contains("Upgrade-Insecure-Requests: 1\r\n"));
|
||||
|
||||
let _ = head.encode_headers(
|
||||
&mut bytes,
|
||||
|
@@ -25,7 +25,9 @@ use pin_project_lite::pin_project;
|
||||
use crate::{
|
||||
body::{BodySize, BoxBody, MessageBody},
|
||||
config::ServiceConfig,
|
||||
header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING},
|
||||
header::{
|
||||
HeaderName, HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING, UPGRADE,
|
||||
},
|
||||
service::HttpFlow,
|
||||
Extensions, OnConnectData, Payload, Request, Response, ResponseHead,
|
||||
};
|
||||
@@ -306,13 +308,22 @@ fn prepare_response(
|
||||
|
||||
// copy headers
|
||||
for (key, value) in head.headers.iter() {
|
||||
match *key {
|
||||
// TODO: consider skipping other headers according to:
|
||||
// https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.2
|
||||
// omit HTTP/1.x only headers
|
||||
CONNECTION | TRANSFER_ENCODING => continue,
|
||||
CONTENT_LENGTH if skip_len => continue,
|
||||
DATE => has_date = true,
|
||||
match key {
|
||||
// omit HTTP/1.x only headers according to:
|
||||
// https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.2
|
||||
&CONNECTION | &TRANSFER_ENCODING | &UPGRADE => continue,
|
||||
|
||||
&CONTENT_LENGTH if skip_len => continue,
|
||||
&DATE => has_date = true,
|
||||
|
||||
// omit HTTP/1.x only headers according to:
|
||||
// https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.2
|
||||
hdr if hdr == HeaderName::from_static("keep-alive")
|
||||
|| hdr == HeaderName::from_static("proxy-connection") =>
|
||||
{
|
||||
continue
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
@@ -13,7 +13,8 @@ use crate::error::PayloadError;
|
||||
/// A boxed payload stream.
|
||||
pub type BoxedPayloadStream = Pin<Box<dyn Stream<Item = Result<Bytes, PayloadError>>>>;
|
||||
|
||||
#[deprecated(since = "4.0.0", note = "Renamed to `BoxedPayloadStream`.")]
|
||||
#[doc(hidden)]
|
||||
#[deprecated(since = "3.0.0", note = "Renamed to `BoxedPayloadStream`.")]
|
||||
pub type PayloadStream = BoxedPayloadStream;
|
||||
|
||||
#[cfg(not(feature = "http2"))]
|
||||
|
@@ -144,7 +144,7 @@ impl ResponseBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set connection type to Upgrade
|
||||
/// Set connection type to `Upgrade`.
|
||||
#[inline]
|
||||
pub fn upgrade<V>(&mut self, value: V) -> &mut Self
|
||||
where
|
||||
@@ -161,7 +161,7 @@ impl ResponseBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Force close connection, even if it is marked as keep-alive
|
||||
/// Force-close connection, even if it is marked as keep-alive.
|
||||
#[inline]
|
||||
pub fn force_close(&mut self) -> &mut Self {
|
||||
if let Some(parts) = self.inner() {
|
||||
|
@@ -47,40 +47,6 @@ pub fn apply_mask_fast32(buf: &mut [u8], mask: [u8; 4]) {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// legacy test from old apply mask test. kept for now for back compat test.
|
||||
// TODO: remove it and favor the other test.
|
||||
#[test]
|
||||
fn test_apply_mask_legacy() {
|
||||
let mask = [0x6d, 0xb6, 0xb2, 0x80];
|
||||
|
||||
let unmasked = vec![
|
||||
0xf3, 0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x82, 0xff, 0xfe, 0x00, 0x17, 0x74, 0xf9,
|
||||
0x12, 0x03,
|
||||
];
|
||||
|
||||
// Check masking with proper alignment.
|
||||
{
|
||||
let mut masked = unmasked.clone();
|
||||
apply_mask_fallback(&mut masked, mask);
|
||||
|
||||
let mut masked_fast = unmasked.clone();
|
||||
apply_mask(&mut masked_fast, mask);
|
||||
|
||||
assert_eq!(masked, masked_fast);
|
||||
}
|
||||
|
||||
// Check masking without alignment.
|
||||
{
|
||||
let mut masked = unmasked.clone();
|
||||
apply_mask_fallback(&mut masked[1..], mask);
|
||||
|
||||
let mut masked_fast = unmasked;
|
||||
apply_mask(&mut masked_fast[1..], mask);
|
||||
|
||||
assert_eq!(masked, masked_fast);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_mask() {
|
||||
let mask = [0x6d, 0xb6, 0xb2, 0x80];
|
||||
|
@@ -850,7 +850,8 @@ async fn not_modified_spec_h1() {
|
||||
Some(&header::HeaderValue::from_static("4")),
|
||||
);
|
||||
// server does not prevent payload from being sent but clients may choose not to read it
|
||||
// TODO: this is probably a bug, especially since CL header can differ in length from the body
|
||||
// TODO: this is probably a bug in the client, especially since CL header can differ in length
|
||||
// from the body
|
||||
assert!(!srv.load_body(res).await.unwrap().is_empty());
|
||||
|
||||
// TODO: add stream response tests
|
||||
|
@@ -3,6 +3,10 @@
|
||||
## Unreleased - 2021-xx-xx
|
||||
|
||||
|
||||
## 0.4.0 - 2022-02-25
|
||||
- No significant changes since `0.4.0-beta.13`.
|
||||
|
||||
|
||||
## 0.4.0-beta.13 - 2022-01-31
|
||||
- No significant changes since `0.4.0-beta.12`.
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "actix-multipart"
|
||||
version = "0.4.0-beta.13"
|
||||
version = "0.4.0"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
description = "Multipart form support for Actix Web"
|
||||
keywords = ["http", "web", "framework", "async", "futures"]
|
||||
@@ -14,8 +14,8 @@ name = "actix_multipart"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
actix-utils = "3.0.0"
|
||||
actix-web = { version = "4.0.0-rc.3", default-features = false }
|
||||
actix-utils = "3"
|
||||
actix-web = { version = "4.0.0", default-features = false }
|
||||
|
||||
bytes = "1"
|
||||
derive_more = "0.99.5"
|
||||
@@ -28,7 +28,7 @@ twoway = "0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
actix-rt = "2.2"
|
||||
actix-http = "3.0.0-rc.4"
|
||||
actix-http = "3.0.0"
|
||||
futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
||||
tokio = { version = "1.8.4", features = ["sync"] }
|
||||
tokio-stream = "0.1"
|
||||
|
@@ -3,11 +3,11 @@
|
||||
> Multipart form support for Actix Web.
|
||||
|
||||
[](https://crates.io/crates/actix-multipart)
|
||||
[](https://docs.rs/actix-multipart/0.4.0-beta.13)
|
||||
[](https://docs.rs/actix-multipart/0.4.0)
|
||||
[](https://blog.rust-lang.org/2021/05/06/Rust-1.54.0.html)
|
||||

|
||||
<br />
|
||||
[](https://deps.rs/crate/actix-multipart/0.4.0-beta.13)
|
||||
[](https://deps.rs/crate/actix-multipart/0.4.0)
|
||||
[](https://crates.io/crates/actix-multipart)
|
||||
[](https://discord.gg/NWpN5mmg3x)
|
||||
|
||||
|
@@ -3,6 +3,77 @@
|
||||
## Unreleased - 2021-xx-xx
|
||||
|
||||
|
||||
## 0.5.0 - 2022-02-22
|
||||
### Added
|
||||
- Add `Path::as_str`. [#2590]
|
||||
- Add `ResourceDef::set_name`. [#373][net#373]
|
||||
- Add `RouterBuilder::push`. [#2612]
|
||||
- Implement `IntoPatterns` for `bytestring::ByteString`. [#372][net#372]
|
||||
- Introduce `ResourceDef::join`. [#380][net#380]
|
||||
- Introduce `ResourceDef::pattern_iter` to get an iterator over all patterns in a multi-pattern resource. [#373][net#373]
|
||||
- `Resource` is now implemented for `&mut Path<_>` and `RefMut<Path<_>>`. [#2568]
|
||||
- Support `build_resource_path` on multi-pattern resources. [#2356]
|
||||
- Support multi-pattern prefixes and joins. [#2356]
|
||||
|
||||
### Changed
|
||||
- Change signature of `ResourceDef::capture_match_info_fn` to remove `user_data` parameter. [#2612]
|
||||
- Deprecate `Path::path`. [#2590]
|
||||
- Disallow prefix routes with tail segments. [#379][net#379]
|
||||
- Enforce path separators on dynamic prefixes. [#378][net#378]
|
||||
- Minimum supported Rust version (MSRV) is now 1.54.
|
||||
- Prefix segments now always end with with a segment delimiter or end-of-input. [#2355]
|
||||
- Prefix segments with trailing slashes define a trailing empty segment. [#2355]
|
||||
- `Quoter::requote` now returns `Option<Vec<u8>>`. [#2613]
|
||||
- Re-work `IntoPatterns` trait, adding a `Patterns` enum. [#372][net#372]
|
||||
- Rename `Path::{len => segment_count}` to be more descriptive of its purpose. [#370][net#370]
|
||||
- Rename `ResourceDef::{is_prefix_match => find_match}`. [#373][net#373]
|
||||
- Rename `ResourceDef::{match_path => capture_match_info}`. [#373][net#373]
|
||||
- Rename `ResourceDef::{match_path_checked => capture_match_info_fn}`. [#373][net#373]
|
||||
- Rename `ResourceDef::{resource_path => resource_path_from_iter}`. [#371][net#371]
|
||||
- Rename `ResourceDef::{resource_path_named => resource_path_from_map}`. [#371][net#371]
|
||||
- Rename `Router::{*_checked => *_fn}`. [#373][net#373]
|
||||
- Replace `Option<U>` with `U` in `Router` API. [#2612]
|
||||
- `Resource` trait now uses an associated type, `Path`, instead of a generic parameter. [#2568]
|
||||
- `ResourceDef::pattern` now returns the first pattern in multi-pattern resources. [#2356]
|
||||
- `ResourceDef::resource_path_from_iter` now takes an `IntoIterator`. [#373][net#373]
|
||||
- Return type of `ResourceDef::name` is now `Option<&str>`. [#373][net#373]
|
||||
- Return type of `ResourceDef::pattern` is now `Option<&str>`. [#373][net#373]
|
||||
|
||||
### Fixed
|
||||
- Fix `ResourceDef`'s `PartialEq` implementation. [#373][net#373]
|
||||
- Fix segment interpolation leaving `Path` in unintended state after matching. [#368][net#368]
|
||||
- Improve malformed path error message. [#384][net#384]
|
||||
- `PathDeserializer` now decodes all percent encoded characters in dynamic segments. [#2566]
|
||||
- Relax bounds on `Router::recognize*` and `ResourceDef::capture_match_info`. [#2612]
|
||||
- Static patterns in multi-patterns are no longer interpreted as regex. [#366][net#366]
|
||||
|
||||
### Removed
|
||||
- `ResourceDef::name_mut`. [#373][net#373]
|
||||
- Unused `ResourceInfo`. [#2612]
|
||||
|
||||
[#2355]: https://github.com/actix/actix-web/pull/2355
|
||||
[#2356]: https://github.com/actix/actix-web/pull/2356
|
||||
[#2566]: https://github.com/actix/actix-net/pull/2566
|
||||
[#2568]: https://github.com/actix/actix-web/pull/2568
|
||||
[#2590]: https://github.com/actix/actix-web/pull/2590
|
||||
[#2612]: https://github.com/actix/actix-web/pull/2612
|
||||
[#2613]: https://github.com/actix/actix-web/pull/2613
|
||||
[net#366]: https://github.com/actix/actix-net/pull/366
|
||||
[net#368]: https://github.com/actix/actix-net/pull/368
|
||||
[net#368]: https://github.com/actix/actix-net/pull/368
|
||||
[net#370]: https://github.com/actix/actix-net/pull/370
|
||||
[net#371]: https://github.com/actix/actix-net/pull/371
|
||||
[net#372]: https://github.com/actix/actix-net/pull/372
|
||||
[net#373]: https://github.com/actix/actix-net/pull/373
|
||||
[net#378]: https://github.com/actix/actix-net/pull/378
|
||||
[net#379]: https://github.com/actix/actix-net/pull/379
|
||||
[net#380]: https://github.com/actix/actix-net/pull/380
|
||||
[net#384]: https://github.com/actix/actix-net/pull/384
|
||||
|
||||
|
||||
<details>
|
||||
<summary>0.5.0 Pre-Releases</summary>
|
||||
|
||||
## 0.5.0-rc.3 - 2022-01-31
|
||||
- Remove unused `ResourceInfo`. [#2612]
|
||||
- Add `RouterBuilder::push`. [#2612]
|
||||
@@ -41,10 +112,10 @@
|
||||
|
||||
|
||||
## 0.5.0-beta.2 - 2021-09-09
|
||||
- Introduce `ResourceDef::join`. [#380]
|
||||
- Disallow prefix routes with tail segments. [#379]
|
||||
- Enforce path separators on dynamic prefixes. [#378]
|
||||
- Improve malformed path error message. [#384]
|
||||
- Introduce `ResourceDef::join`. [#380][net#380]
|
||||
- Disallow prefix routes with tail segments. [#379][net#379]
|
||||
- Enforce path separators on dynamic prefixes. [#378][net#378]
|
||||
- Improve malformed path error message. [#384][net#384]
|
||||
- Prefix segments now always end with with a segment delimiter or end-of-input. [#2355]
|
||||
- Prefix segments with trailing slashes define a trailing empty segment. [#2355]
|
||||
- Support multi-pattern prefixes and joins. [#2356]
|
||||
@@ -52,52 +123,54 @@
|
||||
- Support `build_resource_path` on multi-pattern resources. [#2356]
|
||||
- Minimum supported Rust version (MSRV) is now 1.51.
|
||||
|
||||
[#378]: https://github.com/actix/actix-net/pull/378
|
||||
[#379]: https://github.com/actix/actix-net/pull/379
|
||||
[#380]: https://github.com/actix/actix-net/pull/380
|
||||
[#384]: https://github.com/actix/actix-net/pull/384
|
||||
[net#378]: https://github.com/actix/actix-net/pull/378
|
||||
[net#379]: https://github.com/actix/actix-net/pull/379
|
||||
[net#380]: https://github.com/actix/actix-net/pull/380
|
||||
[net#384]: https://github.com/actix/actix-net/pull/384
|
||||
[#2355]: https://github.com/actix/actix-web/pull/2355
|
||||
[#2356]: https://github.com/actix/actix-web/pull/2356
|
||||
|
||||
|
||||
## 0.5.0-beta.1 - 2021-07-20
|
||||
- Fix a bug in multi-patterns where static patterns are interpreted as regex. [#366]
|
||||
- Introduce `ResourceDef::pattern_iter` to get an iterator over all patterns in a multi-pattern resource. [#373]
|
||||
- Fix segment interpolation leaving `Path` in unintended state after matching. [#368]
|
||||
- Fix `ResourceDef` `PartialEq` implementation. [#373]
|
||||
- Re-work `IntoPatterns` trait, adding a `Patterns` enum. [#372]
|
||||
- Implement `IntoPatterns` for `bytestring::ByteString`. [#372]
|
||||
- Rename `Path::{len => segment_count}` to be more descriptive of it's purpose. [#370]
|
||||
- Rename `ResourceDef::{resource_path => resource_path_from_iter}`. [#371]
|
||||
- `ResourceDef::resource_path_from_iter` now takes an `IntoIterator`. [#373]
|
||||
- Rename `ResourceDef::{resource_path_named => resource_path_from_map}`. [#371]
|
||||
- Rename `ResourceDef::{is_prefix_match => find_match}`. [#373]
|
||||
- Rename `ResourceDef::{match_path => capture_match_info}`. [#373]
|
||||
- Rename `ResourceDef::{match_path_checked => capture_match_info_fn}`. [#373]
|
||||
- Remove `ResourceDef::name_mut` and introduce `ResourceDef::set_name`. [#373]
|
||||
- Rename `Router::{*_checked => *_fn}`. [#373]
|
||||
- Return type of `ResourceDef::name` is now `Option<&str>`. [#373]
|
||||
- Return type of `ResourceDef::pattern` is now `Option<&str>`. [#373]
|
||||
- Fix a bug in multi-patterns where static patterns are interpreted as regex. [#366][net#366]
|
||||
- Introduce `ResourceDef::pattern_iter` to get an iterator over all patterns in a multi-pattern resource. [#373][net#373]
|
||||
- Fix segment interpolation leaving `Path` in unintended state after matching. [#368][net#368]
|
||||
- Fix `ResourceDef` `PartialEq` implementation. [#373][net#373]
|
||||
- Re-work `IntoPatterns` trait, adding a `Patterns` enum. [#372][net#372]
|
||||
- Implement `IntoPatterns` for `bytestring::ByteString`. [#372][net#372]
|
||||
- Rename `Path::{len => segment_count}` to be more descriptive of it's purpose. [#370][net#370]
|
||||
- Rename `ResourceDef::{resource_path => resource_path_from_iter}`. [#371][net#371]
|
||||
- `ResourceDef::resource_path_from_iter` now takes an `IntoIterator`. [#373][net#373]
|
||||
- Rename `ResourceDef::{resource_path_named => resource_path_from_map}`. [#371][net#371]
|
||||
- Rename `ResourceDef::{is_prefix_match => find_match}`. [#373][net#373]
|
||||
- Rename `ResourceDef::{match_path => capture_match_info}`. [#373][net#373]
|
||||
- Rename `ResourceDef::{match_path_checked => capture_match_info_fn}`. [#373][net#373]
|
||||
- Remove `ResourceDef::name_mut` and introduce `ResourceDef::set_name`. [#373][net#373]
|
||||
- Rename `Router::{*_checked => *_fn}`. [#373][net#373]
|
||||
- Return type of `ResourceDef::name` is now `Option<&str>`. [#373][net#373]
|
||||
- Return type of `ResourceDef::pattern` is now `Option<&str>`. [#373][net#373]
|
||||
|
||||
[#368]: https://github.com/actix/actix-net/pull/368
|
||||
[#366]: https://github.com/actix/actix-net/pull/366
|
||||
[#368]: https://github.com/actix/actix-net/pull/368
|
||||
[#370]: https://github.com/actix/actix-net/pull/370
|
||||
[#371]: https://github.com/actix/actix-net/pull/371
|
||||
[#372]: https://github.com/actix/actix-net/pull/372
|
||||
[#373]: https://github.com/actix/actix-net/pull/373
|
||||
[net#368]: https://github.com/actix/actix-net/pull/368
|
||||
[net#366]: https://github.com/actix/actix-net/pull/366
|
||||
[net#368]: https://github.com/actix/actix-net/pull/368
|
||||
[net#370]: https://github.com/actix/actix-net/pull/370
|
||||
[net#371]: https://github.com/actix/actix-net/pull/371
|
||||
[net#372]: https://github.com/actix/actix-net/pull/372
|
||||
[net#373]: https://github.com/actix/actix-net/pull/373
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
## 0.4.0 - 2021-06-06
|
||||
- When matching path parameters, `%25` is now kept in the percent-encoded form; no longer decoded to `%`. [#357]
|
||||
- Path tail patterns now match new lines (`\n`) in request URL. [#360]
|
||||
- Fixed a safety bug where `Path` could return a malformed string after percent decoding. [#359]
|
||||
- Methods `Path::{add, add_static}` now take `impl Into<Cow<'static, str>>`. [#345]
|
||||
- When matching path parameters, `%25` is now kept in the percent-encoded form; no longer decoded to `%`. [#357][net#357]
|
||||
- Path tail patterns now match new lines (`\n`) in request URL. [#360][net#360]
|
||||
- Fixed a safety bug where `Path` could return a malformed string after percent decoding. [#359][net#359]
|
||||
- Methods `Path::{add, add_static}` now take `impl Into<Cow<'static, str>>`. [#345][net#345]
|
||||
|
||||
[#345]: https://github.com/actix/actix-net/pull/345
|
||||
[#357]: https://github.com/actix/actix-net/pull/357
|
||||
[#359]: https://github.com/actix/actix-net/pull/359
|
||||
[#360]: https://github.com/actix/actix-net/pull/360
|
||||
[net#345]: https://github.com/actix/actix-net/pull/345
|
||||
[net#357]: https://github.com/actix/actix-net/pull/357
|
||||
[net#359]: https://github.com/actix/actix-net/pull/359
|
||||
[net#360]: https://github.com/actix/actix-net/pull/360
|
||||
|
||||
|
||||
## 0.3.0 - 2019-12-31
|
||||
@@ -105,15 +178,15 @@
|
||||
|
||||
|
||||
## 0.2.7 - 2021-02-06
|
||||
- Add `Router::recognize_checked` [#247]
|
||||
- Add `Router::recognize_checked` [#247][net#247]
|
||||
|
||||
[#247]: https://github.com/actix/actix-net/pull/247
|
||||
[net#247]: https://github.com/actix/actix-net/pull/247
|
||||
|
||||
|
||||
## 0.2.6 - 2021-01-09
|
||||
- Use `bytestring` version range compatible with Bytes v1.0. [#246]
|
||||
- Use `bytestring` version range compatible with Bytes v1.0. [#246][net#246]
|
||||
|
||||
[#246]: https://github.com/actix/actix-net/pull/246
|
||||
[net#246]: https://github.com/actix/actix-net/pull/246
|
||||
|
||||
|
||||
## 0.2.5 - 2020-09-20
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "actix-router"
|
||||
version = "0.5.0-rc.3"
|
||||
version = "0.5.0"
|
||||
authors = [
|
||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||
"Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com>",
|
||||
|
@@ -29,13 +29,13 @@ openssl = ["tls-openssl", "actix-http/openssl", "awc/openssl"]
|
||||
|
||||
[dependencies]
|
||||
actix-codec = "0.5"
|
||||
actix-http = "3.0.0-rc.4"
|
||||
actix-http = "3"
|
||||
actix-http-test = "3.0.0-beta.13"
|
||||
actix-rt = "2.1"
|
||||
actix-service = "2.0.0"
|
||||
actix-utils = "3.0.0"
|
||||
actix-web = { version = "4.0.0-rc.3", default-features = false, features = ["cookies"] }
|
||||
awc = { version = "3.0.0-beta.21", default-features = false, features = ["cookies"] }
|
||||
actix-service = "2"
|
||||
actix-utils = "3"
|
||||
actix-web = { version = "4", default-features = false, features = ["cookies"] }
|
||||
awc = { version = "3", default-features = false, features = ["cookies"] }
|
||||
|
||||
futures-core = { version = "0.3.7", default-features = false, features = ["std"] }
|
||||
futures-util = { version = "0.3.7", default-features = false, features = [] }
|
||||
|
@@ -43,7 +43,7 @@ pub use actix_http_test::unused_addr;
|
||||
use actix_service::{map_config, IntoServiceFactory, ServiceFactory, ServiceFactoryExt as _};
|
||||
pub use actix_web::test::{
|
||||
call_and_read_body, call_and_read_body_json, call_service, init_service, ok_service,
|
||||
read_body, read_body_json, simple_service, TestRequest,
|
||||
read_body, read_body_json, status_service, TestRequest,
|
||||
};
|
||||
use actix_web::{
|
||||
body::MessageBody,
|
||||
|
@@ -3,6 +3,16 @@
|
||||
## Unreleased - 2021-xx-xx
|
||||
|
||||
|
||||
## 4.1.0 - 2022-03-02
|
||||
- Add support for `actix` version `0.13`. [#2675]
|
||||
|
||||
[#2675]: https://github.com/actix/actix-web/pull/2675
|
||||
|
||||
|
||||
## 4.0.0 - 2022-02-25
|
||||
- No significant changes since `4.0.0-beta.12`.
|
||||
|
||||
|
||||
## 4.0.0-beta.12 - 2022-02-16
|
||||
- No significant changes since `4.0.0-beta.11`.
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "actix-web-actors"
|
||||
version = "4.0.0-beta.12"
|
||||
version = "4.1.0"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
description = "Actix actors support for Actix Web"
|
||||
keywords = ["actix", "http", "web", "framework", "async"]
|
||||
@@ -14,21 +14,21 @@ name = "actix_web_actors"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
actix = { version = "0.12.0", default-features = false }
|
||||
actix = { version = ">=0.12, <0.14", default-features = false }
|
||||
actix-codec = "0.5"
|
||||
actix-http = "3.0.0-rc.4"
|
||||
actix-web = { version = "4.0.0-rc.3", default-features = false }
|
||||
actix-http = "3"
|
||||
actix-web = { version = "4", default-features = false }
|
||||
|
||||
bytes = "1"
|
||||
bytestring = "1"
|
||||
futures-core = { version = "0.3.7", default-features = false }
|
||||
pin-project-lite = "0.2"
|
||||
tokio = { version = "1.8.4", features = ["sync"] }
|
||||
tokio = { version = "1.13.1", features = ["sync"] }
|
||||
|
||||
[dev-dependencies]
|
||||
actix-rt = "2.2"
|
||||
actix-test = "0.1.0-beta.13"
|
||||
awc = { version = "3.0.0-beta.21", default-features = false }
|
||||
awc = { version = "3", default-features = false }
|
||||
|
||||
env_logger = "0.9"
|
||||
futures-util = { version = "0.3.7", default-features = false }
|
||||
|
@@ -3,11 +3,11 @@
|
||||
> Actix actors support for Actix Web.
|
||||
|
||||
[](https://crates.io/crates/actix-web-actors)
|
||||
[](https://docs.rs/actix-web-actors/4.0.0-beta.12)
|
||||
[](https://docs.rs/actix-web-actors/4.1.0)
|
||||
[](https://blog.rust-lang.org/2021/05/06/Rust-1.54.0.html)
|
||||

|
||||
<br />
|
||||
[](https://deps.rs/crate/actix-web-actors/4.0.0-beta.12)
|
||||
[](https://deps.rs/crate/actix-web-actors/4.1.0)
|
||||
[](https://crates.io/crates/actix-web-actors)
|
||||
[](https://discord.gg/NWpN5mmg3x)
|
||||
|
||||
|
@@ -3,6 +3,15 @@
|
||||
## Unreleased - 2021-xx-xx
|
||||
|
||||
|
||||
## 4.0.0 - 2022-02-24
|
||||
- Version aligned with `actix-web` and will remain in sync going forward.
|
||||
- No significant changes since `0.5.0`.
|
||||
|
||||
|
||||
## 0.5.0 - 2022-02-24
|
||||
- No significant changes since `0.5.0-rc.2`.
|
||||
|
||||
|
||||
## 0.5.0-rc.2 - 2022-02-01
|
||||
- No significant changes since `0.5.0-rc.1`.
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "actix-web-codegen"
|
||||
version = "0.5.0-rc.2"
|
||||
version = "4.0.0"
|
||||
description = "Routing and runtime macros for Actix Web"
|
||||
homepage = "https://actix.rs"
|
||||
repository = "https://github.com/actix/actix-web.git"
|
||||
@@ -15,7 +15,7 @@ edition = "2018"
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
actix-router = "0.5.0-rc.3"
|
||||
actix-router = "0.5.0"
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "1", features = ["full", "parsing"] }
|
||||
@@ -25,7 +25,7 @@ actix-macros = "0.2.3"
|
||||
actix-rt = "2.2"
|
||||
actix-test = "0.1.0-beta.13"
|
||||
actix-utils = "3.0.0"
|
||||
actix-web = "4.0.0-rc.3"
|
||||
actix-web = "4.0.0"
|
||||
|
||||
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
||||
trybuild = "1"
|
||||
|
@@ -3,11 +3,11 @@
|
||||
> Routing and runtime macros for Actix Web.
|
||||
|
||||
[](https://crates.io/crates/actix-web-codegen)
|
||||
[](https://docs.rs/actix-web-codegen/0.5.0-rc.2)
|
||||
[](https://docs.rs/actix-web-codegen/4.0.0)
|
||||
[](https://blog.rust-lang.org/2021/05/06/Rust-1.54.0.html)
|
||||

|
||||
<br />
|
||||
[](https://deps.rs/crate/actix-web-codegen/0.5.0-rc.2)
|
||||
[](https://deps.rs/crate/actix-web-codegen/4.0.0)
|
||||
[](https://crates.io/crates/actix-web-codegen)
|
||||
[](https://discord.gg/NWpN5mmg3x)
|
||||
|
||||
|
@@ -1,8 +1,285 @@
|
||||
# Changes
|
||||
# Changelog
|
||||
|
||||
## Unreleased - 2021-xx-xx
|
||||
|
||||
|
||||
## 4.0.1 - 2022-02-25
|
||||
### Fixed
|
||||
- Use stable version in readme example.
|
||||
|
||||
|
||||
## 4.0.0 - 2022-02-25
|
||||
### Dependencies
|
||||
- Updated `actix-*` to Tokio v1-based versions. [#1813]
|
||||
- Updated `actix-web-codegen` to `4.0.0`.
|
||||
- Updated `cookie` to `0.16`. [#2555]
|
||||
- Updated `language-tags` to `0.3`.
|
||||
- Updated `rand` to `0.8`.
|
||||
- Updated `rustls` to `0.20`. [#2414]
|
||||
- Updated `tokio` to `1`.
|
||||
|
||||
### Added
|
||||
- Crate Features:
|
||||
- `cookies`; enabled by default. [#2619]
|
||||
- `compress-brotli`; enabled by default. [#2618]
|
||||
- `compress-gzip`; enabled by default. [#2618]
|
||||
- `compress-zstd`; enabled by default. [#2618]
|
||||
- `macros`; enables routing and runtime macros, enabled by default. [#2619]
|
||||
- Types:
|
||||
- `CustomizeResponder` for customizing response. [#2510]
|
||||
- `dev::ServerHandle` re-export from `actix-server`. [#2442]
|
||||
- `dev::ServiceFactory` re-export from `actix-service`. [#2325]
|
||||
- `guard::GuardContext` for use with the `Guard` trait. [#2552]
|
||||
- `http::header::AcceptEncoding` typed header. [#2482]
|
||||
- `http::header::Range` typed header. [#2485]
|
||||
- `http::KeepAlive` re-export from `actix-http`. [#2625]
|
||||
- `middleware::Compat` that boxes middleware types like `Logger` and `Compress` to be used with constrained type bounds. [#1865]
|
||||
- `web::Header` extractor for extracting typed HTTP headers in handlers. [#2094]
|
||||
- Methods:
|
||||
- `dev::ServiceRequest::guard_ctx()` for obtaining a guard context. [#2552]
|
||||
- `dev::ServiceRequest::parts_mut()`. [#2177]
|
||||
- `dev::ServiceResponse::map_into_{left,right}_body()` and `HttpResponse::map_into_boxed_body()`. [#2468]
|
||||
- `Either<web::Json<T>, web::Form<T>>::into_inner()` which returns the inner type for whichever variant was created. Also works for `Either<web::Form<T>, web::Json<T>>`. [#1894]
|
||||
- `http::header::AcceptLanguage::{ranked, preference}()`. [#2480]
|
||||
- `HttpResponse::add_removal_cookie()`. [#2586]
|
||||
- `HttpResponse::map_into_{left,right}_body()` and `HttpResponse::map_into_boxed_body()`. [#2468]
|
||||
- `HttpServer::worker_max_blocking_threads` for setting block thread pool. [#2200]
|
||||
- `middleware::Logger::log_target()` to allow customize. [#2594]
|
||||
- `Responder::customize()` trait method that wraps responder in `CustomizeResponder`. [#2510]
|
||||
- `Route::service()` for using hand-written services as handlers. [#2262]
|
||||
- `ServiceResponse::into_parts()`. [#2499]
|
||||
- `TestServer::client_headers()` method. [#2097]
|
||||
- `web::ServiceConfig::configure()` to allow easy nesting of configuration functions. [#1988]
|
||||
- Trait Implementations:
|
||||
- Implement `Debug` for `DefaultHeaders`. [#2510]
|
||||
- Implement `FromRequest` for `ConnectionInfo` and `PeerAddr`. [#2263]
|
||||
- Implement `FromRequest` for `Method`. [#2263]
|
||||
- Implement `FromRequest` for `Uri`. [#2263]
|
||||
- Implement `Hash` for `http::header::Encoding`. [#2501]
|
||||
- Implement `Responder` for `Vec<u8>`. [#2625]
|
||||
- Misc:
|
||||
- `#[actix_web::test]` macro for setting up tests with a runtime. [#2409]
|
||||
- Enable registering a vec of services of the same type to `App` [#1933]
|
||||
- Add `services!` macro for helping register multiple services to `App`. [#1933]
|
||||
- Option to allow `Json` extractor to work without a `Content-Type` header present. [#2362]
|
||||
- Connection data set through the `HttpServer::on_connect` callback is now accessible only from the new `HttpRequest::conn_data()` and `ServiceRequest::conn_data()` methods. [#2491]
|
||||
|
||||
### Changed
|
||||
- Functions:
|
||||
- `guard::fn_guard` functions now receives a `&GuardContext`. [#2552]
|
||||
- `guard::Not` is now generic over the type of guard it wraps. [#2552]
|
||||
- `test::{call_service, read_response, read_response_json, send_request}()` now receive a `&Service`. [#1905]
|
||||
- Some guard functions now return `impl Guard` and their concrete types are made private: `guard::Header` and all the method guards. [#2552]
|
||||
- Rename `test::{default_service => status_service}()`. Old name is deprecated. [#2518]
|
||||
- Rename `test::{read_response_json => call_and_read_body_json}()`. Old name is deprecated. [#2518]
|
||||
- Rename `test::{read_response => call_and_read_body}()`. Old name is deprecated. [#2518]
|
||||
- Traits:
|
||||
- `guard::Guard::check` now receives a `&GuardContext`. [#2552]
|
||||
- `FromRequest::Config` associated type was removed. [#2233]
|
||||
- `Responder` trait has been reworked and now `Response`/`HttpResponse` synchronously, making it simpler and more performant. [#1891]
|
||||
- Rename `Factory` trait to `Handler`. [#1852]
|
||||
- Types:
|
||||
- `App`'s `B` (body) type parameter been removed. As a result, `App`s can be returned from functions now. [#2493]
|
||||
- `Compress` middleware's response type is now `EitherBody<Encoder<B>>`. [#2448]
|
||||
- `error::BlockingError` is now a unit struct. It's now only triggered when blocking thread pool has shutdown. [#1957]
|
||||
- `ErrorHandlerResponse`'s response variants now use `ServiceResponse<EitherBody<B>>`. [#2515]
|
||||
- `ErrorHandlers` middleware's response types now use `ServiceResponse<EitherBody<B>>`. [#2515]
|
||||
- `http::header::Encoding` now only represents `Content-Encoding` types. [#2501]
|
||||
- `middleware::Condition` gained a broader middleware compatibility. [#2635]
|
||||
- `Resource` no longer require service body type to be boxed. [#2526]
|
||||
- `Scope` no longer require service body type to be boxed. [#2523]
|
||||
- `web::Path`s inner field is now private. [#1894]
|
||||
- `web::Payload`'s inner field is now private. [#2384]
|
||||
- Error enums are now marked `#[non_exhaustive]`. [#2148]
|
||||
- Enum Variants:
|
||||
- `Either` now uses `Left`/`Right` variants (instead of `A`/`B`) [#1894]
|
||||
- Include size and limits in `JsonPayloadError::Overflow`. [#2162]
|
||||
- Methods:
|
||||
- `App::data()` is deprecated; `App::app_data()` should be preferred. [#2271]
|
||||
- `dev::JsonBody::new()` returns a default limit of 32kB to be consistent with `JsonConfig` and the default behaviour of the `web::Json<T>` extractor. [#2010]
|
||||
- `dev::ServiceRequest::{into_parts, from_parts}()` can no longer fail. [#1893]
|
||||
- `dev::ServiceRequest::from_request` can no longer fail. [#1893]
|
||||
- `dev::ServiceResponse::error_response()` now uses body type of `BoxBody`. [#2201]
|
||||
- `dev::ServiceResponse::map_body()` closure receives and returns `B` instead of `ResponseBody<B>`. [#2201]
|
||||
- `http::header::ContentType::html()` now produces `text/html; charset=utf-8` instead of `text/html`. [#2423]
|
||||
- `HttpRequest::url_for`'s constructed URLs no longer contain query or fragment. [#2430]
|
||||
- `HttpResponseBuilder::json()` can now receive data by value and reference. [#1903]
|
||||
- `HttpServer::{listen_rustls, bind_rustls}()` now honor the ALPN protocols in the configuration parameter. [#2226]
|
||||
- `middleware::NormalizePath()` now will not try to normalize URIs with no valid path [#2246]
|
||||
- `test::TestRequest::param()` now accepts more than just static strings. [#2172]
|
||||
- `web::Data::into_inner()` and `Data::get_ref()` no longer require `T: Sized`. [#2403]
|
||||
- Rename `HttpServer::{client_timeout => client_request_timeout}()`. [#2611]
|
||||
- Rename `HttpServer::{client_shutdown => client_disconnect_timeout}()`. [#2611]
|
||||
- Rename `http::header::Accept::{mime_precedence => ranked}()`. [#2480]
|
||||
- Rename `http::header::Accept::{mime_preference => preference}()`. [#2480]
|
||||
- Rename `middleware::DefaultHeaders::{content_type => add_content_type}()`. [#1875]
|
||||
- Rename `dev::ConnectionInfo::{remote_addr => peer_addr}`, deprecating the old name. [#2554]
|
||||
- Trait Implementations:
|
||||
- `HttpResponse` can now be used as a `Responder` with any body type. [#2567]
|
||||
- Misc:
|
||||
- Maximum number of handler extractors has increased to 12. [#2582]
|
||||
- The default `TrailingSlash` behavior is now `Trim`, in line with existing documentation. See migration guide for implications. [#1875]
|
||||
- `Result` extractor wrapper can now convert error types. [#2581]
|
||||
- Compress middleware will return `406 Not Acceptable` when no content encoding is acceptable to the client. [#2344]
|
||||
- Adjusted default JSON payload limit to 2MB (from 32kb). [#2162]
|
||||
- All error trait bounds in server service builders have changed from `Into<Error>` to `Into<Response<BoxBody>>`. [#2253]
|
||||
- All error trait bounds in message body and stream impls changed from `Into<Error>` to `Into<Box<dyn std::error::Error>>`. [#2253]
|
||||
- Improve spec compliance of `dev::ConnectionInfo` extractor. [#2282]
|
||||
- Associated types in `FromRequest` implementation for `Option` and `Result` have changed. [#2581]
|
||||
- Reduce the level from `error` to `debug` for the log line that is emitted when a `500 Internal Server Error` is built using `HttpResponse::from_error`. [#2201]
|
||||
- Minimum supported Rust version (MSRV) is now 1.54.
|
||||
|
||||
### Fixed
|
||||
- Auto-negotiation of content encoding is more fault-tolerant when using the `Compress` middleware. [#2501]
|
||||
- Scope and Resource middleware can access data items set on their own layer. [#2288]
|
||||
- Multiple calls to `App::data()` with the same type now keeps the latest call's data. [#1906]
|
||||
- Typed headers containing lists that require one or more items now enforce this minimum. [#2482]
|
||||
- `dev::ConnectionInfo::peer_addr` will no longer return the port number. [#2554]
|
||||
- `dev::ConnectionInfo::realip_remote_addr` will no longer return the port number if sourcing the IP from the peer's socket address. [#2554]
|
||||
- Accept wildcard `*` items in `AcceptLanguage`. [#2480]
|
||||
- Relax `Unpin` bound on `S` (stream) parameter of `HttpResponseBuilder::streaming`. [#2448]
|
||||
- Fix quality parse error in `http::header::AcceptEncoding` typed header. [#2344]
|
||||
- Double ampersand in `middleware::Logger` format is escaped correctly. [#2067]
|
||||
- Added the underlying parse error to `test::read_body_json`'s panic message. [#1812]
|
||||
|
||||
### Security
|
||||
- `cookie` upgrade addresses [`RUSTSEC-2020-0071`].
|
||||
|
||||
[`rustsec-2020-0071`]: https://rustsec.org/advisories/RUSTSEC-2020-0071.html
|
||||
|
||||
### Removed
|
||||
- Crate Features:
|
||||
- `compress` feature. [#2065]
|
||||
- Functions:
|
||||
- `test::load_stream` and `test::load_body`; replace usage with `body::to_bytes`. [#2518]
|
||||
- `test::start_with`; moved to new `actix-test` crate. [#2112]
|
||||
- `test::start`; moved to new `actix-test` crate. [#2112]
|
||||
- `test::unused_addr`; moved to new `actix-test` crate. [#2112]
|
||||
- Traits:
|
||||
- `BodyEncoding`; signalling content encoding is now only done via the `Content-Encoding` header. [#2565]
|
||||
- Types:
|
||||
- `dev::{BodySize, MessageBody, SizedStream}` re-exports; they are exposed through the `body` module. [#2468]
|
||||
- `EitherExtractError` direct export. [#2510]
|
||||
- `rt::{Arbiter, ArbiterHandle}` re-exports. [#2619]
|
||||
- `test::TestServer`; moved to new `actix-test` crate. [#2112]
|
||||
- `test::TestServerConfig`; moved to new `actix-test` crate. [#2112]
|
||||
- `web::HttpRequest` re-export. [#2663]
|
||||
- `web::HttpResponse` re-export. [#2663]
|
||||
- Methods:
|
||||
- `AppService::set_service_data`; for custom HTTP service factories adding application data, use the layered data model by calling `ServiceRequest::add_data_container` when handling requests instead. [#1906]
|
||||
- `dev::ConnectionInfo::get`. [#2487]
|
||||
- `dev::ServiceResponse::checked_expr`. [#2401]
|
||||
- `HttpRequestBuilder::del_cookie`. [#2591]
|
||||
- `HttpResponse::take_body` and old `HttpResponse::into_body` method that casted body type. [#2201]
|
||||
- `HttpResponseBuilder::json2()`. [#1903]
|
||||
- `middleware::Compress::new`; restricting compression algorithm is done through feature flags. [#2501]
|
||||
- `test::TestRequest::with_header()`; use `test::TestRequest::default().insert_header()`. [#1869]
|
||||
- Trait Implementations:
|
||||
- Implementation of `From<either::Either>` for `Either` crate. [#2516]
|
||||
- Implementation of `Future` for `HttpResponse`. [#2601]
|
||||
- Misc:
|
||||
- The `client` module was removed; use the `awc` crate directly. [871ca5e4]
|
||||
- `middleware::{normalize, err_handlers}` modules; all necessary middleware types are now exposed in the `middleware` module.
|
||||
|
||||
[#1812]: https://github.com/actix/actix-web/pull/1812
|
||||
[#1813]: https://github.com/actix/actix-web/pull/1813
|
||||
[#1852]: https://github.com/actix/actix-web/pull/1852
|
||||
[#1865]: https://github.com/actix/actix-web/pull/1865
|
||||
[#1869]: https://github.com/actix/actix-web/pull/1869
|
||||
[#1875]: https://github.com/actix/actix-web/pull/1875
|
||||
[#1878]: https://github.com/actix/actix-web/pull/1878
|
||||
[#1891]: https://github.com/actix/actix-web/pull/1891
|
||||
[#1893]: https://github.com/actix/actix-web/pull/1893
|
||||
[#1894]: https://github.com/actix/actix-web/pull/1894
|
||||
[#1903]: https://github.com/actix/actix-web/pull/1903
|
||||
[#1905]: https://github.com/actix/actix-web/pull/1905
|
||||
[#1906]: https://github.com/actix/actix-web/pull/1906
|
||||
[#1933]: https://github.com/actix/actix-web/pull/1933
|
||||
[#1957]: https://github.com/actix/actix-web/pull/1957
|
||||
[#1957]: https://github.com/actix/actix-web/pull/1957
|
||||
[#1981]: https://github.com/actix/actix-web/pull/1981
|
||||
[#1988]: https://github.com/actix/actix-web/pull/1988
|
||||
[#2010]: https://github.com/actix/actix-web/pull/2010
|
||||
[#2065]: https://github.com/actix/actix-web/pull/2065
|
||||
[#2067]: https://github.com/actix/actix-web/pull/2067
|
||||
[#2093]: https://github.com/actix/actix-web/pull/2093
|
||||
[#2094]: https://github.com/actix/actix-web/pull/2094
|
||||
[#2097]: https://github.com/actix/actix-web/pull/2097
|
||||
[#2112]: https://github.com/actix/actix-web/pull/2112
|
||||
[#2148]: https://github.com/actix/actix-web/pull/2148
|
||||
[#2162]: https://github.com/actix/actix-web/pull/2162
|
||||
[#2172]: https://github.com/actix/actix-web/pull/2172
|
||||
[#2177]: https://github.com/actix/actix-web/pull/2177
|
||||
[#2200]: https://github.com/actix/actix-web/pull/2200
|
||||
[#2201]: https://github.com/actix/actix-web/pull/2201
|
||||
[#2201]: https://github.com/actix/actix-web/pull/2201
|
||||
[#2233]: https://github.com/actix/actix-web/pull/2233
|
||||
[#2246]: https://github.com/actix/actix-web/pull/2246
|
||||
[#2250]: https://github.com/actix/actix-web/pull/2250
|
||||
[#2253]: https://github.com/actix/actix-web/pull/2253
|
||||
[#2262]: https://github.com/actix/actix-web/pull/2262
|
||||
[#2263]: https://github.com/actix/actix-web/pull/2263
|
||||
[#2271]: https://github.com/actix/actix-web/pull/2271
|
||||
[#2282]: https://github.com/actix/actix-web/pull/2282
|
||||
[#2288]: https://github.com/actix/actix-web/pull/2288
|
||||
[#2325]: https://github.com/actix/actix-web/pull/2325
|
||||
[#2344]: https://github.com/actix/actix-web/pull/2344
|
||||
[#2362]: https://github.com/actix/actix-web/pull/2362
|
||||
[#2379]: https://github.com/actix/actix-web/pull/2379
|
||||
[#2384]: https://github.com/actix/actix-web/pull/2384
|
||||
[#2401]: https://github.com/actix/actix-web/pull/2401
|
||||
[#2403]: https://github.com/actix/actix-web/pull/2403
|
||||
[#2409]: https://github.com/actix/actix-web/pull/2409
|
||||
[#2414]: https://github.com/actix/actix-web/pull/2414
|
||||
[#2423]: https://github.com/actix/actix-web/pull/2423
|
||||
[#2430]: https://github.com/actix/actix-web/pull/2430
|
||||
[#2442]: https://github.com/actix/actix-web/pull/2442
|
||||
[#2446]: https://github.com/actix/actix-web/pull/2446
|
||||
[#2448]: https://github.com/actix/actix-web/pull/2448
|
||||
[#2468]: https://github.com/actix/actix-web/pull/2468
|
||||
[#2474]: https://github.com/actix/actix-web/pull/2474
|
||||
[#2480]: https://github.com/actix/actix-web/pull/2480
|
||||
[#2482]: https://github.com/actix/actix-web/pull/2482
|
||||
[#2484]: https://github.com/actix/actix-web/pull/2484
|
||||
[#2485]: https://github.com/actix/actix-web/pull/2485
|
||||
[#2487]: https://github.com/actix/actix-web/pull/2487
|
||||
[#2491]: https://github.com/actix/actix-web/pull/2491
|
||||
[#2492]: https://github.com/actix/actix-web/pull/2492
|
||||
[#2493]: https://github.com/actix/actix-web/pull/2493
|
||||
[#2499]: https://github.com/actix/actix-web/pull/2499
|
||||
[#2501]: https://github.com/actix/actix-web/pull/2501
|
||||
[#2510]: https://github.com/actix/actix-web/pull/2510
|
||||
[#2515]: https://github.com/actix/actix-web/pull/2515
|
||||
[#2516]: https://github.com/actix/actix-web/pull/2516
|
||||
[#2518]: https://github.com/actix/actix-web/pull/2518
|
||||
[#2523]: https://github.com/actix/actix-web/pull/2523
|
||||
[#2526]: https://github.com/actix/actix-web/pull/2526
|
||||
[#2552]: https://github.com/actix/actix-web/pull/2552
|
||||
[#2554]: https://github.com/actix/actix-web/pull/2554
|
||||
[#2555]: https://github.com/actix/actix-web/pull/2555
|
||||
[#2565]: https://github.com/actix/actix-web/pull/2565
|
||||
[#2567]: https://github.com/actix/actix-web/pull/2567
|
||||
[#2569]: https://github.com/actix/actix-web/pull/2569
|
||||
[#2581]: https://github.com/actix/actix-web/pull/2581
|
||||
[#2582]: https://github.com/actix/actix-web/pull/2582
|
||||
[#2584]: https://github.com/actix/actix-web/pull/2584
|
||||
[#2585]: https://github.com/actix/actix-web/pull/2585
|
||||
[#2586]: https://github.com/actix/actix-web/pull/2586
|
||||
[#2591]: https://github.com/actix/actix-web/pull/2591
|
||||
[#2594]: https://github.com/actix/actix-web/pull/2594
|
||||
[#2601]: https://github.com/actix/actix-web/pull/2601
|
||||
[#2611]: https://github.com/actix/actix-web/pull/2611
|
||||
[#2619]: https://github.com/actix/actix-web/pull/2619
|
||||
[#2625]: https://github.com/actix/actix-web/pull/2625
|
||||
[#2635]: https://github.com/actix/actix-web/pull/2635
|
||||
[#2659]: https://github.com/actix/actix-web/pull/2659
|
||||
[#2663]: https://github.com/actix/actix-web/pull/2663
|
||||
[871ca5e4]: https://github.com/actix/actix-web/commit/871ca5e4ae2bdc22d1ea02701c2992fa8d04aed7
|
||||
|
||||
|
||||
<details>
|
||||
<summary>4.0.0 Pre-Releases</summary>
|
||||
|
||||
## 4.0.0-rc.3 - 2022-02-08
|
||||
### Changed
|
||||
- `middleware::Condition` gained a broader compatibility; `Compat` is needed in fewer cases. [#2635]
|
||||
@@ -449,6 +726,7 @@
|
||||
[#1875]: https://github.com/actix/actix-web/pull/1875
|
||||
[#1878]: https://github.com/actix/actix-web/pull/1878
|
||||
|
||||
</details>
|
||||
|
||||
## 3.3.3 - 2021-12-18
|
||||
### Changed
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "actix-web"
|
||||
version = "4.0.0-rc.3"
|
||||
version = "4.0.1"
|
||||
authors = [
|
||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||
"Rob Ede <robjtede@icloud.com>",
|
||||
@@ -71,12 +71,13 @@ actix-service = "2"
|
||||
actix-utils = "3"
|
||||
actix-tls = { version = "3", default-features = false, optional = true }
|
||||
|
||||
actix-http = { version = "3.0.0-rc.4", features = ["http2", "ws"] }
|
||||
actix-router = "0.5.0-rc.3"
|
||||
actix-web-codegen = { version = "0.5.0-rc.2", optional = true }
|
||||
actix-http = { version = "3", features = ["http2", "ws"] }
|
||||
actix-router = "0.5"
|
||||
actix-web-codegen = { version = "4", optional = true }
|
||||
|
||||
ahash = "0.7"
|
||||
bytes = "1"
|
||||
bytestring = "1"
|
||||
cfg-if = "1"
|
||||
cookie = { version = "0.16", features = ["percent-encode"], optional = true }
|
||||
derive_more = "0.99.5"
|
||||
@@ -89,7 +90,7 @@ once_cell = "1.5"
|
||||
log = "0.4"
|
||||
mime = "0.3"
|
||||
pin-project-lite = "0.2.7"
|
||||
regex = "1.4"
|
||||
regex = "1.5.5"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_urlencoded = "0.7"
|
||||
@@ -99,9 +100,9 @@ time = { version = "0.3", default-features = false, features = ["formatting"] }
|
||||
url = "2.1"
|
||||
|
||||
[dev-dependencies]
|
||||
actix-files = "0.6.0-beta.16"
|
||||
actix-files = "0.6"
|
||||
actix-test = { version = "0.1.0-beta.13", features = ["openssl", "rustls"] }
|
||||
awc = { version = "3.0.0-beta.21", features = ["openssl"] }
|
||||
awc = { version = "3", features = ["openssl"] }
|
||||
|
||||
brotli = "3.3.3"
|
||||
const-str = "0.3"
|
||||
|
@@ -3,8 +3,7 @@
|
||||
This guide walks you through the process of migrating from v3.x.y to v4.x.y.
|
||||
If you are migrating to v4.x.y from an older version of Actix Web (v2.x.y or earlier), check out the other historical migration notes in this folder.
|
||||
|
||||
This document is not designed to be exhaustive - it focuses on the most significant changes coming in v4.
|
||||
You can find an exhaustive changelog in [CHANGES.md](./CHANGES.md), complete of PR links. If you think that some of the changes that we omitted deserve to be called out in this document, please open an issue or submit a PR.
|
||||
This document is not designed to be exhaustive—it focuses on the most significant changes in v4. You can find an exhaustive changelog in the changelogs for [`actix-web`](./CHANGES.md#400---2022-02-25) and [`actix-http`](../actix-http/CHANGES.md#300---2022-02-25), complete with PR links. If you think there are any changes that deserve to be called out in this document, please open an issue or pull request.
|
||||
|
||||
Headings marked with :warning: are **breaking behavioral changes**. They will probably not surface as compile-time errors though automated tests _might_ detect their effects on your app.
|
||||
|
||||
@@ -30,7 +29,7 @@ Headings marked with :warning: are **breaking behavioral changes**. They will pr
|
||||
- [Server Must Be Polled :warning:](#server-must-be-polled-warning)
|
||||
- [Guards API](#guards-api)
|
||||
- [Returning `HttpResponse` synchronously](#returning-httpresponse-synchronously)
|
||||
- [`#[actix_web::main]` and `#[tokio::main]`](#actixwebmain-and-tokiomain)
|
||||
- [`#[actix_web::main]` and `#[tokio::main]`](#actix_webmain-and-tokiomain)
|
||||
- [`web::block`](#webblock)
|
||||
|
||||
## MSRV
|
||||
@@ -39,8 +38,9 @@ The MSRV of Actix Web has been raised from 1.42 to 1.54.
|
||||
|
||||
## Tokio v1 Ecosystem
|
||||
|
||||
Actix Web v4 is now underpinned by `tokio`'s v1 ecosystem.
|
||||
`cargo` supports having multiple versions of the same crate within the same dependency tree, but `tokio` v1 does not interoperate transparently with its previous versions (v0.2, v0.1). Some of your dependencies might rely on `tokio`, either directly or indirectly - if they are using an older version of `tokio`, check if an update is available.
|
||||
Actix Web v4 is now underpinned by `tokio`'s v1 ecosystem.
|
||||
|
||||
`cargo` supports having multiple versions of the same crate within the same dependency tree, but `tokio` v1 does not interoperate transparently with its previous versions (v0.2, v0.1). Some of your dependencies might rely on `tokio`, either directly or indirectly—if they are using an older version of `tokio`, check if an update is available.
|
||||
The following command can help you to identify these dependencies:
|
||||
|
||||
```sh
|
||||
@@ -59,8 +59,8 @@ Lots of modules have been re-organized in this release. If a compile error refer
|
||||
|
||||
## `NormalizePath` Middleware :warning:
|
||||
|
||||
The default `NormalizePath` behavior now strips trailing slashes by default.
|
||||
This was the _documented_ behaviour in Actix Web v3, but the _actual_ behaviour differed - the discrepancy has now been fixed.
|
||||
The default `NormalizePath` behavior now strips trailing slashes by default. This was the _documented_ behaviour in Actix Web v3, but the _actual_ behaviour differed. The discrepancy has now been resolved.
|
||||
|
||||
As a consequence of this change, routes defined with trailing slashes will become inaccessible when using `NormalizePath::default()`. Calling `NormalizePath::default()` will log a warning. We suggest to use `new` or `trim`.
|
||||
|
||||
```diff
|
||||
@@ -103,8 +103,7 @@ The `compress` feature flag has been split into more granular feature flags, one
|
||||
|
||||
## `web::Path`
|
||||
|
||||
The inner field for `web::Path` is now private.
|
||||
It was causing too many issues when used with inner tuple types due to its `Deref` implementation.
|
||||
The inner field for `web::Path` is now private. It was causing ambiguity when trying to use tuple indexing due to its `Deref` implementation.
|
||||
|
||||
```diff
|
||||
- async fn handler(web::Path((foo, bar)): web::Path<(String, String)>) {
|
||||
@@ -112,13 +111,15 @@ It was causing too many issues when used with inner tuple types due to its `Dere
|
||||
+ let (foo, bar) = params.into_inner();
|
||||
```
|
||||
|
||||
An alternative [path param type with public field but no `Deref` impl is available in `actix-web-lab`](https://docs.rs/actix-web-lab/0.12.0/actix_web_lab/extract/struct.Path.html).
|
||||
|
||||
## Rustls Crate Upgrade
|
||||
|
||||
Actix Web now depends on version 0.20 of `rustls`. As a result, the server config builder has changed. [See the updated example project.](https://github.com/actix/examples/tree/master/https-tls/rustls/)
|
||||
|
||||
## Removed `awc` Client Re-export
|
||||
|
||||
Actix Web's sister crate `awc` is no longer re-exported through the `client` module. This allows `awc` to have its own release cadence - its breaking changes are no longer blocked by Actix Web's (more conservative) release schedule.
|
||||
Actix Web's sister crate `awc` is no longer re-exported through the `client` module. This allows `awc` to have its own release cadence—its breaking changes are no longer blocked by Actix Web's (more conservative) release schedule.
|
||||
|
||||
```diff
|
||||
- use actix_web::client::Client;
|
||||
@@ -134,11 +135,11 @@ Actix Web's sister crate `awc` is no longer re-exported through the `client` mod
|
||||
+ use use actix_test::start;
|
||||
```
|
||||
|
||||
`TestServer` previously lived in `actix_web::test`, but it depends on `awc` which is no longer part of Actix Web's public API (see above).
|
||||
`TestServer` previously lived in `actix_web::test`, but it depends on `awc` which is no longer part of Actix Web's public API (see above).
|
||||
|
||||
## Header APIs
|
||||
|
||||
Header related APIs have been standardized across all `actix-*` crates. The terminology now better matches the underlying `HeaderMap` naming conventions.
|
||||
Header related APIs have been standardized across all `actix-*` crates. The terminology now better matches the underlying `HeaderMap` naming conventions.
|
||||
|
||||
In short, "insert" always indicates that any existing headers with the same name are overridden, while "append" is used for adding with no removal (e.g. multi-valued headers).
|
||||
|
||||
@@ -155,7 +156,7 @@ For request and response builder APIs, the new methods provide a unified interfa
|
||||
+ .insert_header(ContentType::json())
|
||||
```
|
||||
|
||||
We chose to deprecate most of the old methods instead of removing them immediately - the warning notes will guide you on how to update.
|
||||
We chose to deprecate most of the old methods instead of removing them immediately—the warning notes will guide you on how to update.
|
||||
|
||||
## Response Body Types
|
||||
|
||||
@@ -178,24 +179,159 @@ We have boosted the quality and completeness of the documentation for all items
|
||||
|
||||
### `BoxBody`
|
||||
|
||||
`BoxBody` is a new type-erased body type. It's used for all error response bodies.
|
||||
Creating a boxed body is best done by calling [`.boxed()`](https://docs.rs/actix-web/4/actix_web/body/trait.MessageBody.html#method.boxed) on a `MessageBody` type.
|
||||
`BoxBody` is a new type-erased body type.
|
||||
|
||||
It can be useful when writing handlers, responders, and middleware when you want to trade a (very) small amount of performance for a simpler type.
|
||||
|
||||
Creating a boxed body is done most efficiently by calling [`.boxed()`](https://docs.rs/actix-web/4/actix_web/body/trait.MessageBody.html#method.boxed) on a `MessageBody` type.
|
||||
|
||||
### `EitherBody`
|
||||
|
||||
`EitherBody` is a new "either" type that is particularly useful in middlewares that can bail early, returning their own response plus body type.
|
||||
`EitherBody` is a new "either" type that implements `MessageBody`
|
||||
|
||||
It is particularly useful in middleware that can bail early, returning their own response plus body type. By default the "right" variant is `BoxBody` (i.e., `EitherBody<B>` === `EitherBody<B, BoxBody>`) but it can be anything that implements `MessageBody`.
|
||||
|
||||
For example, it will be common among middleware which value performance of the hot path to use:
|
||||
|
||||
```rust
|
||||
type Response = Result<ServiceResponse<EitherBody<B>>, Error>
|
||||
```
|
||||
|
||||
This can be read (ignoring the `Result`) as "resolves with a `ServiceResponse` that is either the inner service's `B` body type or a boxed body type from elsewhere, likely constructed within the middleware itself". Of course, if your middleware contains only simple string other/error responses, it's possible to use them without boxes at the cost of a less simple implementation:
|
||||
|
||||
```rust
|
||||
type Response = Result<ServiceResponse<EitherBody<B, String>>, Error>
|
||||
```
|
||||
|
||||
### Error Handlers
|
||||
|
||||
TODO In particular, folks seem to be struggling with the `ErrorHandlers` middleware because of this change and the obscured nature of `EitherBody` within its types.
|
||||
`ErrorHandlers` is a commonly used middleware that has changed in design slightly due to the other body type changes.
|
||||
|
||||
In particular, an implicit `EitherBody` is used in the `ErrorHandlerResponse<B>` type. An `ErrorHandlerResponse<B>` now expects a `ServiceResponse<EitherBody<B>>` to be returned within response variants. The following is a migration for an error handler that **only modifies** the response argument (left body).
|
||||
|
||||
```diff
|
||||
fn add_error_header<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>, Error> {
|
||||
res.response_mut().headers_mut().insert(
|
||||
header::CONTENT_TYPE,
|
||||
header::HeaderValue::from_static("Error"),
|
||||
);
|
||||
- Ok(ErrorHandlerResponse::Response(res))
|
||||
+ Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))
|
||||
}
|
||||
```
|
||||
|
||||
The following is a migration for an error handler that creates a new response instead (right body).
|
||||
|
||||
```diff
|
||||
fn error_handler<B>(res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>, Error> {
|
||||
- let req = res.request().clone();
|
||||
+ let (req, _res) = res.into_parts();
|
||||
|
||||
let res = actix_files::NamedFile::open("./templates/404.html")?
|
||||
.set_status_code(StatusCode::NOT_FOUND)
|
||||
- .into_response(&req)?
|
||||
- .into_body();
|
||||
+ .into_response(&req);
|
||||
|
||||
- let res = ServiceResponse::new(req, res);
|
||||
+ let res = ServiceResponse::new(req, res).map_into_right_body();
|
||||
Ok(ErrorHandlerResponse::Response(res))
|
||||
}
|
||||
```
|
||||
|
||||
## Middleware Trait APIs
|
||||
|
||||
This section builds upon guidance from the [response body types](#response-body-types) section.
|
||||
The underlying traits that are used for creating middleware, `Service`, `ServiceFactory`, and `Transform`, have changed in design.
|
||||
|
||||
TODO
|
||||
- The associated `Request` type has moved to the type parameter position in order to allow multiple request implementations in other areas of the service stack.
|
||||
- The `self` arguments in `Service` have changed from exclusive (mutable) borrows to shared (immutable) borrows. Since most service layers, such as middleware, do not host mutable state, it reduces the runtime overhead in places where a `RefCell` used to be required for wrapping an inner service.
|
||||
- We've also introduced some macros that reduce boilerplate when implementing `poll_ready`.
|
||||
- Further to the guidance on [response body types](#response-body-types), any use of the old methods on `ServiceResponse` designed to match up body types (e.g., the old `into_body` method), should be replaced with an explicit response body type utilizing `EitherBody<B>`.
|
||||
|
||||
TODO: Also write the Middleware author's guide.
|
||||
A typical migration would look like this:
|
||||
|
||||
```diff
|
||||
use std::{
|
||||
- cell::RefCell,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
rc::Rc,
|
||||
- task::{Context, Poll},
|
||||
};
|
||||
|
||||
use actix_web::{
|
||||
dev::{Service, ServiceRequest, ServiceResponse, Transform},
|
||||
Error,
|
||||
};
|
||||
use futures_util::future::{ok, LocalBoxFuture, Ready};
|
||||
|
||||
pub struct SayHi;
|
||||
|
||||
- impl<S, B> Transform<S> for SayHi
|
||||
+ impl<S, B> Transform<S, ServiceRequest> for SayHi
|
||||
where
|
||||
- S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
||||
+ S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
||||
S::Future: 'static,
|
||||
B: 'static,
|
||||
{
|
||||
- type Request = ServiceRequest;
|
||||
type Response = ServiceResponse<B>;
|
||||
type Error = Error;
|
||||
type InitError = ();
|
||||
type Transform = SayHiMiddleware<S>;
|
||||
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||
|
||||
fn new_transform(&self, service: S) -> Self::Future {
|
||||
ok(SayHiMiddleware {
|
||||
- service: Rc::new(RefCell::new(service)),
|
||||
+ service: Rc::new(service),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SayHiMiddleware<S> {
|
||||
- service: Rc<RefCell<S>>,
|
||||
+ service: Rc<S>,
|
||||
}
|
||||
|
||||
- impl<S, B> Service for SayHiMiddleware<S>
|
||||
+ impl<S, B> Service<ServiceRequest> for SayHiMiddleware<S>
|
||||
where
|
||||
- S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
||||
+ S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
||||
S::Future: 'static,
|
||||
B: 'static,
|
||||
{
|
||||
- type Request = ServiceRequest;
|
||||
type Response = ServiceResponse<B>;
|
||||
type Error = Error;
|
||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||
|
||||
- fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
- self.service.poll_ready(cx)
|
||||
- }
|
||||
+ actix_web::dev::forward_ready!(service);
|
||||
|
||||
- fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
||||
+ fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||
println!("Hi from start. You requested: {}", req.path());
|
||||
|
||||
let fut = self.service.call(req);
|
||||
|
||||
Box::pin(async move {
|
||||
let res = fut.await?;
|
||||
|
||||
println!("Hi from response");
|
||||
Ok(res)
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This new design is forward-looking and should ease transition to traits that support the upcoming Generic Associated Type (GAT) feature in Rust while also trimming down the boilerplate required to implement middleware.
|
||||
|
||||
We understand that creating middleware is still a pain point for Actix Web and we hope to provide [an even more ergonomic solution](https://docs.rs/actix-web-lab/0.11.0/actix_web_lab/middleware/fn.from_fn.html) in a v4.x release.
|
||||
|
||||
## `Responder` Trait
|
||||
|
||||
@@ -251,7 +387,7 @@ You may need to review the [guidance on shared mutable state](https://docs.rs/ac
|
||||
Improvements to module management and re-exports have resulted in not needing direct dependencies on these underlying crates for the vast majority of cases. In particular:
|
||||
|
||||
- all traits necessary for creating middlewares are now re-exported through the `dev` modules;
|
||||
- `#[actix_web::test]` now exists for async test definitions.
|
||||
- `#[actix_web::test]` now exists for async test definitions.
|
||||
|
||||
Relying on these re-exports will ease the transition to future versions of Actix Web.
|
||||
|
||||
|
@@ -6,10 +6,10 @@
|
||||
<p>
|
||||
|
||||
[](https://crates.io/crates/actix-web)
|
||||
[](https://docs.rs/actix-web/4.0.0-rc.3)
|
||||
[](https://docs.rs/actix-web/4.0.1)
|
||||

|
||||

|
||||
[](https://deps.rs/crate/actix-web/4.0.0-rc.3)
|
||||
[](https://deps.rs/crate/actix-web/4.0.1)
|
||||
<br />
|
||||
[](https://github.com/actix/actix-web/actions/workflows/ci.yml)
|
||||
[](https://codecov.io/gh/actix/actix-web)
|
||||
@@ -48,7 +48,7 @@ Dependencies:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
actix-web = "4.0.0-rc.1"
|
||||
actix-web = "4"
|
||||
```
|
||||
|
||||
Code:
|
||||
@@ -56,18 +56,19 @@ Code:
|
||||
```rust
|
||||
use actix_web::{get, web, App, HttpServer, Responder};
|
||||
|
||||
#[get("/{id}/{name}/index.html")]
|
||||
async fn index(params: web::Path<(u32, String)>) -> impl Responder {
|
||||
let (id, name) = params.into_inner();
|
||||
format!("Hello {}! id:{}", name, id)
|
||||
#[get("/hello/{name}")]
|
||||
async fn greet(name: web::Path<String>) -> impl Responder {
|
||||
format!("Hello {name}!")
|
||||
}
|
||||
|
||||
#[actix_web::main] // or #[tokio::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
HttpServer::new(|| App::new().service(index))
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
.run()
|
||||
.await
|
||||
HttpServer::new(|| {
|
||||
App::new().service(greet)
|
||||
})
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
```
|
||||
|
||||
|
@@ -290,12 +290,10 @@ where
|
||||
/// Ok(HttpResponse::Ok().into())
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let app = App::new()
|
||||
/// .service(web::resource("/index.html").route(
|
||||
/// web::get().to(index)))
|
||||
/// .external_resource("youtube", "https://youtube.com/watch/{video_id}");
|
||||
/// }
|
||||
/// let app = App::new()
|
||||
/// .service(web::resource("/index.html").route(
|
||||
/// web::get().to(index)))
|
||||
/// .external_resource("youtube", "https://youtube.com/watch/{video_id}");
|
||||
/// ```
|
||||
pub fn external_resource<N, U>(mut self, name: N, url: U) -> Self
|
||||
where
|
||||
|
@@ -2,6 +2,10 @@
|
||||
//!
|
||||
//! Most users will not have to interact with the types in this module, but it is useful for those
|
||||
//! writing extractors, middleware, libraries, or interacting with the service API directly.
|
||||
//!
|
||||
//! # Request Extractors
|
||||
//! - [`ConnectionInfo`]: Connection information
|
||||
//! - [`PeerAddr`]: Connection information
|
||||
|
||||
pub use actix_http::{Extensions, Payload, RequestHead, Response, ResponseHead};
|
||||
pub use actix_router::{Path, ResourceDef, ResourcePath, Url};
|
||||
|
@@ -47,7 +47,6 @@ impl fmt::Debug for Error {
|
||||
|
||||
impl StdError for Error {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
// TODO: populate if replacement for Box<dyn Error> is found
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@
|
||||
//
|
||||
// See <https://github.com/rust-lang/rust/issues/83375>
|
||||
pub use actix_http::error::{
|
||||
BlockingError, ContentTypeError, DispatchError, HttpError, ParseError, PayloadError,
|
||||
ContentTypeError, DispatchError, HttpError, ParseError, PayloadError,
|
||||
};
|
||||
|
||||
use derive_more::{Display, Error, From};
|
||||
@@ -33,6 +33,14 @@ pub(crate) use macros::{downcast_dyn, downcast_get_type_id};
|
||||
/// This type alias is generally used to avoid writing out `actix_http::Error` directly.
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
/// An error representing a problem running a blocking task on a thread pool.
|
||||
#[derive(Debug, Display, Error)]
|
||||
#[display(fmt = "Blocking thread pool is shut down unexpectedly")]
|
||||
#[non_exhaustive]
|
||||
pub struct BlockingError;
|
||||
|
||||
impl ResponseError for crate::error::BlockingError {}
|
||||
|
||||
/// Errors which can occur when attempting to generate resource uri.
|
||||
#[derive(Debug, PartialEq, Display, Error, From)]
|
||||
#[non_exhaustive]
|
||||
|
@@ -6,20 +6,22 @@ use std::{
|
||||
io::{self, Write as _},
|
||||
};
|
||||
|
||||
use actix_http::{
|
||||
body::BoxBody,
|
||||
header::{self, TryIntoHeaderValue},
|
||||
Response, StatusCode,
|
||||
};
|
||||
use actix_http::Response;
|
||||
use bytes::BytesMut;
|
||||
|
||||
use crate::{
|
||||
body::BoxBody,
|
||||
error::{downcast_dyn, downcast_get_type_id},
|
||||
helpers, HttpResponse,
|
||||
helpers,
|
||||
http::{
|
||||
header::{self, TryIntoHeaderValue},
|
||||
StatusCode,
|
||||
},
|
||||
HttpResponse,
|
||||
};
|
||||
|
||||
/// Errors that can generate responses.
|
||||
// TODO: add std::error::Error bound when replacement for Box<dyn Error> is found
|
||||
// TODO: flesh out documentation
|
||||
pub trait ResponseError: fmt::Debug + fmt::Display {
|
||||
/// Returns appropriate status code for error.
|
||||
///
|
||||
@@ -73,7 +75,6 @@ impl ResponseError for std::str::Utf8Error {
|
||||
|
||||
impl ResponseError for std::io::Error {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
// TODO: decide if these errors should consider not found or permission errors
|
||||
match self.kind() {
|
||||
io::ErrorKind::NotFound => StatusCode::NOT_FOUND,
|
||||
io::ErrorKind::PermissionDenied => StatusCode::FORBIDDEN,
|
||||
@@ -86,7 +87,6 @@ impl ResponseError for actix_http::error::HttpError {}
|
||||
|
||||
impl ResponseError for actix_http::Error {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
// TODO: map error kinds to status code better
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
|
||||
@@ -107,8 +107,6 @@ impl ResponseError for actix_http::error::ParseError {
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for actix_http::error::BlockingError {}
|
||||
|
||||
impl ResponseError for actix_http::error::PayloadError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
|
@@ -118,12 +118,10 @@ pub trait FromRequest: Sized {
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let app = App::new().service(
|
||||
/// web::resource("/users/:first").route(
|
||||
/// web::post().to(index))
|
||||
/// );
|
||||
/// }
|
||||
/// let app = App::new().service(
|
||||
/// web::resource("/users/:first").route(
|
||||
/// web::post().to(index))
|
||||
/// );
|
||||
/// ```
|
||||
impl<T> FromRequest for Option<T>
|
||||
where
|
||||
@@ -205,11 +203,9 @@ where
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let app = App::new().service(
|
||||
/// web::resource("/users/:first").route(web::post().to(index))
|
||||
/// );
|
||||
/// }
|
||||
/// let app = App::new().service(
|
||||
/// web::resource("/users/:first").route(web::post().to(index))
|
||||
/// );
|
||||
/// ```
|
||||
impl<T, E> FromRequest for Result<T, E>
|
||||
where
|
||||
|
@@ -2,5 +2,4 @@
|
||||
|
||||
pub mod header;
|
||||
|
||||
// TODO: figure out how best to expose http::Error vs actix_http::Error
|
||||
pub use actix_http::{uri, ConnectionType, Error, KeepAlive, Method, StatusCode, Uri, Version};
|
||||
|
@@ -159,7 +159,7 @@ impl ConnectionInfo {
|
||||
pub fn realip_remote_addr(&self) -> Option<&str> {
|
||||
self.realip_remote_addr
|
||||
.as_deref()
|
||||
.or_else(|| self.peer_addr.as_deref())
|
||||
.or(self.peer_addr.as_deref())
|
||||
}
|
||||
|
||||
/// Returns serialized IP address of the peer connection.
|
||||
|
@@ -4,18 +4,19 @@
|
||||
//! ```no_run
|
||||
//! use actix_web::{get, web, App, HttpServer, Responder};
|
||||
//!
|
||||
//! #[get("/{id}/{name}/index.html")]
|
||||
//! async fn index(path: web::Path<(u32, String)>) -> impl Responder {
|
||||
//! let (id, name) = path.into_inner();
|
||||
//! format!("Hello {}! id:{}", name, id)
|
||||
//! #[get("/hello/{name}")]
|
||||
//! async fn greet(name: web::Path<String>) -> impl Responder {
|
||||
//! format!("Hello {}!", name)
|
||||
//! }
|
||||
//!
|
||||
//! #[actix_web::main]
|
||||
//! #[actix_web::main] // or #[tokio::main]
|
||||
//! async fn main() -> std::io::Result<()> {
|
||||
//! HttpServer::new(|| App::new().service(index))
|
||||
//! .bind("127.0.0.1:8080")?
|
||||
//! .run()
|
||||
//! .await
|
||||
//! HttpServer::new(|| {
|
||||
//! App::new().service(greet)
|
||||
//! })
|
||||
//! .bind(("127.0.0.1", 8080))?
|
||||
//! .run()
|
||||
//! .await
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
|
@@ -185,6 +185,7 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
body,
|
||||
http::{
|
||||
header::{HeaderValue, CONTENT_TYPE},
|
||||
StatusCode,
|
||||
@@ -203,7 +204,7 @@ mod tests {
|
||||
Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))
|
||||
}
|
||||
|
||||
let srv = test::simple_service(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
let srv = test::status_service(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
|
||||
let mw = ErrorHandlers::new()
|
||||
.handler(StatusCode::INTERNAL_SERVER_ERROR, error_handler)
|
||||
@@ -230,7 +231,7 @@ mod tests {
|
||||
))
|
||||
}
|
||||
|
||||
let srv = test::simple_service(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
let srv = test::status_service(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
|
||||
let mw = ErrorHandlers::new()
|
||||
.handler(StatusCode::INTERNAL_SERVER_ERROR, error_handler)
|
||||
@@ -245,9 +246,7 @@ mod tests {
|
||||
#[actix_rt::test]
|
||||
async fn changes_body_type() {
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn error_handler<B: 'static>(
|
||||
res: ServiceResponse<B>,
|
||||
) -> Result<ErrorHandlerResponse<B>> {
|
||||
fn error_handler<B>(res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
|
||||
let (req, res) = res.into_parts();
|
||||
let res = res.set_body(Bytes::from("sorry, that's no bueno"));
|
||||
|
||||
@@ -258,7 +257,7 @@ mod tests {
|
||||
Ok(ErrorHandlerResponse::Response(res))
|
||||
}
|
||||
|
||||
let srv = test::simple_service(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
let srv = test::status_service(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
|
||||
let mw = ErrorHandlers::new()
|
||||
.handler(StatusCode::INTERNAL_SERVER_ERROR, error_handler)
|
||||
@@ -270,5 +269,33 @@ mod tests {
|
||||
assert_eq!(test::read_body(res).await, "sorry, that's no bueno");
|
||||
}
|
||||
|
||||
// TODO: test where error is thrown
|
||||
#[actix_rt::test]
|
||||
async fn error_thrown() {
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn error_handler<B>(_res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
|
||||
Err(crate::error::ErrorInternalServerError(
|
||||
"error in error handler",
|
||||
))
|
||||
}
|
||||
|
||||
let srv = test::status_service(StatusCode::BAD_REQUEST);
|
||||
|
||||
let mw = ErrorHandlers::new()
|
||||
.handler(StatusCode::BAD_REQUEST, error_handler)
|
||||
.new_transform(srv.into_service())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let err = mw
|
||||
.call(TestRequest::default().to_srv_request())
|
||||
.await
|
||||
.unwrap_err();
|
||||
let res = err.error_response();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||
assert_eq!(
|
||||
body::to_bytes(res.into_body()).await.unwrap(),
|
||||
"error in error handler"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -407,12 +407,10 @@ impl Drop for HttpRequest {
|
||||
/// format!("Got thing: {:?}", req)
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let app = App::new().service(
|
||||
/// web::resource("/users/{first}").route(
|
||||
/// web::get().to(index))
|
||||
/// );
|
||||
/// }
|
||||
/// let app = App::new().service(
|
||||
/// web::resource("/users/{first}").route(
|
||||
/// web::get().to(index))
|
||||
/// );
|
||||
/// ```
|
||||
impl FromRequest for HttpRequest {
|
||||
type Error = Error;
|
||||
|
@@ -93,19 +93,17 @@ where
|
||||
/// "Welcome!"
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let app = App::new()
|
||||
/// .service(
|
||||
/// web::resource("/app")
|
||||
/// .guard(guard::Header("content-type", "text/plain"))
|
||||
/// .route(web::get().to(index))
|
||||
/// )
|
||||
/// .service(
|
||||
/// web::resource("/app")
|
||||
/// .guard(guard::Header("content-type", "text/json"))
|
||||
/// .route(web::get().to(|| HttpResponse::MethodNotAllowed()))
|
||||
/// );
|
||||
/// }
|
||||
/// let app = App::new()
|
||||
/// .service(
|
||||
/// web::resource("/app")
|
||||
/// .guard(guard::Header("content-type", "text/plain"))
|
||||
/// .route(web::get().to(index))
|
||||
/// )
|
||||
/// .service(
|
||||
/// web::resource("/app")
|
||||
/// .guard(guard::Header("content-type", "text/json"))
|
||||
/// .route(web::get().to(|| HttpResponse::MethodNotAllowed()))
|
||||
/// );
|
||||
/// ```
|
||||
pub fn guard<G: Guard + 'static>(mut self, guard: G) -> Self {
|
||||
self.guards.push(Box::new(guard));
|
||||
@@ -137,14 +135,13 @@ where
|
||||
/// ```
|
||||
/// use actix_web::{web, guard, App};
|
||||
///
|
||||
/// fn main() {
|
||||
/// let app = App::new().service(
|
||||
/// web::resource("/container/")
|
||||
/// .route(web::get().to(get_handler))
|
||||
/// .route(web::post().to(post_handler))
|
||||
/// .route(web::delete().to(delete_handler))
|
||||
/// );
|
||||
/// }
|
||||
/// let app = App::new().service(
|
||||
/// web::resource("/container/")
|
||||
/// .route(web::get().to(get_handler))
|
||||
/// .route(web::post().to(post_handler))
|
||||
/// .route(web::delete().to(delete_handler))
|
||||
/// );
|
||||
///
|
||||
/// # async fn get_handler() -> impl actix_web::Responder { actix_web::HttpResponse::Ok() }
|
||||
/// # async fn post_handler() -> impl actix_web::Responder { actix_web::HttpResponse::Ok() }
|
||||
/// # async fn delete_handler() -> impl actix_web::Responder { actix_web::HttpResponse::Ok() }
|
||||
|
@@ -7,14 +7,35 @@ use actix_http::{
|
||||
};
|
||||
use bytes::{Bytes, BytesMut};
|
||||
|
||||
use crate::{Error, HttpRequest, HttpResponse};
|
||||
|
||||
use super::CustomizeResponder;
|
||||
use crate::{Error, HttpRequest, HttpResponse};
|
||||
|
||||
/// Trait implemented by types that can be converted to an HTTP response.
|
||||
///
|
||||
/// Any types that implement this trait can be used in the return type of a handler.
|
||||
// # TODO: more about implementation notes and foreign impls
|
||||
/// Any types that implement this trait can be used in the return type of a handler. Since handlers
|
||||
/// will only have one return type, it is idiomatic to use opaque return types `-> impl Responder`.
|
||||
///
|
||||
/// # Implementations
|
||||
/// It is often not required to implement `Responder` for your own types due to a broad base of
|
||||
/// built-in implementations:
|
||||
/// - `HttpResponse` and `HttpResponseBuilder`
|
||||
/// - `Option<R>` where `R: Responder`
|
||||
/// - `Result<R, E>` where `R: Responder` and [`E: ResponseError`](crate::ResponseError)
|
||||
/// - `(R, StatusCode) where `R: Responder`
|
||||
/// - `&'static str`, `String`, `&'_ String`, `Cow<'_, str>`, [`ByteString`](bytestring::ByteString)
|
||||
/// - `&'static [u8]`, `Vec<u8>`, `Bytes`, `BytesMut`
|
||||
/// - [`Json<T>`](crate::web::Json) and [`Form<T>`](crate::web::Form) where `T: Serialize`
|
||||
/// - [`Either<L, R>`](crate::web::Either) where `L: Serialize` and `R: Serialize`
|
||||
/// - [`CustomizeResponder<R>`]
|
||||
/// - [`actix_files::NamedFile`](https://docs.rs/actix-files/latest/actix_files/struct.NamedFile.html)
|
||||
/// - [Experimental responders from `actix-web-lab`](https://docs.rs/actix-web-lab/latest/actix_web_lab/respond/index.html)
|
||||
/// - Third party integrations may also have implemented `Responder` where appropriate. For example,
|
||||
/// HTML templating engines.
|
||||
///
|
||||
/// # Customizing Responder Output
|
||||
/// Calling [`.customize()`](Responder::customize) on any responder type will wrap it in a
|
||||
/// [`CustomizeResponder`] capable of overriding various parts of the response such as the status
|
||||
/// code and header map.
|
||||
pub trait Responder {
|
||||
type Body: MessageBody + 'static;
|
||||
|
||||
@@ -23,7 +44,7 @@ pub trait Responder {
|
||||
|
||||
/// Wraps responder to allow alteration of its response.
|
||||
///
|
||||
/// See [`CustomizeResponder`] docs for its capabilities.
|
||||
/// See [`CustomizeResponder`] docs for more details on its capabilities.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
@@ -84,11 +105,8 @@ impl Responder for actix_http::ResponseBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Responder for Option<T>
|
||||
where
|
||||
T: Responder,
|
||||
{
|
||||
type Body = EitherBody<T::Body>;
|
||||
impl<R: Responder> Responder for Option<R> {
|
||||
type Body = EitherBody<R::Body>;
|
||||
|
||||
fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
|
||||
match self {
|
||||
@@ -98,12 +116,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Responder for Result<T, E>
|
||||
impl<R, E> Responder for Result<R, E>
|
||||
where
|
||||
T: Responder,
|
||||
R: Responder,
|
||||
E: Into<Error>,
|
||||
{
|
||||
type Body = EitherBody<T::Body>;
|
||||
type Body = EitherBody<R::Body>;
|
||||
|
||||
fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
|
||||
match self {
|
||||
@@ -113,8 +131,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Responder> Responder for (T, StatusCode) {
|
||||
type Body = T::Body;
|
||||
impl<R: Responder> Responder for (R, StatusCode) {
|
||||
type Body = R::Body;
|
||||
|
||||
fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
|
||||
let mut res = self.0.respond_to(req);
|
||||
@@ -147,6 +165,7 @@ impl_responder_by_forward_into_base_response!(BytesMut);
|
||||
|
||||
impl_responder_by_forward_into_base_response!(&'static str);
|
||||
impl_responder_by_forward_into_base_response!(String);
|
||||
impl_responder_by_forward_into_base_response!(bytestring::ByteString);
|
||||
|
||||
macro_rules! impl_into_string_responder {
|
||||
($res:ty) => {
|
||||
|
@@ -323,12 +323,6 @@ impl From<Error> for HttpResponse {
|
||||
impl<B> From<HttpResponse<B>> for Response<B> {
|
||||
fn from(res: HttpResponse<B>) -> Self {
|
||||
// this impl will always be called as part of dispatcher
|
||||
|
||||
// TODO: expose cause somewhere?
|
||||
// if let Some(err) = res.error {
|
||||
// return Response::from_error(err);
|
||||
// }
|
||||
|
||||
res.res
|
||||
}
|
||||
}
|
||||
|
@@ -151,7 +151,7 @@ impl ResourceMap {
|
||||
.char_indices()
|
||||
.filter_map(|(i, c)| (c == '/').then(|| i))
|
||||
.nth(2)
|
||||
.unwrap_or_else(|| path.len());
|
||||
.unwrap_or(path.len());
|
||||
|
||||
(
|
||||
Cow::Borrowed(&path[..third_slash_index]),
|
||||
|
@@ -78,18 +78,18 @@ pub struct ServiceRequest {
|
||||
}
|
||||
|
||||
impl ServiceRequest {
|
||||
/// Construct service request
|
||||
/// Construct `ServiceRequest` from parts.
|
||||
pub(crate) fn new(req: HttpRequest, payload: Payload) -> Self {
|
||||
Self { req, payload }
|
||||
}
|
||||
|
||||
/// Deconstruct request into parts
|
||||
/// Deconstruct `ServiceRequest` into inner parts.
|
||||
#[inline]
|
||||
pub fn into_parts(self) -> (HttpRequest, Payload) {
|
||||
(self.req, self.payload)
|
||||
}
|
||||
|
||||
/// Get mutable access to inner `HttpRequest` and `Payload`
|
||||
/// Returns mutable accessors to inner parts.
|
||||
#[inline]
|
||||
pub fn parts_mut(&mut self) -> (&mut HttpRequest, &mut Payload) {
|
||||
(&mut self.req, &mut self.payload)
|
||||
@@ -105,9 +105,7 @@ impl ServiceRequest {
|
||||
Self { req, payload }
|
||||
}
|
||||
|
||||
/// Construct request from request.
|
||||
///
|
||||
/// The returned `ServiceRequest` would have no payload.
|
||||
/// Construct `ServiceRequest` with no payload from given `HttpRequest`.
|
||||
#[inline]
|
||||
pub fn from_request(req: HttpRequest) -> Self {
|
||||
ServiceRequest {
|
||||
@@ -116,63 +114,63 @@ impl ServiceRequest {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create service response
|
||||
/// Create `ServiceResponse` from this request and given response.
|
||||
#[inline]
|
||||
pub fn into_response<B, R: Into<Response<B>>>(self, res: R) -> ServiceResponse<B> {
|
||||
let res = HttpResponse::from(res.into());
|
||||
ServiceResponse::new(self.req, res)
|
||||
}
|
||||
|
||||
/// Create service response for error
|
||||
/// Create `ServiceResponse` from this request and given error.
|
||||
#[inline]
|
||||
pub fn error_response<E: Into<Error>>(self, err: E) -> ServiceResponse {
|
||||
let res = HttpResponse::from_error(err.into());
|
||||
ServiceResponse::new(self.req, res)
|
||||
}
|
||||
|
||||
/// This method returns reference to the request head
|
||||
/// Returns a reference to the request head.
|
||||
#[inline]
|
||||
pub fn head(&self) -> &RequestHead {
|
||||
self.req.head()
|
||||
}
|
||||
|
||||
/// This method returns reference to the request head
|
||||
/// Returns a mutable reference to the request head.
|
||||
#[inline]
|
||||
pub fn head_mut(&mut self) -> &mut RequestHead {
|
||||
self.req.head_mut()
|
||||
}
|
||||
|
||||
/// Request's uri.
|
||||
/// Returns the request URI.
|
||||
#[inline]
|
||||
pub fn uri(&self) -> &Uri {
|
||||
&self.head().uri
|
||||
}
|
||||
|
||||
/// Read the Request method.
|
||||
/// Returns the request method.
|
||||
#[inline]
|
||||
pub fn method(&self) -> &Method {
|
||||
&self.head().method
|
||||
}
|
||||
|
||||
/// Read the Request Version.
|
||||
/// Returns the request version.
|
||||
#[inline]
|
||||
pub fn version(&self) -> Version {
|
||||
self.head().version
|
||||
}
|
||||
|
||||
/// Returns a reference to request headers.
|
||||
#[inline]
|
||||
/// Returns request's headers.
|
||||
pub fn headers(&self) -> &HeaderMap {
|
||||
&self.head().headers
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to request headers.
|
||||
#[inline]
|
||||
/// Returns mutable request's headers.
|
||||
pub fn headers_mut(&mut self) -> &mut HeaderMap {
|
||||
&mut self.head_mut().headers
|
||||
}
|
||||
|
||||
/// The target path of this Request.
|
||||
/// Returns request path.
|
||||
#[inline]
|
||||
pub fn path(&self) -> &str {
|
||||
self.head().uri.path()
|
||||
@@ -184,7 +182,7 @@ impl ServiceRequest {
|
||||
self.req.query_string()
|
||||
}
|
||||
|
||||
/// Peer socket address.
|
||||
/// Returns peer's socket address.
|
||||
///
|
||||
/// Peer address is the directly connected peer's socket address. If a proxy is used in front of
|
||||
/// the Actix Web server, then it would be address of this proxy.
|
||||
@@ -197,24 +195,23 @@ impl ServiceRequest {
|
||||
self.head().peer_addr
|
||||
}
|
||||
|
||||
/// Get *ConnectionInfo* for the current request.
|
||||
/// Returns a reference to connection info.
|
||||
#[inline]
|
||||
pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> {
|
||||
self.req.connection_info()
|
||||
}
|
||||
|
||||
/// Returns a reference to the Path parameters.
|
||||
/// Returns 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.
|
||||
/// 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<Url> {
|
||||
self.req.match_info()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the Path parameters.
|
||||
/// Returns a mutable reference to the path match information.
|
||||
#[inline]
|
||||
pub fn match_info_mut(&mut self) -> &mut Path<Url> {
|
||||
self.req.match_info_mut()
|
||||
@@ -232,13 +229,13 @@ impl ServiceRequest {
|
||||
self.req.match_pattern()
|
||||
}
|
||||
|
||||
/// Get a reference to a `ResourceMap` of current application.
|
||||
/// Returns a reference to the application's resource map.
|
||||
#[inline]
|
||||
pub fn resource_map(&self) -> &ResourceMap {
|
||||
self.req.resource_map()
|
||||
}
|
||||
|
||||
/// Service configuration
|
||||
/// Returns a reference to the application's configuration.
|
||||
#[inline]
|
||||
pub fn app_config(&self) -> &AppConfig {
|
||||
self.req.app_config()
|
||||
@@ -262,6 +259,7 @@ impl ServiceRequest {
|
||||
self.req.conn_data()
|
||||
}
|
||||
|
||||
/// Return request cookies.
|
||||
#[cfg(feature = "cookies")]
|
||||
#[inline]
|
||||
pub fn cookies(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> {
|
||||
|
@@ -5,7 +5,7 @@
|
||||
//!
|
||||
//! # Off-The-Shelf Test Services
|
||||
//! - [`ok_service`]
|
||||
//! - [`simple_service`]
|
||||
//! - [`status_service`]
|
||||
//!
|
||||
//! # Calling Test Service
|
||||
//! - [`TestRequest`]
|
||||
@@ -27,7 +27,7 @@ mod test_utils;
|
||||
|
||||
pub use self::test_request::TestRequest;
|
||||
#[allow(deprecated)]
|
||||
pub use self::test_services::{default_service, ok_service, simple_service};
|
||||
pub use self::test_services::{default_service, ok_service, simple_service, status_service};
|
||||
#[allow(deprecated)]
|
||||
pub use self::test_utils::{
|
||||
call_and_read_body, call_and_read_body_json, call_service, init_service, read_body,
|
||||
|
@@ -24,10 +24,10 @@ use crate::cookie::{Cookie, CookieJar};
|
||||
///
|
||||
/// For unit testing, actix provides a request builder type and a simple handler runner. TestRequest implements a builder-like pattern.
|
||||
/// You can generate various types of request via TestRequest's methods:
|
||||
/// * `TestRequest::to_request` creates `actix_http::Request` instance.
|
||||
/// * `TestRequest::to_srv_request` creates `ServiceRequest` instance, which is used for testing middlewares and chain adapters.
|
||||
/// * `TestRequest::to_srv_response` creates `ServiceResponse` instance.
|
||||
/// * `TestRequest::to_http_request` creates `HttpRequest` instance, which is used for testing handlers.
|
||||
/// - [`TestRequest::to_request`] creates an [`actix_http::Request`](Request).
|
||||
/// - [`TestRequest::to_srv_request`] creates a [`ServiceRequest`], which is used for testing middlewares and chain adapters.
|
||||
/// - [`TestRequest::to_srv_response`] creates a [`ServiceResponse`].
|
||||
/// - [`TestRequest::to_http_request`] creates an [`HttpRequest`], which is used for testing handlers.
|
||||
///
|
||||
/// ```
|
||||
/// use actix_web::{test, HttpRequest, HttpResponse, HttpMessage};
|
||||
@@ -42,15 +42,17 @@ use crate::cookie::{Cookie, CookieJar};
|
||||
/// }
|
||||
///
|
||||
/// #[actix_web::test]
|
||||
/// # // force rustdoc to display the correct thing and also compile check the test
|
||||
/// # async fn _test() {}
|
||||
/// async fn test_index() {
|
||||
/// let req = test::TestRequest::default().insert_header("content-type", "text/plain")
|
||||
/// let req = test::TestRequest::default().insert_header(header::ContentType::plaintext())
|
||||
/// .to_http_request();
|
||||
///
|
||||
/// let resp = index(req).await.unwrap();
|
||||
/// let resp = index(req).await;
|
||||
/// assert_eq!(resp.status(), StatusCode::OK);
|
||||
///
|
||||
/// let req = test::TestRequest::default().to_http_request();
|
||||
/// let resp = index(req).await.unwrap();
|
||||
/// let resp = index(req).await;
|
||||
/// assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||
/// }
|
||||
/// ```
|
||||
|
@@ -10,11 +10,11 @@ use crate::{
|
||||
/// Creates service that always responds with `200 OK` and no body.
|
||||
pub fn ok_service(
|
||||
) -> impl Service<ServiceRequest, Response = ServiceResponse<BoxBody>, Error = Error> {
|
||||
simple_service(StatusCode::OK)
|
||||
status_service(StatusCode::OK)
|
||||
}
|
||||
|
||||
/// Creates service that always responds with given status code and no body.
|
||||
pub fn simple_service(
|
||||
pub fn status_service(
|
||||
status_code: StatusCode,
|
||||
) -> impl Service<ServiceRequest, Response = ServiceResponse<BoxBody>, Error = Error> {
|
||||
fn_service(move |req: ServiceRequest| {
|
||||
@@ -23,9 +23,17 @@ pub fn simple_service(
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[deprecated(since = "4.0.0", note = "Renamed to `simple_service`.")]
|
||||
#[deprecated(since = "4.0.0", note = "Renamed to `status_service`.")]
|
||||
pub fn simple_service(
|
||||
status_code: StatusCode,
|
||||
) -> impl Service<ServiceRequest, Response = ServiceResponse<BoxBody>, Error = Error> {
|
||||
status_service(status_code)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[deprecated(since = "4.0.0", note = "Renamed to `status_service`.")]
|
||||
pub fn default_service(
|
||||
status_code: StatusCode,
|
||||
) -> impl Service<ServiceRequest, Response = ServiceResponse<BoxBody>, Error = Error> {
|
||||
simple_service(status_code)
|
||||
status_service(status_code)
|
||||
}
|
||||
|
@@ -89,7 +89,7 @@ where
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if service call returns error.
|
||||
/// Panics if service call returns error. To handle errors use `app.call(req)`.
|
||||
pub async fn call_service<S, R, B, E>(app: &S, req: R) -> S::Response
|
||||
where
|
||||
S: Service<R, Response = ServiceResponse<B>, Error = E>,
|
||||
|
@@ -1,4 +1,18 @@
|
||||
//! Essentials helper functions and types for application registration.
|
||||
//!
|
||||
//! # Request Extractors
|
||||
//! - [`Data`]: Application data item
|
||||
//! - [`ReqData`]: Request-local data item
|
||||
//! - [`Path`]: URL path parameters / dynamic segments
|
||||
//! - [`Query`]: URL query parameters
|
||||
//! - [`Header`]: Typed header
|
||||
//! - [`Json`]: JSON payload
|
||||
//! - [`Form`]: URL-encoded payload
|
||||
//! - [`Bytes`]: Raw payload
|
||||
//!
|
||||
//! # Responders
|
||||
//! - [`Json`]: JSON request payload
|
||||
//! - [`Bytes`]: Raw request payload
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
@@ -12,9 +26,7 @@ use crate::{
|
||||
|
||||
pub use crate::config::ServiceConfig;
|
||||
pub use crate::data::Data;
|
||||
pub use crate::request::HttpRequest;
|
||||
pub use crate::request_data::ReqData;
|
||||
pub use crate::response::HttpResponse;
|
||||
pub use crate::types::*;
|
||||
|
||||
/// Creates a new resource for a specific path.
|
||||
|
@@ -3,6 +3,103 @@
|
||||
## Unreleased - 2021-xx-xx
|
||||
|
||||
|
||||
## 3.0.0 - 2022-03-07
|
||||
### Dependencies
|
||||
- Updated `actix-*` to Tokio v1-based versions. [#1813]
|
||||
- Updated `bytes` to `1.0`. [#1813]
|
||||
- Updated `cookie` to `0.16`. [#2555]
|
||||
- Updated `rand` to `0.8`.
|
||||
- Updated `rustls` to `0.20`. [#2414]
|
||||
- Updated `tokio` to `1`.
|
||||
|
||||
### Added
|
||||
- `trust-dns` crate feature to enable `trust-dns-resolver` as client DNS resolver; disabled by default. [#1969]
|
||||
- `cookies` crate feature; enabled by default. [#2619]
|
||||
- `compress-brotli` crate feature; enabled by default. [#2250]
|
||||
- `compress-gzip` crate feature; enabled by default. [#2250]
|
||||
- `compress-zstd` crate feature; enabled by default. [#2250]
|
||||
- `client::Connector::handshake_timeout()` for customizing TLS connection handshake timeout. [#2081]
|
||||
- `client::ConnectorService` as `client::Connector::finish` method's return type [#2081]
|
||||
- `client::ConnectionIo` trait alias [#2081]
|
||||
- `Client::headers()` to get default mut reference of `HeaderMap` of client object. [#2114]
|
||||
- `ClientResponse::timeout()` for set the timeout of collecting response body. [#1931]
|
||||
- `ClientBuilder::local_address()` for binding to a local IP address for this client. [#2024]
|
||||
- `ClientRequest::insert_header()` method which allows using typed and untyped headers. [#1869]
|
||||
- `ClientRequest::append_header()` method which allows using typed and untyped headers. [#1869]
|
||||
- `ClientBuilder::add_default_header()` (and deprecate `ClientBuilder::header()`). [#2510]
|
||||
|
||||
### Changed
|
||||
- `client::Connector` type now only has one generic type for `actix_service::Service`. [#2063]
|
||||
- `client::error::ConnectError` Resolver variant contains `Box<dyn std::error::Error>` type. [#1905]
|
||||
- `client::ConnectorConfig` default timeout changed to 5 seconds. [#1905]
|
||||
- `ConnectorService` type is renamed to `BoxConnectorService`. [#2081]
|
||||
- Fix http/https encoding when enabling `compress` feature. [#2116]
|
||||
- Rename `TestResponse::{header => append_header, set => insert_header}`. These methods now take a `TryIntoHeaderPair`. [#2094]
|
||||
- `ClientBuilder::connector()` method now takes `Connector<T, U>` type. [#2008]
|
||||
- Basic auth now accepts blank passwords as an empty string instead of an `Option`. [#2050]
|
||||
- Relax default timeout for `Connector` to 5 seconds (up from 1 second). [#1905]
|
||||
- `*::send_json()` and `*::send_form()` methods now receive `impl Serialize`. [#2553]
|
||||
- `FrozenClientRequest::extra_header()` now uses receives an `impl TryIntoHeaderPair`. [#2553]
|
||||
- Rename `Connector::{ssl => openssl}()`. [#2503]
|
||||
- `ClientRequest::send_body` now takes an `impl MessageBody`. [#2546]
|
||||
- Rename `MessageBody => ResponseBody` to avoid conflicts with `MessageBody` trait. [#2546]
|
||||
- Minimum supported Rust version (MSRV) is now 1.54.
|
||||
|
||||
### Fixed
|
||||
- Send headers along with redirected requests. [#2310]
|
||||
- Improve `Client` instantiation efficiency when using `openssl` by only building connectors once. [#2503]
|
||||
- Remove unnecessary `Unpin` bounds on `*::send_stream`. [#2553]
|
||||
- `impl Future` for `ResponseBody` no longer requires the body type be `Unpin`. [#2546]
|
||||
- `impl Future` for `JsonBody` no longer requires the body type be `Unpin`. [#2546]
|
||||
- `impl Stream` for `ClientResponse` no longer requires the body type be `Unpin`. [#2546]
|
||||
|
||||
### Removed
|
||||
- `compress` crate feature. [#2250]
|
||||
- `ClientRequest::set`; use `ClientRequest::insert_header`. [#1869]
|
||||
- `ClientRequest::set_header`; use `ClientRequest::insert_header`. [#1869]
|
||||
- `ClientRequest::set_header_if_none`; use `ClientRequest::insert_header_if_none`. [#1869]
|
||||
- `ClientRequest::header`; use `ClientRequest::append_header`. [#1869]
|
||||
- Deprecated methods on `ClientRequest`: `if_true`, `if_some`. [#2148]
|
||||
- `ClientBuilder::default` function [#2008]
|
||||
|
||||
### Security
|
||||
- `cookie` upgrade addresses [`RUSTSEC-2020-0071`].
|
||||
|
||||
[`RUSTSEC-2020-0071`]: https://rustsec.org/advisories/RUSTSEC-2020-0071.html
|
||||
|
||||
[#1813]: https://github.com/actix/actix-web/pull/1813
|
||||
[#1869]: https://github.com/actix/actix-web/pull/1869
|
||||
[#1905]: https://github.com/actix/actix-web/pull/1905
|
||||
[#1905]: https://github.com/actix/actix-web/pull/1905
|
||||
[#1931]: https://github.com/actix/actix-web/pull/1931
|
||||
[#1969]: https://github.com/actix/actix-web/pull/1969
|
||||
[#1969]: https://github.com/actix/actix-web/pull/1969
|
||||
[#1981]: https://github.com/actix/actix-web/pull/1981
|
||||
[#2008]: https://github.com/actix/actix-web/pull/2008
|
||||
[#2024]: https://github.com/actix/actix-web/pull/2024
|
||||
[#2050]: https://github.com/actix/actix-web/pull/2050
|
||||
[#2063]: https://github.com/actix/actix-web/pull/2063
|
||||
[#2081]: https://github.com/actix/actix-web/pull/2081
|
||||
[#2081]: https://github.com/actix/actix-web/pull/2081
|
||||
[#2094]: https://github.com/actix/actix-web/pull/2094
|
||||
[#2114]: https://github.com/actix/actix-web/pull/2114
|
||||
[#2116]: https://github.com/actix/actix-web/pull/2116
|
||||
[#2148]: https://github.com/actix/actix-web/pull/2148
|
||||
[#2250]: https://github.com/actix/actix-web/pull/2250
|
||||
[#2310]: https://github.com/actix/actix-web/pull/2310
|
||||
[#2414]: https://github.com/actix/actix-web/pull/2414
|
||||
[#2425]: https://github.com/actix/actix-web/pull/2425
|
||||
[#2474]: https://github.com/actix/actix-web/pull/2474
|
||||
[#2503]: https://github.com/actix/actix-web/pull/2503
|
||||
[#2510]: https://github.com/actix/actix-web/pull/2510
|
||||
[#2546]: https://github.com/actix/actix-web/pull/2546
|
||||
[#2553]: https://github.com/actix/actix-web/pull/2553
|
||||
[#2555]: https://github.com/actix/actix-web/pull/2555
|
||||
|
||||
|
||||
<details>
|
||||
<summary>3.0.0 Pre-Releases</summary>
|
||||
|
||||
## 3.0.0-beta.21 - 2022-02-16
|
||||
- No significant changes since `3.0.0-beta.20`.
|
||||
|
||||
@@ -170,6 +267,7 @@
|
||||
|
||||
[#1813]: https://github.com/actix/actix-web/pull/1813
|
||||
|
||||
</details>
|
||||
|
||||
## 2.0.3 - 2020-11-29
|
||||
### Fixed
|
||||
|
@@ -1,11 +1,11 @@
|
||||
[package]
|
||||
name = "awc"
|
||||
version = "3.0.0-beta.21"
|
||||
version = "3.0.0"
|
||||
authors = [
|
||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||
"fakeshadow <24548779@qq.com>",
|
||||
]
|
||||
description = "Async HTTP and WebSocket client library built on the Actix ecosystem"
|
||||
description = "Async HTTP and WebSocket client library"
|
||||
keywords = ["actix", "http", "framework", "async", "web"]
|
||||
categories = [
|
||||
"network-programming",
|
||||
@@ -59,11 +59,11 @@ dangerous-h2c = []
|
||||
|
||||
[dependencies]
|
||||
actix-codec = "0.5"
|
||||
actix-service = "2.0.0"
|
||||
actix-http = { version = "3.0.0-rc.4", features = ["http2", "ws"] }
|
||||
actix-service = "2"
|
||||
actix-http = { version = "3", features = ["http2", "ws"] }
|
||||
actix-rt = { version = "2.1", default-features = false }
|
||||
actix-tls = { version = "3", features = ["connect", "uri"] }
|
||||
actix-utils = "3.0.0"
|
||||
actix-utils = "3"
|
||||
|
||||
ahash = "0.7"
|
||||
base64 = "0.13"
|
||||
@@ -93,13 +93,13 @@ tls-rustls = { package = "rustls", version = "0.20.0", optional = true, features
|
||||
trust-dns-resolver = { version = "0.20.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
actix-http = { version = "3.0.0-rc.4", features = ["openssl"] }
|
||||
actix-http = { version = "3", features = ["openssl"] }
|
||||
actix-http-test = { version = "3.0.0-beta.13", features = ["openssl"] }
|
||||
actix-server = "2"
|
||||
actix-test = { version = "0.1.0-beta.13", features = ["openssl", "rustls"] }
|
||||
actix-tls = { version = "3", features = ["openssl", "rustls"] }
|
||||
actix-utils = "3.0.0"
|
||||
actix-web = { version = "4.0.0-rc.3", features = ["openssl"] }
|
||||
actix-utils = "3"
|
||||
actix-web = { version = "4", features = ["openssl"] }
|
||||
|
||||
brotli = "3.3.3"
|
||||
const-str = "0.3"
|
||||
|
@@ -3,9 +3,9 @@
|
||||
> Async HTTP and WebSocket client library.
|
||||
|
||||
[](https://crates.io/crates/awc)
|
||||
[](https://docs.rs/awc/3.0.0-beta.21)
|
||||
[](https://docs.rs/awc/3.0.0)
|
||||

|
||||
[](https://deps.rs/crate/awc/3.0.0-beta.21)
|
||||
[](https://deps.rs/crate/awc/3.0.0)
|
||||
[](https://discord.gg/NWpN5mmg3x)
|
||||
|
||||
## Documentation & Resources
|
||||
|
@@ -246,7 +246,12 @@ where
|
||||
///
|
||||
/// The default limit size is 100.
|
||||
pub fn limit(mut self, limit: usize) -> Self {
|
||||
self.config.limit = limit;
|
||||
if limit == 0 {
|
||||
self.config.limit = u32::MAX as usize;
|
||||
} else {
|
||||
self.config.limit = limit;
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
|
@@ -83,12 +83,12 @@ where
|
||||
false
|
||||
};
|
||||
|
||||
framed.send((head, body.size()).into()).await?;
|
||||
|
||||
let mut pin_framed = Pin::new(&mut framed);
|
||||
|
||||
// special handle for EXPECT request.
|
||||
let (do_send, mut res_head) = if is_expect {
|
||||
pin_framed.send((head, body.size()).into()).await?;
|
||||
|
||||
let head = poll_fn(|cx| pin_framed.as_mut().poll_next(cx))
|
||||
.await
|
||||
.ok_or(ConnectError::Disconnected)??;
|
||||
@@ -97,13 +97,17 @@ where
|
||||
// and current head would be used as final response head.
|
||||
(head.status == StatusCode::CONTINUE, Some(head))
|
||||
} else {
|
||||
pin_framed.feed((head, body.size()).into()).await?;
|
||||
|
||||
(true, None)
|
||||
};
|
||||
|
||||
if do_send {
|
||||
// send request body
|
||||
match body.size() {
|
||||
BodySize::None | BodySize::Sized(0) => {}
|
||||
BodySize::None | BodySize::Sized(0) => {
|
||||
poll_fn(|cx| pin_framed.as_mut().flush(cx)).await?;
|
||||
}
|
||||
_ => send_body(body, pin_framed.as_mut()).await?,
|
||||
};
|
||||
|
||||
|
@@ -232,7 +232,7 @@ where
|
||||
None => {
|
||||
let (io, proto) = connector.call(req).await?;
|
||||
|
||||
// TODO: remove when http3 is added in support.
|
||||
// NOTE: remove when http3 is added in support.
|
||||
assert!(proto != Protocol::Http3);
|
||||
|
||||
if proto == Protocol::Http1 {
|
||||
|
@@ -30,17 +30,35 @@ pub type BoxConnectorService = Rc<
|
||||
|
||||
pub type BoxedSocket = Box<dyn ConnectionIo>;
|
||||
|
||||
/// Combined HTTP and WebSocket request type received by connection service.
|
||||
pub enum ConnectRequest {
|
||||
/// Standard HTTP request.
|
||||
///
|
||||
/// Contains the request head, body type, and optional pre-resolved socket address.
|
||||
Client(RequestHeadType, AnyBody, Option<net::SocketAddr>),
|
||||
|
||||
/// Tunnel used by WebSocket connection requests.
|
||||
///
|
||||
/// Contains the request head and optional pre-resolved socket address.
|
||||
Tunnel(RequestHead, Option<net::SocketAddr>),
|
||||
}
|
||||
|
||||
/// Combined HTTP response & WebSocket tunnel type returned from connection service.
|
||||
pub enum ConnectResponse {
|
||||
/// Standard HTTP response.
|
||||
Client(ClientResponse),
|
||||
|
||||
/// Tunnel used for WebSocket communication.
|
||||
///
|
||||
/// Contains response head and framed HTTP/1.1 codec.
|
||||
Tunnel(ResponseHead, Framed<BoxedSocket, ClientCodec>),
|
||||
}
|
||||
|
||||
impl ConnectResponse {
|
||||
/// Unwraps type into HTTP response.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if enum variant is not `Client`.
|
||||
pub fn into_client_response(self) -> ClientResponse {
|
||||
match self {
|
||||
ConnectResponse::Client(res) => res,
|
||||
@@ -50,6 +68,10 @@ impl ConnectResponse {
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwraps type into WebSocket tunnel response.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if enum variant is not `Tunnel`.
|
||||
pub fn into_tunnel_response(self) -> (ResponseHead, Framed<BoxedSocket, ClientCodec>) {
|
||||
match self {
|
||||
ConnectResponse::Tunnel(head, framed) => (head, framed),
|
||||
@@ -136,30 +158,37 @@ where
|
||||
ConnectRequestProj::Connection { fut, req } => {
|
||||
let connection = ready!(fut.poll(cx))?;
|
||||
let req = req.take().unwrap();
|
||||
|
||||
match req {
|
||||
ConnectRequest::Client(head, body, ..) => {
|
||||
// send request
|
||||
let fut = ConnectRequestFuture::Client {
|
||||
fut: connection.send_request(head, body),
|
||||
};
|
||||
|
||||
self.set(fut);
|
||||
}
|
||||
|
||||
ConnectRequest::Tunnel(head, ..) => {
|
||||
// send request
|
||||
let fut = ConnectRequestFuture::Tunnel {
|
||||
fut: connection.open_tunnel(RequestHeadType::from(head)),
|
||||
};
|
||||
|
||||
self.set(fut);
|
||||
}
|
||||
}
|
||||
|
||||
self.poll(cx)
|
||||
}
|
||||
|
||||
ConnectRequestProj::Client { fut } => {
|
||||
let (head, payload) = ready!(fut.as_mut().poll(cx))?;
|
||||
Poll::Ready(Ok(ConnectResponse::Client(ClientResponse::new(
|
||||
head, payload,
|
||||
))))
|
||||
}
|
||||
|
||||
ConnectRequestProj::Tunnel { fut } => {
|
||||
let (head, framed) = ready!(fut.as_mut().poll(cx))?;
|
||||
let framed = framed.into_map_io(|io| Box::new(io) as _);
|
||||
|
@@ -1,22 +1,25 @@
|
||||
//! `awc` is a HTTP and WebSocket client library built on the Actix ecosystem.
|
||||
//! `awc` is an asynchronous HTTP and WebSocket client library.
|
||||
//!
|
||||
//! # Making a GET request
|
||||
//! # `GET` Requests
|
||||
//! ```no_run
|
||||
//! # #[actix_rt::main]
|
||||
//! # async fn main() -> Result<(), awc::error::SendRequestError> {
|
||||
//! // create client
|
||||
//! let mut client = awc::Client::default();
|
||||
//! let response = client.get("http://www.rust-lang.org") // <- Create request builder
|
||||
//! .insert_header(("User-Agent", "Actix-web"))
|
||||
//! .send() // <- Send http request
|
||||
//! .await?;
|
||||
//!
|
||||
//! println!("Response: {:?}", response);
|
||||
//! // construct request
|
||||
//! let req = client.get("http://www.rust-lang.org")
|
||||
//! .insert_header(("User-Agent", "awc/3.0"));
|
||||
//!
|
||||
//! // send request and await response
|
||||
//! let res = req.send().await?;
|
||||
//! println!("Response: {:?}", res);
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Making POST requests
|
||||
//! ## Raw body contents
|
||||
//! # `POST` Requests
|
||||
//! ## Raw Body
|
||||
//! ```no_run
|
||||
//! # #[actix_rt::main]
|
||||
//! # async fn main() -> Result<(), awc::error::SendRequestError> {
|
||||
@@ -28,20 +31,6 @@
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Forms
|
||||
//! ```no_run
|
||||
//! # #[actix_rt::main]
|
||||
//! # async fn main() -> Result<(), awc::error::SendRequestError> {
|
||||
//! let params = [("foo", "bar"), ("baz", "quux")];
|
||||
//!
|
||||
//! let mut client = awc::Client::default();
|
||||
//! let response = client.post("http://httpbin.org/post")
|
||||
//! .send_form(¶ms)
|
||||
//! .await?;
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## JSON
|
||||
//! ```no_run
|
||||
//! # #[actix_rt::main]
|
||||
@@ -59,6 +48,20 @@
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## URL Encoded Form
|
||||
//! ```no_run
|
||||
//! # #[actix_rt::main]
|
||||
//! # async fn main() -> Result<(), awc::error::SendRequestError> {
|
||||
//! let params = [("foo", "bar"), ("baz", "quux")];
|
||||
//!
|
||||
//! let mut client = awc::Client::default();
|
||||
//! let response = client.post("http://httpbin.org/post")
|
||||
//! .send_form(¶ms)
|
||||
//! .await?;
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Response Compression
|
||||
//! All [official][iana-encodings] and common content encoding codecs are supported, optionally.
|
||||
//!
|
||||
@@ -76,11 +79,12 @@
|
||||
//!
|
||||
//! [iana-encodings]: https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#content-coding
|
||||
//!
|
||||
//! # WebSocket support
|
||||
//! # WebSockets
|
||||
//! ```no_run
|
||||
//! # #[actix_rt::main]
|
||||
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! use futures_util::{sink::SinkExt, stream::StreamExt};
|
||||
//! use futures_util::{sink::SinkExt as _, stream::StreamExt as _};
|
||||
//!
|
||||
//! let (_resp, mut connection) = awc::Client::new()
|
||||
//! .ws("ws://echo.websocket.org")
|
||||
//! .connect()
|
||||
@@ -89,8 +93,9 @@
|
||||
//! connection
|
||||
//! .send(awc::ws::Message::Text("Echo".into()))
|
||||
//! .await?;
|
||||
//!
|
||||
//! let response = connection.next().await.unwrap()?;
|
||||
//! # assert_eq!(response, awc::ws::Frame::Text("Echo".as_bytes().into()));
|
||||
//! assert_eq!(response, awc::ws::Frame::Text("Echo".into()));
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
|
@@ -161,7 +161,8 @@ where
|
||||
| StatusCode::SEE_OTHER
|
||||
| StatusCode::TEMPORARY_REDIRECT
|
||||
| StatusCode::PERMANENT_REDIRECT
|
||||
if *max_redirect_times > 0 =>
|
||||
if *max_redirect_times > 0
|
||||
&& res.headers().contains_key(header::LOCATION) =>
|
||||
{
|
||||
let reuse_body = res.head().status == StatusCode::TEMPORARY_REDIRECT
|
||||
|| res.head().status == StatusCode::PERMANENT_REDIRECT;
|
||||
@@ -245,26 +246,32 @@ where
|
||||
}
|
||||
|
||||
fn build_next_uri(res: &ClientResponse, prev_uri: &Uri) -> Result<Uri, SendRequestError> {
|
||||
let uri = res
|
||||
.headers()
|
||||
.get(header::LOCATION)
|
||||
.map(|value| {
|
||||
// try to parse the location to a full uri
|
||||
let uri = Uri::try_from(value.as_bytes())
|
||||
.map_err(|e| SendRequestError::Url(InvalidUrl::HttpError(e.into())))?;
|
||||
if uri.scheme().is_none() || uri.authority().is_none() {
|
||||
let uri = Uri::builder()
|
||||
.scheme(prev_uri.scheme().cloned().unwrap())
|
||||
.authority(prev_uri.authority().cloned().unwrap())
|
||||
.path_and_query(value.as_bytes())
|
||||
.build()?;
|
||||
Ok::<_, SendRequestError>(uri)
|
||||
} else {
|
||||
Ok(uri)
|
||||
}
|
||||
})
|
||||
// TODO: this error type is wrong.
|
||||
.ok_or(SendRequestError::Url(InvalidUrl::MissingScheme))??;
|
||||
// responses without this header are not processed
|
||||
let location = res.headers().get(header::LOCATION).unwrap();
|
||||
|
||||
// try to parse the location and resolve to a full URI but fall back to default if it fails
|
||||
let uri = Uri::try_from(location.as_bytes()).unwrap_or_else(|_| Uri::default());
|
||||
|
||||
let uri = if uri.scheme().is_none() || uri.authority().is_none() {
|
||||
let builder = Uri::builder()
|
||||
.scheme(prev_uri.scheme().cloned().unwrap())
|
||||
.authority(prev_uri.authority().cloned().unwrap());
|
||||
|
||||
// when scheme or authority is missing treat the location value as path and query
|
||||
// recover error where location does not have leading slash
|
||||
let path = if location.as_bytes().starts_with(b"/") {
|
||||
location.as_bytes().to_owned()
|
||||
} else {
|
||||
[b"/", location.as_bytes()].concat()
|
||||
};
|
||||
|
||||
builder
|
||||
.path_and_query(path)
|
||||
.build()
|
||||
.map_err(|err| SendRequestError::Url(InvalidUrl::HttpError(err)))?
|
||||
} else {
|
||||
uri
|
||||
};
|
||||
|
||||
Ok(uri)
|
||||
}
|
||||
@@ -287,10 +294,13 @@ mod tests {
|
||||
use actix_web::{web, App, Error, HttpRequest, HttpResponse};
|
||||
|
||||
use super::*;
|
||||
use crate::{http::header::HeaderValue, ClientBuilder};
|
||||
use crate::{
|
||||
http::{header::HeaderValue, StatusCode},
|
||||
ClientBuilder,
|
||||
};
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_basic_redirect() {
|
||||
async fn basic_redirect() {
|
||||
let client = ClientBuilder::new()
|
||||
.disable_redirects()
|
||||
.wrap(Redirect::new().max_redirect_times(10))
|
||||
@@ -315,6 +325,44 @@ mod tests {
|
||||
assert_eq!(res.status().as_u16(), 400);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn redirect_relative_without_leading_slash() {
|
||||
let client = ClientBuilder::new().finish();
|
||||
|
||||
let srv = actix_test::start(|| {
|
||||
App::new()
|
||||
.service(web::resource("/").route(web::to(|| async {
|
||||
HttpResponse::Found()
|
||||
.insert_header(("location", "abc/"))
|
||||
.finish()
|
||||
})))
|
||||
.service(
|
||||
web::resource("/abc/")
|
||||
.route(web::to(|| async { HttpResponse::Accepted().finish() })),
|
||||
)
|
||||
});
|
||||
|
||||
let res = client.get(srv.url("/")).send().await.unwrap();
|
||||
assert_eq!(res.status(), StatusCode::ACCEPTED);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn redirect_without_location() {
|
||||
let client = ClientBuilder::new()
|
||||
.disable_redirects()
|
||||
.wrap(Redirect::new().max_redirect_times(10))
|
||||
.finish();
|
||||
|
||||
let srv = actix_test::start(|| {
|
||||
App::new().service(web::resource("/").route(web::to(|| async {
|
||||
Ok::<_, Error>(HttpResponse::Found().finish())
|
||||
})))
|
||||
});
|
||||
|
||||
let res = client.get(srv.url("/")).send().await.unwrap();
|
||||
assert_eq!(res.status(), StatusCode::FOUND);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_redirect_limit() {
|
||||
let client = ClientBuilder::new()
|
||||
@@ -328,14 +376,14 @@ mod tests {
|
||||
.service(web::resource("/").route(web::to(|| async {
|
||||
Ok::<_, Error>(
|
||||
HttpResponse::Found()
|
||||
.append_header(("location", "/test"))
|
||||
.insert_header(("location", "/test"))
|
||||
.finish(),
|
||||
)
|
||||
})))
|
||||
.service(web::resource("/test").route(web::to(|| async {
|
||||
Ok::<_, Error>(
|
||||
HttpResponse::Found()
|
||||
.append_header(("location", "/test2"))
|
||||
.insert_header(("location", "/test2"))
|
||||
.finish(),
|
||||
)
|
||||
})))
|
||||
@@ -345,8 +393,15 @@ mod tests {
|
||||
});
|
||||
|
||||
let res = client.get(srv.url("/")).send().await.unwrap();
|
||||
|
||||
assert_eq!(res.status().as_u16(), 302);
|
||||
assert_eq!(res.status(), StatusCode::FOUND);
|
||||
assert_eq!(
|
||||
res.headers()
|
||||
.get(header::LOCATION)
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"/test2"
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
|
@@ -505,7 +505,7 @@ impl fmt::Debug for ClientRequest {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"\nClientRequest {:?} {}:{}",
|
||||
"\nClientRequest {:?} {} {}",
|
||||
self.head.version, self.head.method, self.head.uri
|
||||
)?;
|
||||
writeln!(f, " headers:")?;
|
||||
|
@@ -160,7 +160,7 @@ where
|
||||
///
|
||||
/// # Errors
|
||||
/// `Future` implementation returns error if:
|
||||
/// - content length is greater than [limit](JsonBody::limit) (default: 2 MiB)
|
||||
/// - content length is greater than [limit](ResponseBody::limit) (default: 2 MiB)
|
||||
///
|
||||
/// # Examples
|
||||
/// ```no_run
|
||||
|
Reference in New Issue
Block a user