Merge remote-tracking branch 'origin/master' into test-crate

This commit is contained in:
Rob Ede 2021-04-01 19:15:21 +01:00
commit 34d3c7a835
No known key found for this signature in database
GPG Key ID: 97C636207D3EF933
110 changed files with 1272 additions and 745 deletions

3
.cargo/config.toml Normal file
View File

@ -0,0 +1,3 @@
[alias]
chk = "hack check --workspace --all-features --tests --examples"
lint = "hack --clean-per-run clippy --workspace --tests --examples"

View File

@ -1,6 +1,10 @@
# Changes # Changes
## Unreleased - 2021-xx-xx ## Unreleased - 2021-xx-xx
### Added
* `Header` extractor for extracting common HTTP headers in handlers. [#2094]
* Added `TestServer::client_headers` method. [#2097]
### Fixed ### Fixed
* Double ampersand in Logger format is escaped correctly. [#2067] * Double ampersand in Logger format is escaped correctly. [#2067]
@ -12,11 +16,13 @@
* The `client` mod was removed. Clients should now use `awc` directly. * The `client` mod was removed. Clients should now use `awc` directly.
[871ca5e4](https://github.com/actix/actix-web/commit/871ca5e4ae2bdc22d1ea02701c2992fa8d04aed7) [871ca5e4](https://github.com/actix/actix-web/commit/871ca5e4ae2bdc22d1ea02701c2992fa8d04aed7)
* Integration testing was moved to new `actix-test` crate. Namely these items from the `test` * Integration testing was moved to new `actix-test` crate. Namely these items from the `test`
module: `TestServer`, `TestServerConfig`, `start`, `start_with`, and `unused_addr`. [#???] module: `TestServer`, `TestServerConfig`, `start`, `start_with`, and `unused_addr`. [#2112]
[#2067]: https://github.com/actix/actix-web/pull/2067 [#2067]: https://github.com/actix/actix-web/pull/2067
[#2093]: https://github.com/actix/actix-web/pull/2093 [#2093]: https://github.com/actix/actix-web/pull/2093
[#???]: https://github.com/actix/actix-web/pull/??? [#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
## 4.0.0-beta.4 - 2021-03-09 ## 4.0.0-beta.4 - 2021-03-09

View File

@ -80,7 +80,7 @@ actix-router = "0.2.7"
actix-rt = "2.2" actix-rt = "2.2"
actix-server = "2.0.0-beta.3" actix-server = "2.0.0-beta.3"
actix-service = "2.0.0-beta.4" actix-service = "2.0.0-beta.4"
actix-utils = "3.0.0-beta.2" actix-utils = "3.0.0-beta.4"
actix-tls = { version = "3.0.0-beta.5", default-features = false, optional = true } actix-tls = { version = "3.0.0-beta.5", default-features = false, optional = true }
actix-web-codegen = "0.5.0-beta.2" actix-web-codegen = "0.5.0-beta.2"
@ -93,6 +93,8 @@ either = "1.5.3"
encoding_rs = "0.8" encoding_rs = "0.8"
futures-core = { version = "0.3.7", default-features = false } futures-core = { version = "0.3.7", default-features = false }
futures-util = { version = "0.3.7", default-features = false } futures-util = { version = "0.3.7", default-features = false }
language-tags = "0.2"
once_cell = "1.5"
log = "0.4" log = "0.4"
mime = "0.3" mime = "0.3"
pin-project = "1.0.0" pin-project = "1.0.0"

View File

@ -19,12 +19,12 @@ path = "src/lib.rs"
[dependencies] [dependencies]
actix-web = { version = "4.0.0-beta.4", default-features = false } actix-web = { version = "4.0.0-beta.4", default-features = false }
actix-service = "2.0.0-beta.4" actix-service = "2.0.0-beta.4"
actix-utils = "3.0.0-beta.4"
askama_escape = "0.10" askama_escape = "0.10"
bitflags = "1" bitflags = "1"
bytes = "1" bytes = "1"
futures-core = { version = "0.3.7", default-features = false } futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
futures-util = { version = "0.3.7", default-features = false }
http-range = "0.1.4" http-range = "0.1.4"
derive_more = "0.99.5" derive_more = "0.99.5"
log = "0.4" log = "0.4"

View File

@ -1,6 +1,7 @@
use std::{cell::RefCell, fmt, io, path::PathBuf, rc::Rc}; use std::{cell::RefCell, fmt, io, path::PathBuf, rc::Rc};
use actix_service::{boxed, IntoServiceFactory, ServiceFactory, ServiceFactoryExt}; use actix_service::{boxed, IntoServiceFactory, ServiceFactory, ServiceFactoryExt};
use actix_utils::future::ok;
use actix_web::{ use actix_web::{
dev::{AppService, HttpServiceFactory, ResourceDef, ServiceRequest, ServiceResponse}, dev::{AppService, HttpServiceFactory, ResourceDef, ServiceRequest, ServiceResponse},
error::Error, error::Error,
@ -8,7 +9,7 @@ use actix_web::{
http::header::DispositionType, http::header::DispositionType,
HttpRequest, HttpRequest,
}; };
use futures_util::future::{ok, FutureExt, LocalBoxFuture}; use futures_core::future::LocalBoxFuture;
use crate::{ use crate::{
directory_listing, named, Directory, DirectoryRenderer, FilesService, HttpNewService, directory_listing, named, Directory, DirectoryRenderer, FilesService, HttpNewService,
@ -263,18 +264,18 @@ impl ServiceFactory<ServiceRequest> for Files {
}; };
if let Some(ref default) = *self.default.borrow() { if let Some(ref default) = *self.default.borrow() {
default let fut = default.new_service(());
.new_service(()) Box::pin(async {
.map(move |result| match result { match fut.await {
Ok(default) => { Ok(default) => {
srv.default = Some(default); srv.default = Some(default);
Ok(srv) Ok(srv)
} }
Err(_) => Err(()), Err(_) => Err(()),
}) }
.boxed_local() })
} else { } else {
ok(srv).boxed_local() Box::pin(ok(srv))
} }
} }
} }

View File

@ -65,6 +65,7 @@ mod tests {
}; };
use actix_service::ServiceFactory; use actix_service::ServiceFactory;
use actix_utils::future::ok;
use actix_web::{ use actix_web::{
guard, guard,
http::{ http::{
@ -76,7 +77,6 @@ mod tests {
web::{self, Bytes}, web::{self, Bytes},
App, HttpResponse, Responder, App, HttpResponse, Responder,
}; };
use futures_util::future::ok;
use super::*; use super::*;

View File

@ -3,8 +3,8 @@ use std::{
str::FromStr, str::FromStr,
}; };
use actix_utils::future::{ready, Ready};
use actix_web::{dev::Payload, FromRequest, HttpRequest}; use actix_web::{dev::Payload, FromRequest, HttpRequest};
use futures_util::future::{ready, Ready};
use crate::error::UriSegmentError; use crate::error::UriSegmentError;

View File

@ -1,6 +1,7 @@
use std::{fmt, io, path::PathBuf, rc::Rc}; use std::{fmt, io, path::PathBuf, rc::Rc};
use actix_service::Service; use actix_service::Service;
use actix_utils::future::ok;
use actix_web::{ use actix_web::{
dev::{ServiceRequest, ServiceResponse}, dev::{ServiceRequest, ServiceResponse},
error::Error, error::Error,
@ -8,7 +9,7 @@ use actix_web::{
http::{header, Method}, http::{header, Method},
HttpResponse, HttpResponse,
}; };
use futures_util::future::{ok, Either, LocalBoxFuture, Ready}; use futures_core::future::LocalBoxFuture;
use crate::{ use crate::{
named, Directory, DirectoryRenderer, FilesError, HttpService, MimeOverride, NamedFile, named, Directory, DirectoryRenderer, FilesError, HttpService, MimeOverride, NamedFile,
@ -29,19 +30,18 @@ pub struct FilesService {
pub(crate) hidden_files: bool, pub(crate) hidden_files: bool,
} }
type FilesServiceFuture = Either<
Ready<Result<ServiceResponse, Error>>,
LocalBoxFuture<'static, Result<ServiceResponse, Error>>,
>;
impl FilesService { impl FilesService {
fn handle_err(&self, e: io::Error, req: ServiceRequest) -> FilesServiceFuture { fn handle_err(
log::debug!("Failed to handle {}: {}", req.path(), e); &self,
err: io::Error,
req: ServiceRequest,
) -> LocalBoxFuture<'static, Result<ServiceResponse, Error>> {
log::debug!("error handling {}: {}", req.path(), err);
if let Some(ref default) = self.default { if let Some(ref default) = self.default {
Either::Right(default.call(req)) Box::pin(default.call(req))
} else { } else {
Either::Left(ok(req.error_response(e))) Box::pin(ok(req.error_response(err)))
} }
} }
} }
@ -55,7 +55,7 @@ impl fmt::Debug for FilesService {
impl Service<ServiceRequest> for FilesService { impl Service<ServiceRequest> for FilesService {
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
type Future = FilesServiceFuture; type Future = LocalBoxFuture<'static, Result<ServiceResponse, Error>>;
actix_service::always_ready!(); actix_service::always_ready!();
@ -69,7 +69,7 @@ impl Service<ServiceRequest> for FilesService {
}; };
if !is_method_valid { if !is_method_valid {
return Either::Left(ok(req.into_response( return Box::pin(ok(req.into_response(
actix_web::HttpResponse::MethodNotAllowed() actix_web::HttpResponse::MethodNotAllowed()
.insert_header(header::ContentType(mime::TEXT_PLAIN_UTF_8)) .insert_header(header::ContentType(mime::TEXT_PLAIN_UTF_8))
.body("Request did not meet this resource's requirements."), .body("Request did not meet this resource's requirements."),
@ -79,13 +79,13 @@ impl Service<ServiceRequest> for FilesService {
let real_path = let real_path =
match PathBufWrap::parse_path(req.match_info().path(), self.hidden_files) { match PathBufWrap::parse_path(req.match_info().path(), self.hidden_files) {
Ok(item) => item, Ok(item) => item,
Err(e) => return Either::Left(ok(req.error_response(e))), Err(e) => return Box::pin(ok(req.error_response(e))),
}; };
// full file path // full file path
let path = match self.directory.join(&real_path).canonicalize() { let path = match self.directory.join(&real_path).canonicalize() {
Ok(path) => path, Ok(path) => path,
Err(e) => return self.handle_err(e, req), Err(err) => return Box::pin(self.handle_err(err, req)),
}; };
if path.is_dir() { if path.is_dir() {
@ -93,7 +93,7 @@ impl Service<ServiceRequest> for FilesService {
if self.redirect_to_slash && !req.path().ends_with('/') { if self.redirect_to_slash && !req.path().ends_with('/') {
let redirect_to = format!("{}/", req.path()); let redirect_to = format!("{}/", req.path());
return Either::Left(ok(req.into_response( return Box::pin(ok(req.into_response(
HttpResponse::Found() HttpResponse::Found()
.insert_header((header::LOCATION, redirect_to)) .insert_header((header::LOCATION, redirect_to))
.body("") .body("")
@ -114,9 +114,9 @@ impl Service<ServiceRequest> for FilesService {
let (req, _) = req.into_parts(); let (req, _) = req.into_parts();
let res = named_file.into_response(&req); let res = named_file.into_response(&req);
Either::Left(ok(ServiceResponse::new(req, res))) Box::pin(ok(ServiceResponse::new(req, res)))
} }
Err(e) => self.handle_err(e, req), Err(err) => self.handle_err(err, req),
} }
} else if self.show_index { } else if self.show_index {
let dir = Directory::new(self.directory.clone(), path); let dir = Directory::new(self.directory.clone(), path);
@ -124,12 +124,12 @@ impl Service<ServiceRequest> for FilesService {
let (req, _) = req.into_parts(); let (req, _) = req.into_parts();
let x = (self.renderer)(&dir, &req); let x = (self.renderer)(&dir, &req);
match x { Box::pin(match x {
Ok(resp) => Either::Left(ok(resp)), Ok(resp) => ok(resp),
Err(e) => Either::Left(ok(ServiceResponse::from_err(e, req))), Err(err) => ok(ServiceResponse::from_err(err, req)),
} })
} else { } else {
Either::Left(ok(ServiceResponse::from_err( Box::pin(ok(ServiceResponse::from_err(
FilesError::IsDirectory, FilesError::IsDirectory,
req.into_parts().0, req.into_parts().0,
))) )))
@ -145,9 +145,9 @@ impl Service<ServiceRequest> for FilesService {
let (req, _) = req.into_parts(); let (req, _) = req.into_parts();
let res = named_file.into_response(&req); let res = named_file.into_response(&req);
Either::Left(ok(ServiceResponse::new(req, res))) Box::pin(ok(ServiceResponse::new(req, res)))
} }
Err(e) => self.handle_err(e, req), Err(err) => self.handle_err(err, req),
} }
} }
} }

View File

@ -1,7 +1,8 @@
# Changes # Changes
## Unreleased - 2021-xx-xx ## Unreleased - 2021-xx-xx
### Added
* Added `TestServer::client_headers` method. [#2097]
## 3.0.0-beta.3 - 2021-03-09 ## 3.0.0-beta.3 - 2021-03-09
* No notable changes. * No notable changes.

View File

@ -32,7 +32,7 @@ openssl = ["tls-openssl", "awc/openssl"]
actix-service = "2.0.0-beta.4" actix-service = "2.0.0-beta.4"
actix-codec = "0.4.0-beta.1" actix-codec = "0.4.0-beta.1"
actix-tls = "3.0.0-beta.5" actix-tls = "3.0.0-beta.5"
actix-utils = "3.0.0-beta.2" actix-utils = "3.0.0-beta.4"
actix-rt = "2.2" actix-rt = "2.2"
actix-server = "2.0.0-beta.3" actix-server = "2.0.0-beta.3"
awc = { version = "3.0.0-beta.3", default-features = false } awc = { version = "3.0.0-beta.3", default-features = false }

View File

@ -13,7 +13,9 @@ use std::{net, thread, time};
use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_rt::{net::TcpStream, System}; use actix_rt::{net::TcpStream, System};
use actix_server::{Server, ServiceFactory}; use actix_server::{Server, ServiceFactory};
use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connector}; use awc::{
error::PayloadError, http::HeaderMap, ws, Client, ClientRequest, ClientResponse, Connector,
};
use bytes::Bytes; use bytes::Bytes;
use futures_core::stream::Stream; use futures_core::stream::Stream;
use http::Method; use http::Method;
@ -248,6 +250,14 @@ impl TestServer {
self.ws_at("/").await self.ws_at("/").await
} }
/// Get default HeaderMap of Client.
///
/// Returns Some(&mut HeaderMap) when Client object is unique
/// (No other clone of client exists at the same time).
pub fn client_headers(&mut self) -> Option<&mut HeaderMap> {
self.client.headers()
}
/// Stop HTTP server /// Stop HTTP server
fn stop(&mut self) { fn stop(&mut self) {
self.system.stop(); self.system.stop();

View File

@ -9,8 +9,14 @@
### Changed ### Changed
* `client::Connector` type now only have one generic type for `actix_service::Service`. [#2063] * `client::Connector` type now only have one generic type for `actix_service::Service`. [#2063]
### Removed
* Common HTTP headers were moved into actix-web. [2094]
* `ResponseError` impl for `actix_utils::timeout::TimeoutError`. [#2127]
[#2063]: https://github.com/actix/actix-web/pull/2063 [#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
[#2127]: https://github.com/actix/actix-web/pull/2127
## 3.0.0-beta.4 - 2021-03-08 ## 3.0.0-beta.4 - 2021-03-08

View File

@ -46,7 +46,7 @@ trust-dns = ["trust-dns-resolver"]
[dependencies] [dependencies]
actix-service = "2.0.0-beta.4" actix-service = "2.0.0-beta.4"
actix-codec = "0.4.0-beta.1" actix-codec = "0.4.0-beta.1"
actix-utils = "3.0.0-beta.2" actix-utils = "3.0.0-beta.4"
actix-rt = "2.2" actix-rt = "2.2"
actix-tls = "3.0.0-beta.5" actix-tls = "3.0.0-beta.5"
@ -65,11 +65,13 @@ http = "0.2.2"
httparse = "1.3" httparse = "1.3"
itoa = "0.4" itoa = "0.4"
language-tags = "0.2" language-tags = "0.2"
local-channel = "0.1"
once_cell = "1.5" once_cell = "1.5"
log = "0.4" log = "0.4"
mime = "0.3" mime = "0.3"
percent-encoding = "2.1" percent-encoding = "2.1"
pin-project = "1.0.0" pin-project = "1.0.0"
pin-project-lite = "0.2"
rand = "0.8" rand = "0.8"
regex = "1.3" regex = "1.3"
serde = "1.0" serde = "1.0"

View File

@ -2,7 +2,7 @@ use std::{env, io};
use actix_http::{HttpService, Response}; use actix_http::{HttpService, Response};
use actix_server::Server; use actix_server::Server;
use futures_util::future; use actix_utils::future;
use http::header::HeaderValue; use http::header::HeaderValue;
use log::info; use log::info;

View File

@ -20,8 +20,9 @@ mod tests {
use std::pin::Pin; use std::pin::Pin;
use actix_rt::pin; use actix_rt::pin;
use actix_utils::future::poll_fn;
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures_util::{future::poll_fn, stream}; use futures_util::stream;
use super::*; use super::*;

View File

@ -63,11 +63,9 @@ where
X: ServiceFactory<Request, Config = (), Response = Request>, X: ServiceFactory<Request, Config = (), Response = Request>,
X::Error: Into<Error>, X::Error: Into<Error>,
X::InitError: fmt::Debug, X::InitError: fmt::Debug,
<X::Service as Service<Request>>::Future: 'static,
U: ServiceFactory<(Request, Framed<T, Codec>), Config = (), Response = ()>, U: ServiceFactory<(Request, Framed<T, Codec>), Config = (), Response = ()>,
U::Error: fmt::Display, U::Error: fmt::Display,
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
<U::Service as Service<(Request, Framed<T, Codec>)>>::Future: 'static,
{ {
/// Set server keep-alive setting. /// Set server keep-alive setting.
/// ///
@ -127,7 +125,6 @@ where
X1: ServiceFactory<Request, Config = (), Response = Request>, X1: ServiceFactory<Request, Config = (), Response = Request>,
X1::Error: Into<Error>, X1::Error: Into<Error>,
X1::InitError: fmt::Debug, X1::InitError: fmt::Debug,
<X1::Service as Service<Request>>::Future: 'static,
{ {
HttpServiceBuilder { HttpServiceBuilder {
keep_alive: self.keep_alive, keep_alive: self.keep_alive,
@ -152,7 +149,6 @@ where
U1: ServiceFactory<(Request, Framed<T, Codec>), Config = (), Response = ()>, U1: ServiceFactory<(Request, Framed<T, Codec>), Config = (), Response = ()>,
U1::Error: fmt::Display, U1::Error: fmt::Display,
U1::InitError: fmt::Debug, U1::InitError: fmt::Debug,
<U1::Service as Service<(Request, Framed<T, Codec>)>>::Future: 'static,
{ {
HttpServiceBuilder { HttpServiceBuilder {
keep_alive: self.keep_alive, keep_alive: self.keep_alive,
@ -211,7 +207,6 @@ where
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>> + 'static, S::Response: Into<Response<B>> + 'static,
<S::Service as Service<Request>>::Future: 'static,
{ {
let cfg = ServiceConfig::new( let cfg = ServiceConfig::new(
self.keep_alive, self.keep_alive,
@ -233,7 +228,6 @@ where
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>> + 'static, S::Response: Into<Response<B>> + 'static,
<S::Service as Service<Request>>::Future: 'static,
{ {
let cfg = ServiceConfig::new( let cfg = ServiceConfig::new(
self.keep_alive, self.keep_alive,

View File

@ -5,10 +5,11 @@ use std::{
}; };
use actix_codec::Framed; use actix_codec::Framed;
use actix_utils::future::poll_fn;
use bytes::buf::BufMut; use bytes::buf::BufMut;
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures_core::Stream; use futures_core::{ready, Stream};
use futures_util::{future::poll_fn, SinkExt as _}; use futures_util::SinkExt as _;
use crate::error::PayloadError; use crate::error::PayloadError;
use crate::h1; use crate::h1;
@ -17,7 +18,7 @@ use crate::http::{
StatusCode, StatusCode,
}; };
use crate::message::{RequestHeadType, ResponseHead}; use crate::message::{RequestHeadType, ResponseHead};
use crate::payload::{Payload, PayloadStream}; use crate::payload::Payload;
use super::connection::{ConnectionIo, H1Connection}; use super::connection::{ConnectionIo, H1Connection};
use super::error::{ConnectError, SendRequestError}; use super::error::{ConnectError, SendRequestError};
@ -122,10 +123,7 @@ where
Ok((head, Payload::None)) Ok((head, Payload::None))
} }
_ => { _ => Ok((head, Payload::Stream(Box::pin(PlStream::new(framed))))),
let pl: PayloadStream = Box::pin(PlStream::new(framed));
Ok((head, pl.into()))
}
} }
} }
@ -194,21 +192,16 @@ where
} }
#[pin_project::pin_project] #[pin_project::pin_project]
pub(crate) struct PlStream<Io: ConnectionIo> pub(crate) struct PlStream<Io: ConnectionIo> {
where
Io: ConnectionIo,
{
#[pin] #[pin]
framed: Option<Framed<H1Connection<Io>, h1::ClientPayloadCodec>>, framed: Framed<H1Connection<Io>, h1::ClientPayloadCodec>,
} }
impl<Io: ConnectionIo> PlStream<Io> { impl<Io: ConnectionIo> PlStream<Io> {
fn new(framed: Framed<H1Connection<Io>, h1::ClientCodec>) -> Self { fn new(framed: Framed<H1Connection<Io>, h1::ClientCodec>) -> Self {
let framed = framed.into_map_codec(|codec| codec.into_payload_codec()); let framed = framed.into_map_codec(|codec| codec.into_payload_codec());
PlStream { PlStream { framed }
framed: Some(framed),
}
} }
} }
@ -219,20 +212,16 @@ impl<Io: ConnectionIo> Stream for PlStream<Io> {
self: Pin<&mut Self>, self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
) -> Poll<Option<Self::Item>> { ) -> Poll<Option<Self::Item>> {
let mut framed = self.project().framed.as_pin_mut().unwrap(); let mut this = self.project();
match framed.as_mut().next_item(cx)? { match ready!(this.framed.as_mut().next_item(cx)?) {
Poll::Pending => Poll::Pending, Some(Some(chunk)) => Poll::Ready(Some(Ok(chunk))),
Poll::Ready(Some(chunk)) => { Some(None) => {
if let Some(chunk) = chunk { let keep_alive = this.framed.codec_ref().keepalive();
Poll::Ready(Some(Ok(chunk))) this.framed.io_mut().on_release(keep_alive);
} else { Poll::Ready(None)
let keep_alive = framed.codec_ref().keepalive();
framed.io_mut().on_release(keep_alive);
Poll::Ready(None)
}
} }
Poll::Ready(None) => Poll::Ready(None), None => Poll::Ready(None),
} }
} }
} }

View File

@ -1,7 +1,7 @@
use std::future::Future; use std::future::Future;
use actix_utils::future::poll_fn;
use bytes::Bytes; use bytes::Bytes;
use futures_util::future::poll_fn;
use h2::{ use h2::{
client::{Builder, Connection, SendRequest}, client::{Builder, Connection, SendRequest},
SendStream, SendStream,

View File

@ -6,9 +6,6 @@ use std::str::Utf8Error;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use std::{fmt, io, result}; use std::{fmt, io, result};
use actix_codec::{Decoder, Encoder};
use actix_utils::dispatcher::DispatcherError as FramedDispatcherError;
use actix_utils::timeout::TimeoutError;
use bytes::BytesMut; use bytes::BytesMut;
use derive_more::{Display, From}; use derive_more::{Display, From};
use http::uri::InvalidUri; use http::uri::InvalidUri;
@ -148,19 +145,6 @@ impl From<ResponseBuilder> for Error {
} }
} }
/// Inspects the underlying enum and returns an appropriate status code.
///
/// If the variant is [`TimeoutError::Service`], the error code of the service is returned.
/// Otherwise, [`StatusCode::GATEWAY_TIMEOUT`] is returned.
impl<E: ResponseError> ResponseError for TimeoutError<E> {
fn status_code(&self) -> StatusCode {
match self {
TimeoutError::Service(e) => e.status_code(),
TimeoutError::Timeout => StatusCode::GATEWAY_TIMEOUT,
}
}
}
#[derive(Debug, Display)] #[derive(Debug, Display)]
#[display(fmt = "UnknownError")] #[display(fmt = "UnknownError")]
struct UnitError; struct UnitError;
@ -469,14 +453,6 @@ impl ResponseError for ContentTypeError {
} }
} }
impl<E, U: Encoder<I> + Decoder, I> ResponseError for FramedDispatcherError<E, U, I>
where
E: fmt::Debug + fmt::Display,
<U as Encoder<I>>::Error: fmt::Debug,
<U as Decoder>::Error: fmt::Debug,
{
}
/// Helper type that can wrap any error and generate custom response. /// Helper type that can wrap any error and generate custom response.
/// ///
/// In following example any `io::Error` will be converted into "BAD REQUEST" /// In following example any `io::Error` will be converted into "BAD REQUEST"

View File

@ -951,7 +951,8 @@ mod tests {
use std::str; use std::str;
use actix_service::fn_service; use actix_service::fn_service;
use futures_util::future::{lazy, ready, Ready}; use actix_utils::future::{ready, Ready};
use futures_util::future::lazy;
use super::*; use super::*;
use crate::{ use crate::{

View File

@ -1,5 +1,5 @@
use actix_service::{Service, ServiceFactory}; use actix_service::{Service, ServiceFactory};
use futures_util::future::{ready, Ready}; use actix_utils::future::{ready, Ready};
use crate::error::Error; use crate::error::Error;
use crate::request::Request; use crate::request::Request;

View File

@ -263,7 +263,7 @@ impl Inner {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use futures_util::future::poll_fn; use actix_utils::future::poll_fn;
#[actix_rt::test] #[actix_rt::test]
async fn test_unread_data() { async fn test_unread_data() {

View File

@ -6,15 +6,15 @@ use std::{fmt, net};
use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_rt::net::TcpStream; use actix_rt::net::TcpStream;
use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory}; use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory};
use futures_core::{future::LocalBoxFuture, ready}; use actix_utils::future::ready;
use futures_util::future::ready; use futures_core::future::LocalBoxFuture;
use crate::body::MessageBody; use crate::body::MessageBody;
use crate::config::ServiceConfig; use crate::config::ServiceConfig;
use crate::error::{DispatchError, Error}; use crate::error::{DispatchError, Error};
use crate::request::Request; use crate::request::Request;
use crate::response::Response; use crate::response::Response;
use crate::service::HttpFlow; use crate::service::HttpServiceHandler;
use crate::{ConnectCallback, OnConnectData}; use crate::{ConnectCallback, OnConnectData};
use super::codec::Codec; use super::codec::Codec;
@ -315,47 +315,10 @@ where
} }
/// `Service` implementation for HTTP/1 transport /// `Service` implementation for HTTP/1 transport
pub struct H1ServiceHandler<T, S, B, X, U> pub type H1ServiceHandler<T, S, B, X, U> = HttpServiceHandler<T, S, B, X, U>;
where
S: Service<Request>,
X: Service<Request>,
U: Service<(Request, Framed<T, Codec>)>,
{
flow: Rc<HttpFlow<S, X, U>>,
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
cfg: ServiceConfig,
_phantom: PhantomData<B>,
}
impl<T, S, B, X, U> H1ServiceHandler<T, S, B, X, U>
where
S: Service<Request>,
S::Error: Into<Error>,
S::Response: Into<Response<B>>,
B: MessageBody,
X: Service<Request, Response = Request>,
X::Error: Into<Error>,
U: Service<(Request, Framed<T, Codec>), Response = ()>,
U::Error: fmt::Display,
{
fn new(
cfg: ServiceConfig,
service: S,
expect: X,
upgrade: Option<U>,
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
) -> H1ServiceHandler<T, S, B, X, U> {
H1ServiceHandler {
flow: HttpFlow::new(service, expect, upgrade),
cfg,
on_connect_ext,
_phantom: PhantomData,
}
}
}
impl<T, S, B, X, U> Service<(T, Option<net::SocketAddr>)> impl<T, S, B, X, U> Service<(T, Option<net::SocketAddr>)>
for H1ServiceHandler<T, S, B, X, U> for HttpServiceHandler<T, S, B, X, U>
where where
T: AsyncRead + AsyncWrite + Unpin, T: AsyncRead + AsyncWrite + Unpin,
S: Service<Request>, S: Service<Request>,
@ -372,27 +335,10 @@ where
type Future = Dispatcher<T, S, B, X, U>; type Future = Dispatcher<T, S, B, X, U>;
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
ready!(self.flow.expect.poll_ready(cx)).map_err(|e| { self._poll_ready(cx).map_err(|e| {
let e = e.into(); log::error!("HTTP/1 service readiness error: {:?}", e);
log::error!("Http expect service readiness error: {:?}", e);
DispatchError::Service(e) DispatchError::Service(e)
})?; })
if let Some(ref upg) = self.flow.upgrade {
ready!(upg.poll_ready(cx)).map_err(|e| {
let e = e.into();
log::error!("Http upgrade service readiness error: {:?}", e);
DispatchError::Service(e)
})?;
};
ready!(self.flow.service.poll_ready(cx)).map_err(|e| {
let e = e.into();
log::error!("Http service readiness error: {:?}", e);
DispatchError::Service(e)
})?;
Poll::Ready(Ok(()))
} }
fn call(&self, (io, addr): (T, Option<net::SocketAddr>)) -> Self::Future { fn call(&self, (io, addr): (T, Option<net::SocketAddr>)) -> Self::Future {

View File

@ -10,9 +10,9 @@ use actix_service::{
fn_factory, fn_service, pipeline_factory, IntoServiceFactory, Service, fn_factory, fn_service, pipeline_factory, IntoServiceFactory, Service,
ServiceFactory, ServiceFactory,
}; };
use actix_utils::future::ready;
use bytes::Bytes; use bytes::Bytes;
use futures_core::{future::LocalBoxFuture, ready}; use futures_core::{future::LocalBoxFuture, ready};
use futures_util::future::ready;
use h2::server::{handshake, Handshake}; use h2::server::{handshake, Handshake};
use log::error; use log::error;

View File

@ -1,9 +1,6 @@
//! Typed HTTP headers, pre-defined `HeaderName`s, traits for parsing and conversion, and other //! Typed HTTP headers, pre-defined `HeaderName`s, traits for parsing and conversion, and other
//! header utility methods. //! header utility methods.
use std::fmt;
use bytes::{Bytes, BytesMut};
use percent_encoding::{AsciiSet, CONTROLS}; use percent_encoding::{AsciiSet, CONTROLS};
pub use http::header::*; pub use http::header::*;
@ -16,11 +13,9 @@ mod into_pair;
mod into_value; mod into_value;
mod utils; mod utils;
mod common;
pub(crate) mod map; pub(crate) mod map;
mod shared; mod shared;
pub use self::common::*;
#[doc(hidden)] #[doc(hidden)]
pub use self::shared::*; pub use self::shared::*;
@ -41,34 +36,6 @@ pub trait Header: IntoHeaderValue {
fn parse<T: HttpMessage>(msg: &T) -> Result<Self, ParseError>; fn parse<T: HttpMessage>(msg: &T) -> Result<Self, ParseError>;
} }
#[derive(Debug, Default)]
pub(crate) struct Writer {
buf: BytesMut,
}
impl Writer {
fn new() -> Writer {
Writer::default()
}
fn take(&mut self) -> Bytes {
self.buf.split().freeze()
}
}
impl fmt::Write for Writer {
#[inline]
fn write_str(&mut self, s: &str) -> fmt::Result {
self.buf.extend_from_slice(s.as_bytes());
Ok(())
}
#[inline]
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
fmt::write(self, args)
}
}
/// Convert `http::HeaderMap` to our `HeaderMap`. /// Convert `http::HeaderMap` to our `HeaderMap`.
impl From<http::HeaderMap> for HeaderMap { impl From<http::HeaderMap> for HeaderMap {
fn from(mut map: http::HeaderMap) -> HeaderMap { fn from(mut map: http::HeaderMap) -> HeaderMap {

View File

@ -1,18 +1,20 @@
use std::fmt::{self, Display}; use std::{
use std::io::Write; fmt,
use std::str::FromStr; io::Write,
use std::time::{SystemTime, UNIX_EPOCH}; str::FromStr,
time::{SystemTime, UNIX_EPOCH},
};
use bytes::buf::BufMut; use bytes::buf::BufMut;
use bytes::BytesMut; use bytes::BytesMut;
use http::header::{HeaderValue, InvalidHeaderValue}; use http::header::{HeaderValue, InvalidHeaderValue};
use time::{offset, OffsetDateTime, PrimitiveDateTime}; use time::{OffsetDateTime, PrimitiveDateTime, UtcOffset};
use crate::error::ParseError; use crate::error::ParseError;
use crate::header::IntoHeaderValue; use crate::header::IntoHeaderValue;
use crate::time_parser; use crate::time_parser;
/// A timestamp with HTTP formatting and parsing /// A timestamp with HTTP formatting and parsing.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct HttpDate(OffsetDateTime); pub struct HttpDate(OffsetDateTime);
@ -27,18 +29,12 @@ impl FromStr for HttpDate {
} }
} }
impl Display for HttpDate { impl fmt::Display for HttpDate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0.format("%a, %d %b %Y %H:%M:%S GMT"), f) fmt::Display::fmt(&self.0.format("%a, %d %b %Y %H:%M:%S GMT"), f)
} }
} }
impl From<OffsetDateTime> for HttpDate {
fn from(dt: OffsetDateTime) -> HttpDate {
HttpDate(dt)
}
}
impl From<SystemTime> for HttpDate { impl From<SystemTime> for HttpDate {
fn from(sys: SystemTime) -> HttpDate { fn from(sys: SystemTime) -> HttpDate {
HttpDate(PrimitiveDateTime::from(sys).assume_utc()) HttpDate(PrimitiveDateTime::from(sys).assume_utc())
@ -54,7 +50,7 @@ impl IntoHeaderValue for HttpDate {
wrt, wrt,
"{}", "{}",
self.0 self.0
.to_offset(offset!(UTC)) .to_offset(UtcOffset::UTC)
.format("%a, %d %b %Y %H:%M:%S GMT") .format("%a, %d %b %Y %H:%M:%S GMT")
) )
.unwrap(); .unwrap();

View File

@ -1,15 +1,13 @@
//! Originally taken from `hyper::header::shared`. //! Originally taken from `hyper::header::shared`.
mod charset; mod charset;
mod encoding; mod content_encoding;
mod entity;
mod extended; mod extended;
mod httpdate; mod httpdate;
mod quality_item; mod quality_item;
pub use self::charset::Charset; pub use self::charset::Charset;
pub use self::encoding::Encoding; pub use self::content_encoding::ContentEncoding;
pub use self::entity::EntityTag;
pub use self::extended::{parse_extended_value, ExtendedValue}; pub use self::extended::{parse_extended_value, ExtendedValue};
pub use self::httpdate::HttpDate; pub use self::httpdate::HttpDate;
pub use self::quality_item::{q, qitem, Quality, QualityItem}; pub use self::quality_item::{q, qitem, Quality, QualityItem};

View File

@ -193,21 +193,69 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::super::encoding::*;
use super::*; use super::*;
// copy of encoding from actix-web headers
#[derive(Clone, PartialEq, Debug)]
pub enum Encoding {
Chunked,
Brotli,
Gzip,
Deflate,
Compress,
Identity,
Trailers,
EncodingExt(String),
}
impl fmt::Display for Encoding {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Encoding::*;
f.write_str(match *self {
Chunked => "chunked",
Brotli => "br",
Gzip => "gzip",
Deflate => "deflate",
Compress => "compress",
Identity => "identity",
Trailers => "trailers",
EncodingExt(ref s) => s.as_ref(),
})
}
}
impl str::FromStr for Encoding {
type Err = crate::error::ParseError;
fn from_str(s: &str) -> Result<Encoding, crate::error::ParseError> {
use Encoding::*;
match s {
"chunked" => Ok(Chunked),
"br" => Ok(Brotli),
"deflate" => Ok(Deflate),
"gzip" => Ok(Gzip),
"compress" => Ok(Compress),
"identity" => Ok(Identity),
"trailers" => Ok(Trailers),
_ => Ok(EncodingExt(s.to_owned())),
}
}
}
#[test] #[test]
fn test_quality_item_fmt_q_1() { fn test_quality_item_fmt_q_1() {
use Encoding::*;
let x = qitem(Chunked); let x = qitem(Chunked);
assert_eq!(format!("{}", x), "chunked"); assert_eq!(format!("{}", x), "chunked");
} }
#[test] #[test]
fn test_quality_item_fmt_q_0001() { fn test_quality_item_fmt_q_0001() {
use Encoding::*;
let x = QualityItem::new(Chunked, Quality(1)); let x = QualityItem::new(Chunked, Quality(1));
assert_eq!(format!("{}", x), "chunked; q=0.001"); assert_eq!(format!("{}", x), "chunked; q=0.001");
} }
#[test] #[test]
fn test_quality_item_fmt_q_05() { fn test_quality_item_fmt_q_05() {
use Encoding::*;
// Custom value // Custom value
let x = QualityItem { let x = QualityItem {
item: EncodingExt("identity".to_owned()), item: EncodingExt("identity".to_owned()),
@ -218,6 +266,7 @@ mod tests {
#[test] #[test]
fn test_quality_item_fmt_q_0() { fn test_quality_item_fmt_q_0() {
use Encoding::*;
// Custom value // Custom value
let x = QualityItem { let x = QualityItem {
item: EncodingExt("identity".to_owned()), item: EncodingExt("identity".to_owned()),
@ -228,6 +277,7 @@ mod tests {
#[test] #[test]
fn test_quality_item_from_str1() { fn test_quality_item_from_str1() {
use Encoding::*;
let x: Result<QualityItem<Encoding>, _> = "chunked".parse(); let x: Result<QualityItem<Encoding>, _> = "chunked".parse();
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
@ -237,8 +287,10 @@ mod tests {
} }
); );
} }
#[test] #[test]
fn test_quality_item_from_str2() { fn test_quality_item_from_str2() {
use Encoding::*;
let x: Result<QualityItem<Encoding>, _> = "chunked; q=1".parse(); let x: Result<QualityItem<Encoding>, _> = "chunked; q=1".parse();
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
@ -248,8 +300,10 @@ mod tests {
} }
); );
} }
#[test] #[test]
fn test_quality_item_from_str3() { fn test_quality_item_from_str3() {
use Encoding::*;
let x: Result<QualityItem<Encoding>, _> = "gzip; q=0.5".parse(); let x: Result<QualityItem<Encoding>, _> = "gzip; q=0.5".parse();
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
@ -259,8 +313,10 @@ mod tests {
} }
); );
} }
#[test] #[test]
fn test_quality_item_from_str4() { fn test_quality_item_from_str4() {
use Encoding::*;
let x: Result<QualityItem<Encoding>, _> = "gzip; q=0.273".parse(); let x: Result<QualityItem<Encoding>, _> = "gzip; q=0.273".parse();
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
@ -270,16 +326,19 @@ mod tests {
} }
); );
} }
#[test] #[test]
fn test_quality_item_from_str5() { fn test_quality_item_from_str5() {
let x: Result<QualityItem<Encoding>, _> = "gzip; q=0.2739999".parse(); let x: Result<QualityItem<Encoding>, _> = "gzip; q=0.2739999".parse();
assert!(x.is_err()); assert!(x.is_err());
} }
#[test] #[test]
fn test_quality_item_from_str6() { fn test_quality_item_from_str6() {
let x: Result<QualityItem<Encoding>, _> = "gzip; q=2".parse(); let x: Result<QualityItem<Encoding>, _> = "gzip; q=2".parse();
assert!(x.is_err()); assert!(x.is_err());
} }
#[test] #[test]
fn test_quality_item_ordering() { fn test_quality_item_ordering() {
let x: QualityItem<Encoding> = "gzip; q=0.5".parse().ok().unwrap(); let x: QualityItem<Encoding> = "gzip; q=0.5".parse().ok().unwrap();

View File

@ -1,7 +1,6 @@
use std::{fmt, str::FromStr}; use std::{fmt, str::FromStr};
use http::HeaderValue; use super::HeaderValue;
use crate::{error::ParseError, header::HTTP_VALUE}; use crate::{error::ParseError, header::HTTP_VALUE};
/// Reads a comma-delimited raw header into a Vec. /// Reads a comma-delimited raw header into a Vec.

View File

@ -359,10 +359,10 @@ impl ResponseBuilder {
/// ///
/// ``` /// ```
/// # use actix_http::Response; /// # use actix_http::Response;
/// use actix_http::http::header::ContentType; /// use actix_http::http::header;
/// ///
/// Response::Ok() /// Response::Ok()
/// .insert_header(ContentType(mime::APPLICATION_JSON)) /// .insert_header((header::CONTENT_TYPE, mime::APPLICATION_JSON))
/// .insert_header(("X-TEST", "value")) /// .insert_header(("X-TEST", "value"))
/// .finish(); /// .finish();
/// ``` /// ```
@ -386,10 +386,10 @@ impl ResponseBuilder {
/// ///
/// ``` /// ```
/// # use actix_http::Response; /// # use actix_http::Response;
/// use actix_http::http::header::ContentType; /// use actix_http::http::header;
/// ///
/// Response::Ok() /// Response::Ok()
/// .append_header(ContentType(mime::APPLICATION_JSON)) /// .append_header((header::CONTENT_TYPE, mime::APPLICATION_JSON))
/// .append_header(("X-TEST", "value1")) /// .append_header(("X-TEST", "value1"))
/// .append_header(("X-TEST", "value2")) /// .append_header(("X-TEST", "value2"))
/// .finish(); /// .finish();
@ -682,7 +682,7 @@ impl ResponseBuilder {
}; };
if !contains { if !contains {
self.insert_header(header::ContentType(mime::APPLICATION_JSON)); self.insert_header((header::CONTENT_TYPE, mime::APPLICATION_JSON));
} }
self.body(Body::from(body)) self.body(Body::from(body))
@ -1133,7 +1133,7 @@ mod tests {
#[test] #[test]
fn response_builder_header_insert_typed() { fn response_builder_header_insert_typed() {
let mut res = Response::Ok(); let mut res = Response::Ok();
res.insert_header(header::ContentType(mime::APPLICATION_OCTET_STREAM)); res.insert_header((header::CONTENT_TYPE, mime::APPLICATION_OCTET_STREAM));
let res = res.finish(); let res = res.finish();
assert_eq!( assert_eq!(
@ -1158,8 +1158,8 @@ mod tests {
#[test] #[test]
fn response_builder_header_append_typed() { fn response_builder_header_append_typed() {
let mut res = Response::Ok(); let mut res = Response::Ok();
res.append_header(header::ContentType(mime::APPLICATION_OCTET_STREAM)); res.append_header((header::CONTENT_TYPE, mime::APPLICATION_OCTET_STREAM));
res.append_header(header::ContentType(mime::APPLICATION_JSON)); res.append_header((header::CONTENT_TYPE, mime::APPLICATION_JSON));
let res = res.finish(); let res = res.finish();
let headers: Vec<_> = res.headers().get_all("Content-Type").cloned().collect(); let headers: Vec<_> = res.headers().get_all("Content-Type").cloned().collect();

View File

@ -12,7 +12,7 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_rt::net::TcpStream; use actix_rt::net::TcpStream;
use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory}; use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory};
use bytes::Bytes; use bytes::Bytes;
use futures_core::ready; use futures_core::{future::LocalBoxFuture, ready};
use h2::server::{handshake, Handshake}; use h2::server::{handshake, Handshake};
use pin_project::pin_project; use pin_project::pin_project;
@ -107,7 +107,6 @@ where
X1: ServiceFactory<Request, Config = (), Response = Request>, X1: ServiceFactory<Request, Config = (), Response = Request>,
X1::Error: Into<Error>, X1::Error: Into<Error>,
X1::InitError: fmt::Debug, X1::InitError: fmt::Debug,
<X1::Service as Service<Request>>::Future: 'static,
{ {
HttpService { HttpService {
expect, expect,
@ -128,7 +127,6 @@ where
U1: ServiceFactory<(Request, Framed<T, h1::Codec>), Config = (), Response = ()>, U1: ServiceFactory<(Request, Framed<T, h1::Codec>), Config = (), Response = ()>,
U1::Error: fmt::Display, U1::Error: fmt::Display,
U1::InitError: fmt::Debug, U1::InitError: fmt::Debug,
<U1::Service as Service<(Request, Framed<T, h1::Codec>)>>::Future: 'static,
{ {
HttpService { HttpService {
upgrade, upgrade,
@ -150,23 +148,24 @@ where
impl<S, B, X, U> HttpService<TcpStream, S, B, X, U> impl<S, B, X, U> HttpService<TcpStream, S, B, X, U>
where where
S: ServiceFactory<Request, Config = ()>, S: ServiceFactory<Request, Config = ()>,
S::Future: 'static,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>> + 'static, S::Response: Into<Response<B>> + 'static,
<S::Service as Service<Request>>::Future: 'static, <S::Service as Service<Request>>::Future: 'static,
B: MessageBody + 'static, B: MessageBody + 'static,
X: ServiceFactory<Request, Config = (), Response = Request>, X: ServiceFactory<Request, Config = (), Response = Request>,
X::Future: 'static,
X::Error: Into<Error>, X::Error: Into<Error>,
X::InitError: fmt::Debug, X::InitError: fmt::Debug,
<X::Service as Service<Request>>::Future: 'static,
U: ServiceFactory< U: ServiceFactory<
(Request, Framed<TcpStream, h1::Codec>), (Request, Framed<TcpStream, h1::Codec>),
Config = (), Config = (),
Response = (), Response = (),
>, >,
U::Future: 'static,
U::Error: fmt::Display + Into<Error>, U::Error: fmt::Display + Into<Error>,
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
<U::Service as Service<(Request, Framed<TcpStream, h1::Codec>)>>::Future: 'static,
{ {
/// Create simple tcp stream service /// Create simple tcp stream service
pub fn tcp( pub fn tcp(
@ -196,23 +195,24 @@ mod openssl {
impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U> impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>
where where
S: ServiceFactory<Request, Config = ()>, S: ServiceFactory<Request, Config = ()>,
S::Future: 'static,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>> + 'static, S::Response: Into<Response<B>> + 'static,
<S::Service as Service<Request>>::Future: 'static, <S::Service as Service<Request>>::Future: 'static,
B: MessageBody + 'static, B: MessageBody + 'static,
X: ServiceFactory<Request, Config = (), Response = Request>, X: ServiceFactory<Request, Config = (), Response = Request>,
X::Future: 'static,
X::Error: Into<Error>, X::Error: Into<Error>,
X::InitError: fmt::Debug, X::InitError: fmt::Debug,
<X::Service as Service<Request>>::Future: 'static,
U: ServiceFactory< U: ServiceFactory<
(Request, Framed<TlsStream<TcpStream>, h1::Codec>), (Request, Framed<TlsStream<TcpStream>, h1::Codec>),
Config = (), Config = (),
Response = (), Response = (),
>, >,
U::Future: 'static,
U::Error: fmt::Display + Into<Error>, U::Error: fmt::Display + Into<Error>,
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
<U::Service as Service<(Request, Framed<TlsStream<TcpStream>, h1::Codec>)>>::Future: 'static,
{ {
/// Create openssl based service /// Create openssl based service
pub fn openssl( pub fn openssl(
@ -252,32 +252,33 @@ mod openssl {
mod rustls { mod rustls {
use std::io; use std::io;
use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::rustls::{Acceptor, ServerConfig, Session, TlsStream}; use actix_tls::accept::rustls::{Acceptor, ServerConfig, Session, TlsStream};
use actix_tls::accept::TlsError; use actix_tls::accept::TlsError;
use super::*; use super::*;
use actix_service::ServiceFactoryExt;
impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U> impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>
where where
S: ServiceFactory<Request, Config = ()>, S: ServiceFactory<Request, Config = ()>,
S::Future: 'static,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>> + 'static, S::Response: Into<Response<B>> + 'static,
<S::Service as Service<Request>>::Future: 'static, <S::Service as Service<Request>>::Future: 'static,
B: MessageBody + 'static, B: MessageBody + 'static,
X: ServiceFactory<Request, Config = (), Response = Request>, X: ServiceFactory<Request, Config = (), Response = Request>,
X::Future: 'static,
X::Error: Into<Error>, X::Error: Into<Error>,
X::InitError: fmt::Debug, X::InitError: fmt::Debug,
<X::Service as Service<Request>>::Future: 'static,
U: ServiceFactory< U: ServiceFactory<
(Request, Framed<TlsStream<TcpStream>, h1::Codec>), (Request, Framed<TlsStream<TcpStream>, h1::Codec>),
Config = (), Config = (),
Response = (), Response = (),
>, >,
U::Future: 'static,
U::Error: fmt::Display + Into<Error>, U::Error: fmt::Display + Into<Error>,
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
<U::Service as Service<(Request, Framed<TlsStream<TcpStream>, h1::Codec>)>>::Future: 'static,
{ {
/// Create openssl based service /// Create openssl based service
pub fn rustls( pub fn rustls(
@ -319,137 +320,117 @@ mod rustls {
impl<T, S, B, X, U> ServiceFactory<(T, Protocol, Option<net::SocketAddr>)> impl<T, S, B, X, U> ServiceFactory<(T, Protocol, Option<net::SocketAddr>)>
for HttpService<T, S, B, X, U> for HttpService<T, S, B, X, U>
where where
T: AsyncRead + AsyncWrite + Unpin, T: AsyncRead + AsyncWrite + Unpin + 'static,
S: ServiceFactory<Request, Config = ()>, S: ServiceFactory<Request, Config = ()>,
S::Future: 'static,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>> + 'static, S::Response: Into<Response<B>> + 'static,
<S::Service as Service<Request>>::Future: 'static, <S::Service as Service<Request>>::Future: 'static,
B: MessageBody + 'static, B: MessageBody + 'static,
X: ServiceFactory<Request, Config = (), Response = Request>, X: ServiceFactory<Request, Config = (), Response = Request>,
X::Future: 'static,
X::Error: Into<Error>, X::Error: Into<Error>,
X::InitError: fmt::Debug, X::InitError: fmt::Debug,
<X::Service as Service<Request>>::Future: 'static,
U: ServiceFactory<(Request, Framed<T, h1::Codec>), Config = (), Response = ()>, U: ServiceFactory<(Request, Framed<T, h1::Codec>), Config = (), Response = ()>,
U::Future: 'static,
U::Error: fmt::Display + Into<Error>, U::Error: fmt::Display + Into<Error>,
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
<U::Service as Service<(Request, Framed<T, h1::Codec>)>>::Future: 'static,
{ {
type Response = (); type Response = ();
type Error = DispatchError; type Error = DispatchError;
type Config = (); type Config = ();
type Service = HttpServiceHandler<T, S::Service, B, X::Service, U::Service>; type Service = HttpServiceHandler<T, S::Service, B, X::Service, U::Service>;
type InitError = (); type InitError = ();
type Future = HttpServiceResponse<T, S, B, X, U>; type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
HttpServiceResponse { let service = self.srv.new_service(());
fut: self.srv.new_service(()), let expect = self.expect.new_service(());
fut_ex: Some(self.expect.new_service(())), let upgrade = self.upgrade.as_ref().map(|s| s.new_service(()));
fut_upg: self.upgrade.as_ref().map(|f| f.new_service(())), let on_connect_ext = self.on_connect_ext.clone();
expect: None, let cfg = self.cfg.clone();
upgrade: None,
on_connect_ext: self.on_connect_ext.clone(),
cfg: self.cfg.clone(),
_phantom: PhantomData,
}
}
}
#[doc(hidden)] Box::pin(async move {
#[pin_project] let expect = expect
pub struct HttpServiceResponse<T, S, B, X, U> .await
where .map_err(|e| log::error!("Init http expect service error: {:?}", e))?;
S: ServiceFactory<Request>,
X: ServiceFactory<Request>,
U: ServiceFactory<(Request, Framed<T, h1::Codec>)>,
{
#[pin]
fut: S::Future,
#[pin]
fut_ex: Option<X::Future>,
#[pin]
fut_upg: Option<U::Future>,
expect: Option<X::Service>,
upgrade: Option<U::Service>,
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
cfg: ServiceConfig,
_phantom: PhantomData<B>,
}
impl<T, S, B, X, U> Future for HttpServiceResponse<T, S, B, X, U> let upgrade = match upgrade {
where Some(upgrade) => {
T: AsyncRead + AsyncWrite + Unpin, let upgrade = upgrade.await.map_err(|e| {
S: ServiceFactory<Request>, log::error!("Init http upgrade service error: {:?}", e)
S::Error: Into<Error> + 'static, })?;
S::InitError: fmt::Debug, Some(upgrade)
S::Response: Into<Response<B>> + 'static, }
<S::Service as Service<Request>>::Future: 'static, None => None,
B: MessageBody + 'static, };
X: ServiceFactory<Request, Response = Request>,
X::Error: Into<Error>,
X::InitError: fmt::Debug,
<X::Service as Service<Request>>::Future: 'static,
U: ServiceFactory<(Request, Framed<T, h1::Codec>), Response = ()>,
U::Error: fmt::Display,
U::InitError: fmt::Debug,
<U::Service as Service<(Request, Framed<T, h1::Codec>)>>::Future: 'static,
{
type Output =
Result<HttpServiceHandler<T, S::Service, B, X::Service, U::Service>, ()>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { let service = service
let mut this = self.as_mut().project(); .await
.map_err(|e| log::error!("Init http service error: {:?}", e))?;
if let Some(fut) = this.fut_ex.as_pin_mut() { Ok(HttpServiceHandler::new(
let expect = ready!(fut cfg,
.poll(cx)
.map_err(|e| log::error!("Init http service error: {:?}", e)))?;
this = self.as_mut().project();
*this.expect = Some(expect);
this.fut_ex.set(None);
}
if let Some(fut) = this.fut_upg.as_pin_mut() {
let upgrade = ready!(fut
.poll(cx)
.map_err(|e| log::error!("Init http service error: {:?}", e)))?;
this = self.as_mut().project();
*this.upgrade = Some(upgrade);
this.fut_upg.set(None);
}
let result = ready!(this
.fut
.poll(cx)
.map_err(|e| log::error!("Init http service error: {:?}", e)));
Poll::Ready(result.map(|service| {
let this = self.as_mut().project();
HttpServiceHandler::new(
this.cfg.clone(),
service, service,
this.expect.take().unwrap(), expect,
this.upgrade.take(), upgrade,
this.on_connect_ext.clone(), on_connect_ext,
) ))
})) })
} }
} }
/// `Service` implementation for HTTP transport /// `Service` implementation for HTTP/1 and HTTP/2 transport
pub struct HttpServiceHandler<T, S, B, X, U> pub struct HttpServiceHandler<T, S, B, X, U>
where where
S: Service<Request>, S: Service<Request>,
X: Service<Request>, X: Service<Request>,
U: Service<(Request, Framed<T, h1::Codec>)>, U: Service<(Request, Framed<T, h1::Codec>)>,
{ {
flow: Rc<HttpFlow<S, X, U>>, pub(super) flow: Rc<HttpFlow<S, X, U>>,
cfg: ServiceConfig, pub(super) cfg: ServiceConfig,
on_connect_ext: Option<Rc<ConnectCallback<T>>>, pub(super) on_connect_ext: Option<Rc<ConnectCallback<T>>>,
_phantom: PhantomData<B>, _phantom: PhantomData<B>,
} }
impl<T, S, B, X, U> HttpServiceHandler<T, S, B, X, U>
where
S: Service<Request>,
S::Error: Into<Error>,
X: Service<Request>,
X::Error: Into<Error>,
U: Service<(Request, Framed<T, h1::Codec>)>,
U::Error: Into<Error>,
{
pub(super) fn new(
cfg: ServiceConfig,
service: S,
expect: X,
upgrade: Option<U>,
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
) -> HttpServiceHandler<T, S, B, X, U> {
HttpServiceHandler {
cfg,
on_connect_ext,
flow: HttpFlow::new(service, expect, upgrade),
_phantom: PhantomData,
}
}
pub(super) fn _poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
ready!(self.flow.expect.poll_ready(cx).map_err(Into::into))?;
ready!(self.flow.service.poll_ready(cx).map_err(Into::into))?;
if let Some(ref upg) = self.flow.upgrade {
ready!(upg.poll_ready(cx).map_err(Into::into))?;
};
Poll::Ready(Ok(()))
}
}
/// A collection of services that describe an HTTP request flow. /// A collection of services that describe an HTTP request flow.
pub(super) struct HttpFlow<S, X, U> { pub(super) struct HttpFlow<S, X, U> {
pub(super) service: S, pub(super) service: S,
@ -467,34 +448,6 @@ impl<S, X, U> HttpFlow<S, X, U> {
} }
} }
impl<T, S, B, X, U> HttpServiceHandler<T, S, B, X, U>
where
S: Service<Request>,
S::Error: Into<Error> + 'static,
S::Future: 'static,
S::Response: Into<Response<B>> + 'static,
B: MessageBody + 'static,
X: Service<Request, Response = Request>,
X::Error: Into<Error>,
U: Service<(Request, Framed<T, h1::Codec>), Response = ()>,
U::Error: fmt::Display,
{
fn new(
cfg: ServiceConfig,
service: S,
expect: X,
upgrade: Option<U>,
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
) -> HttpServiceHandler<T, S, B, X, U> {
HttpServiceHandler {
cfg,
on_connect_ext,
flow: HttpFlow::new(service, expect, upgrade),
_phantom: PhantomData,
}
}
}
impl<T, S, B, X, U> Service<(T, Protocol, Option<net::SocketAddr>)> impl<T, S, B, X, U> Service<(T, Protocol, Option<net::SocketAddr>)>
for HttpServiceHandler<T, S, B, X, U> for HttpServiceHandler<T, S, B, X, U>
where where
@ -514,47 +467,10 @@ where
type Future = HttpServiceHandlerResponse<T, S, B, X, U>; type Future = HttpServiceHandlerResponse<T, S, B, X, U>;
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let ready = self self._poll_ready(cx).map_err(|e| {
.flow log::error!("HTTP service readiness error: {:?}", e);
.expect DispatchError::Service(e)
.poll_ready(cx) })
.map_err(|e| {
let e = e.into();
log::error!("Http service readiness error: {:?}", e);
DispatchError::Service(e)
})?
.is_ready();
let ready = self
.flow
.service
.poll_ready(cx)
.map_err(|e| {
let e = e.into();
log::error!("Http service readiness error: {:?}", e);
DispatchError::Service(e)
})?
.is_ready()
&& ready;
let ready = if let Some(ref upg) = self.flow.upgrade {
upg.poll_ready(cx)
.map_err(|e| {
let e = e.into();
log::error!("Http service readiness error: {:?}", e);
DispatchError::Service(e)
})?
.is_ready()
&& ready
} else {
ready
};
if ready {
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
} }
fn call( fn call(

View File

@ -4,7 +4,6 @@ use std::task::{Context, Poll};
use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_service::{IntoService, Service}; use actix_service::{IntoService, Service};
use actix_utils::dispatcher::{Dispatcher as InnerDispatcher, DispatcherError};
use super::{Codec, Frame, Message}; use super::{Codec, Frame, Message};
@ -15,7 +14,7 @@ where
T: AsyncRead + AsyncWrite, T: AsyncRead + AsyncWrite,
{ {
#[pin] #[pin]
inner: InnerDispatcher<S, T, Codec, Message>, inner: inner::Dispatcher<S, T, Codec, Message>,
} }
impl<S, T> Dispatcher<S, T> impl<S, T> Dispatcher<S, T>
@ -27,13 +26,13 @@ where
{ {
pub fn new<F: IntoService<S, Frame>>(io: T, service: F) -> Self { pub fn new<F: IntoService<S, Frame>>(io: T, service: F) -> Self {
Dispatcher { Dispatcher {
inner: InnerDispatcher::new(Framed::new(io, Codec::new()), service), inner: inner::Dispatcher::new(Framed::new(io, Codec::new()), service),
} }
} }
pub fn with<F: IntoService<S, Frame>>(framed: Framed<T, Codec>, service: F) -> Self { pub fn with<F: IntoService<S, Frame>>(framed: Framed<T, Codec>, service: F) -> Self {
Dispatcher { Dispatcher {
inner: InnerDispatcher::new(framed, service), inner: inner::Dispatcher::new(framed, service),
} }
} }
} }
@ -45,9 +44,393 @@ where
S::Future: 'static, S::Future: 'static,
S::Error: 'static, S::Error: 'static,
{ {
type Output = Result<(), DispatcherError<S::Error, Codec, Message>>; type Output = Result<(), inner::DispatcherError<S::Error, Codec, Message>>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.project().inner.poll(cx) self.project().inner.poll(cx)
} }
} }
/// Framed dispatcher service and related utilities.
mod inner {
// allow dead code since this mod was ripped from actix-utils
#![allow(dead_code)]
use core::{
fmt,
future::Future,
mem,
pin::Pin,
task::{Context, Poll},
};
use actix_service::{IntoService, Service};
use futures_core::stream::Stream;
use local_channel::mpsc;
use log::debug;
use pin_project_lite::pin_project;
use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed};
use crate::ResponseError;
/// Framed transport errors
pub enum DispatcherError<E, U, I>
where
U: Encoder<I> + Decoder,
{
/// Inner service error.
Service(E),
/// Frame encoding error.
Encoder(<U as Encoder<I>>::Error),
/// Frame decoding error.
Decoder(<U as Decoder>::Error),
}
impl<E, U, I> From<E> for DispatcherError<E, U, I>
where
U: Encoder<I> + Decoder,
{
fn from(err: E) -> Self {
DispatcherError::Service(err)
}
}
impl<E, U, I> fmt::Debug for DispatcherError<E, U, I>
where
E: fmt::Debug,
U: Encoder<I> + Decoder,
<U as Encoder<I>>::Error: fmt::Debug,
<U as Decoder>::Error: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
DispatcherError::Service(ref e) => {
write!(fmt, "DispatcherError::Service({:?})", e)
}
DispatcherError::Encoder(ref e) => {
write!(fmt, "DispatcherError::Encoder({:?})", e)
}
DispatcherError::Decoder(ref e) => {
write!(fmt, "DispatcherError::Decoder({:?})", e)
}
}
}
}
impl<E, U, I> fmt::Display for DispatcherError<E, U, I>
where
E: fmt::Display,
U: Encoder<I> + Decoder,
<U as Encoder<I>>::Error: fmt::Debug,
<U as Decoder>::Error: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
DispatcherError::Service(ref e) => write!(fmt, "{}", e),
DispatcherError::Encoder(ref e) => write!(fmt, "{:?}", e),
DispatcherError::Decoder(ref e) => write!(fmt, "{:?}", e),
}
}
}
impl<E, U, I> ResponseError for DispatcherError<E, U, I>
where
E: fmt::Debug + fmt::Display,
U: Encoder<I> + Decoder,
<U as Encoder<I>>::Error: fmt::Debug,
<U as Decoder>::Error: fmt::Debug,
{
}
/// Message type wrapper for signalling end of message stream.
pub enum Message<T> {
/// Message item.
Item(T),
/// Signal from service to flush all messages and stop processing.
Close,
}
pin_project! {
/// A future that reads frames from a [`Framed`] object and passes them to a [`Service`].
pub struct Dispatcher<S, T, U, I>
where
S: Service<<U as Decoder>::Item, Response = I>,
S::Error: 'static,
S::Future: 'static,
T: AsyncRead,
T: AsyncWrite,
U: Encoder<I>,
U: Decoder,
I: 'static,
<U as Encoder<I>>::Error: fmt::Debug,
{
service: S,
state: State<S, U, I>,
#[pin]
framed: Framed<T, U>,
rx: mpsc::Receiver<Result<Message<I>, S::Error>>,
tx: mpsc::Sender<Result<Message<I>, S::Error>>,
}
}
enum State<S, U, I>
where
S: Service<<U as Decoder>::Item>,
U: Encoder<I> + Decoder,
{
Processing,
Error(DispatcherError<S::Error, U, I>),
FramedError(DispatcherError<S::Error, U, I>),
FlushAndStop,
Stopping,
}
impl<S, U, I> State<S, U, I>
where
S: Service<<U as Decoder>::Item>,
U: Encoder<I> + Decoder,
{
fn take_error(&mut self) -> DispatcherError<S::Error, U, I> {
match mem::replace(self, State::Processing) {
State::Error(err) => err,
_ => panic!(),
}
}
fn take_framed_error(&mut self) -> DispatcherError<S::Error, U, I> {
match mem::replace(self, State::Processing) {
State::FramedError(err) => err,
_ => panic!(),
}
}
}
impl<S, T, U, I> Dispatcher<S, T, U, I>
where
S: Service<<U as Decoder>::Item, Response = I>,
S::Error: 'static,
S::Future: 'static,
T: AsyncRead + AsyncWrite,
U: Decoder + Encoder<I>,
I: 'static,
<U as Decoder>::Error: fmt::Debug,
<U as Encoder<I>>::Error: fmt::Debug,
{
/// Create new `Dispatcher`.
pub fn new<F>(framed: Framed<T, U>, service: F) -> Self
where
F: IntoService<S, <U as Decoder>::Item>,
{
let (tx, rx) = mpsc::channel();
Dispatcher {
framed,
rx,
tx,
service: service.into_service(),
state: State::Processing,
}
}
/// Construct new `Dispatcher` instance with customer `mpsc::Receiver`
pub fn with_rx<F>(
framed: Framed<T, U>,
service: F,
rx: mpsc::Receiver<Result<Message<I>, S::Error>>,
) -> Self
where
F: IntoService<S, <U as Decoder>::Item>,
{
let tx = rx.sender();
Dispatcher {
framed,
rx,
tx,
service: service.into_service(),
state: State::Processing,
}
}
/// Get sender handle.
pub fn tx(&self) -> mpsc::Sender<Result<Message<I>, S::Error>> {
self.tx.clone()
}
/// Get reference to a service wrapped by `Dispatcher` instance.
pub fn service(&self) -> &S {
&self.service
}
/// Get mutable reference to a service wrapped by `Dispatcher` instance.
pub fn service_mut(&mut self) -> &mut S {
&mut self.service
}
/// Get reference to a framed instance wrapped by `Dispatcher` instance.
pub fn framed(&self) -> &Framed<T, U> {
&self.framed
}
/// Get mutable reference to a framed instance wrapped by `Dispatcher` instance.
pub fn framed_mut(&mut self) -> &mut Framed<T, U> {
&mut self.framed
}
/// Read from framed object.
fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool
where
S: Service<<U as Decoder>::Item, Response = I>,
S::Error: 'static,
S::Future: 'static,
T: AsyncRead + AsyncWrite,
U: Decoder + Encoder<I>,
I: 'static,
<U as Encoder<I>>::Error: fmt::Debug,
{
loop {
let this = self.as_mut().project();
match this.service.poll_ready(cx) {
Poll::Ready(Ok(_)) => {
let item = match this.framed.next_item(cx) {
Poll::Ready(Some(Ok(el))) => el,
Poll::Ready(Some(Err(err))) => {
*this.state =
State::FramedError(DispatcherError::Decoder(err));
return true;
}
Poll::Pending => return false,
Poll::Ready(None) => {
*this.state = State::Stopping;
return true;
}
};
let tx = this.tx.clone();
let fut = this.service.call(item);
actix_rt::spawn(async move {
let item = fut.await;
let _ = tx.send(item.map(Message::Item));
});
}
Poll::Pending => return false,
Poll::Ready(Err(err)) => {
*this.state = State::Error(DispatcherError::Service(err));
return true;
}
}
}
}
/// Write to framed object.
fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool
where
S: Service<<U as Decoder>::Item, Response = I>,
S::Error: 'static,
S::Future: 'static,
T: AsyncRead + AsyncWrite,
U: Decoder + Encoder<I>,
I: 'static,
<U as Encoder<I>>::Error: fmt::Debug,
{
loop {
let mut this = self.as_mut().project();
while !this.framed.is_write_buf_full() {
match Pin::new(&mut this.rx).poll_next(cx) {
Poll::Ready(Some(Ok(Message::Item(msg)))) => {
if let Err(err) = this.framed.as_mut().write(msg) {
*this.state =
State::FramedError(DispatcherError::Encoder(err));
return true;
}
}
Poll::Ready(Some(Ok(Message::Close))) => {
*this.state = State::FlushAndStop;
return true;
}
Poll::Ready(Some(Err(err))) => {
*this.state = State::Error(DispatcherError::Service(err));
return true;
}
Poll::Ready(None) | Poll::Pending => break,
}
}
if !this.framed.is_write_buf_empty() {
match this.framed.flush(cx) {
Poll::Pending => break,
Poll::Ready(Ok(_)) => {}
Poll::Ready(Err(err)) => {
debug!("Error sending data: {:?}", err);
*this.state =
State::FramedError(DispatcherError::Encoder(err));
return true;
}
}
} else {
break;
}
}
false
}
}
impl<S, T, U, I> Future for Dispatcher<S, T, U, I>
where
S: Service<<U as Decoder>::Item, Response = I>,
S::Error: 'static,
S::Future: 'static,
T: AsyncRead + AsyncWrite,
U: Decoder + Encoder<I>,
I: 'static,
<U as Encoder<I>>::Error: fmt::Debug,
<U as Decoder>::Error: fmt::Debug,
{
type Output = Result<(), DispatcherError<S::Error, U, I>>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
let this = self.as_mut().project();
return match this.state {
State::Processing => {
if self.as_mut().poll_read(cx) || self.as_mut().poll_write(cx) {
continue;
} else {
Poll::Pending
}
}
State::Error(_) => {
// flush write buffer
if !this.framed.is_write_buf_empty()
&& this.framed.flush(cx).is_pending()
{
return Poll::Pending;
}
Poll::Ready(Err(this.state.take_error()))
}
State::FlushAndStop => {
if !this.framed.is_write_buf_empty() {
this.framed.flush(cx).map(|res| {
if let Err(err) = res {
debug!("Error sending data: {:?}", err);
}
Ok(())
})
} else {
Poll::Ready(Ok(()))
}
}
State::FramedError(_) => {
Poll::Ready(Err(this.state.take_framed_error()))
}
State::Stopping => Poll::Ready(Ok(())),
};
}
}
}
}

View File

@ -3,11 +3,9 @@ use actix_http::{
}; };
use actix_http_test::test_server; use actix_http_test::test_server;
use actix_service::ServiceFactoryExt; use actix_service::ServiceFactoryExt;
use actix_utils::future;
use bytes::Bytes; use bytes::Bytes;
use futures_util::{ use futures_util::StreamExt as _;
future::{self, ok},
StreamExt as _,
};
const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \
@ -63,7 +61,7 @@ async fn test_h1_v2() {
async fn test_connection_close() { async fn test_connection_close() {
let srv = test_server(move || { let srv = test_server(move || {
HttpService::build() HttpService::build()
.finish(|_| ok::<_, ()>(Response::Ok().body(STR))) .finish(|_| future::ok::<_, ()>(Response::Ok().body(STR)))
.tcp() .tcp()
.map(|_| ()) .map(|_| ())
}) })
@ -79,9 +77,9 @@ async fn test_with_query_parameter() {
HttpService::build() HttpService::build()
.finish(|req: Request| { .finish(|req: Request| {
if req.uri().query().unwrap().contains("qp=") { if req.uri().query().unwrap().contains("qp=") {
ok::<_, ()>(Response::Ok().finish()) future::ok::<_, ()>(Response::Ok().finish())
} else { } else {
ok::<_, ()>(Response::BadRequest().finish()) future::ok::<_, ()>(Response::BadRequest().finish())
} }
}) })
.tcp() .tcp()

View File

@ -11,12 +11,10 @@ use actix_http::HttpMessage;
use actix_http::{body, Error, HttpService, Request, Response}; use actix_http::{body, Error, HttpService, Request, Response};
use actix_http_test::test_server; use actix_http_test::test_server;
use actix_service::{fn_service, ServiceFactoryExt}; use actix_service::{fn_service, ServiceFactoryExt};
use actix_utils::future::{err, ok, ready};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures_core::Stream; use futures_core::Stream;
use futures_util::{ use futures_util::stream::{once, StreamExt as _};
future::{err, ok, ready},
stream::{once, StreamExt as _},
};
use openssl::{ use openssl::{
pkey::PKey, pkey::PKey,
ssl::{SslAcceptor, SslMethod}, ssl::{SslAcceptor, SslMethod},

View File

@ -8,10 +8,10 @@ use actix_http::http::{Method, StatusCode, Version};
use actix_http::{body, error, Error, HttpService, Request, Response}; use actix_http::{body, error, Error, HttpService, Request, Response};
use actix_http_test::test_server; use actix_http_test::test_server;
use actix_service::{fn_factory_with_config, fn_service}; use actix_service::{fn_factory_with_config, fn_service};
use actix_utils::future::{err, ok};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures_core::Stream; use futures_core::Stream;
use futures_util::future::{self, err, ok};
use futures_util::stream::{once, StreamExt as _}; use futures_util::stream::{once, StreamExt as _};
use rustls::{ use rustls::{
internal::pemfile::{certs, pkcs8_private_keys}, internal::pemfile::{certs, pkcs8_private_keys},
@ -51,7 +51,7 @@ fn tls_config() -> RustlsServerConfig {
async fn test_h1() -> io::Result<()> { async fn test_h1() -> io::Result<()> {
let srv = test_server(move || { let srv = test_server(move || {
HttpService::build() HttpService::build()
.h1(|_| future::ok::<_, Error>(Response::Ok().finish())) .h1(|_| ok::<_, Error>(Response::Ok().finish()))
.rustls(tls_config()) .rustls(tls_config())
}) })
.await; .await;
@ -65,7 +65,7 @@ async fn test_h1() -> io::Result<()> {
async fn test_h2() -> io::Result<()> { async fn test_h2() -> io::Result<()> {
let srv = test_server(move || { let srv = test_server(move || {
HttpService::build() HttpService::build()
.h2(|_| future::ok::<_, Error>(Response::Ok().finish())) .h2(|_| ok::<_, Error>(Response::Ok().finish()))
.rustls(tls_config()) .rustls(tls_config())
}) })
.await; .await;
@ -82,7 +82,7 @@ async fn test_h1_1() -> io::Result<()> {
.h1(|req: Request| { .h1(|req: Request| {
assert!(req.peer_addr().is_some()); assert!(req.peer_addr().is_some());
assert_eq!(req.version(), Version::HTTP_11); assert_eq!(req.version(), Version::HTTP_11);
future::ok::<_, Error>(Response::Ok().finish()) ok::<_, Error>(Response::Ok().finish())
}) })
.rustls(tls_config()) .rustls(tls_config())
}) })
@ -100,7 +100,7 @@ async fn test_h2_1() -> io::Result<()> {
.finish(|req: Request| { .finish(|req: Request| {
assert!(req.peer_addr().is_some()); assert!(req.peer_addr().is_some());
assert_eq!(req.version(), Version::HTTP_2); assert_eq!(req.version(), Version::HTTP_2);
future::ok::<_, Error>(Response::Ok().finish()) ok::<_, Error>(Response::Ok().finish())
}) })
.rustls(tls_config()) .rustls(tls_config())
}) })
@ -144,7 +144,7 @@ async fn test_h2_content_length() {
StatusCode::OK, StatusCode::OK,
StatusCode::NOT_FOUND, StatusCode::NOT_FOUND,
]; ];
future::ok::<_, ()>(Response::new(statuses[indx])) ok::<_, ()>(Response::new(statuses[indx]))
}) })
.rustls(tls_config()) .rustls(tls_config())
}) })
@ -213,7 +213,7 @@ async fn test_h2_headers() {
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ", TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
)); ));
} }
future::ok::<_, ()>(config.body(data.clone())) ok::<_, ()>(config.body(data.clone()))
}) })
.rustls(tls_config()) .rustls(tls_config())
}).await; }).await;
@ -252,7 +252,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
async fn test_h2_body2() { async fn test_h2_body2() {
let mut srv = test_server(move || { let mut srv = test_server(move || {
HttpService::build() HttpService::build()
.h2(|_| future::ok::<_, ()>(Response::Ok().body(STR))) .h2(|_| ok::<_, ()>(Response::Ok().body(STR)))
.rustls(tls_config()) .rustls(tls_config())
}) })
.await; .await;

View File

@ -5,9 +5,10 @@ use std::{net, thread};
use actix_http_test::test_server; use actix_http_test::test_server;
use actix_rt::time::sleep; use actix_rt::time::sleep;
use actix_service::fn_service; use actix_service::fn_service;
use actix_utils::future::{err, ok, ready};
use bytes::Bytes; use bytes::Bytes;
use futures_util::future::{self, err, ok, ready, FutureExt};
use futures_util::stream::{once, StreamExt as _}; use futures_util::stream::{once, StreamExt as _};
use futures_util::FutureExt as _;
use regex::Regex; use regex::Regex;
use actix_http::HttpMessage; use actix_http::HttpMessage;
@ -24,7 +25,7 @@ async fn test_h1() {
.client_disconnect(1000) .client_disconnect(1000)
.h1(|req: Request| { .h1(|req: Request| {
assert!(req.peer_addr().is_some()); assert!(req.peer_addr().is_some());
future::ok::<_, ()>(Response::Ok().finish()) ok::<_, ()>(Response::Ok().finish())
}) })
.tcp() .tcp()
}) })
@ -44,7 +45,7 @@ async fn test_h1_2() {
.finish(|req: Request| { .finish(|req: Request| {
assert!(req.peer_addr().is_some()); assert!(req.peer_addr().is_some());
assert_eq!(req.version(), http::Version::HTTP_11); assert_eq!(req.version(), http::Version::HTTP_11);
future::ok::<_, ()>(Response::Ok().finish()) ok::<_, ()>(Response::Ok().finish())
}) })
.tcp() .tcp()
}) })
@ -65,7 +66,7 @@ async fn test_expect_continue() {
err(error::ErrorPreconditionFailed("error")) err(error::ErrorPreconditionFailed("error"))
} }
})) }))
.finish(|_| future::ok::<_, ()>(Response::Ok().finish())) .finish(|_| ok::<_, ()>(Response::Ok().finish()))
.tcp() .tcp()
}) })
.await; .await;
@ -96,7 +97,7 @@ async fn test_expect_continue_h1() {
} }
}) })
})) }))
.h1(fn_service(|_| future::ok::<_, ()>(Response::Ok().finish()))) .h1(fn_service(|_| ok::<_, ()>(Response::Ok().finish())))
.tcp() .tcp()
}) })
.await; .await;
@ -175,7 +176,7 @@ async fn test_slow_request() {
let srv = test_server(|| { let srv = test_server(|| {
HttpService::build() HttpService::build()
.client_timeout(100) .client_timeout(100)
.finish(|_| future::ok::<_, ()>(Response::Ok().finish())) .finish(|_| ok::<_, ()>(Response::Ok().finish()))
.tcp() .tcp()
}) })
.await; .await;
@ -191,7 +192,7 @@ async fn test_slow_request() {
async fn test_http1_malformed_request() { async fn test_http1_malformed_request() {
let srv = test_server(|| { let srv = test_server(|| {
HttpService::build() HttpService::build()
.h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .h1(|_| ok::<_, ()>(Response::Ok().finish()))
.tcp() .tcp()
}) })
.await; .await;
@ -207,7 +208,7 @@ async fn test_http1_malformed_request() {
async fn test_http1_keepalive() { async fn test_http1_keepalive() {
let srv = test_server(|| { let srv = test_server(|| {
HttpService::build() HttpService::build()
.h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .h1(|_| ok::<_, ()>(Response::Ok().finish()))
.tcp() .tcp()
}) })
.await; .await;
@ -229,7 +230,7 @@ async fn test_http1_keepalive_timeout() {
let srv = test_server(|| { let srv = test_server(|| {
HttpService::build() HttpService::build()
.keep_alive(1) .keep_alive(1)
.h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .h1(|_| ok::<_, ()>(Response::Ok().finish()))
.tcp() .tcp()
}) })
.await; .await;
@ -250,7 +251,7 @@ async fn test_http1_keepalive_timeout() {
async fn test_http1_keepalive_close() { async fn test_http1_keepalive_close() {
let srv = test_server(|| { let srv = test_server(|| {
HttpService::build() HttpService::build()
.h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .h1(|_| ok::<_, ()>(Response::Ok().finish()))
.tcp() .tcp()
}) })
.await; .await;
@ -271,7 +272,7 @@ async fn test_http1_keepalive_close() {
async fn test_http10_keepalive_default_close() { async fn test_http10_keepalive_default_close() {
let srv = test_server(|| { let srv = test_server(|| {
HttpService::build() HttpService::build()
.h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .h1(|_| ok::<_, ()>(Response::Ok().finish()))
.tcp() .tcp()
}) })
.await; .await;
@ -291,7 +292,7 @@ async fn test_http10_keepalive_default_close() {
async fn test_http10_keepalive() { async fn test_http10_keepalive() {
let srv = test_server(|| { let srv = test_server(|| {
HttpService::build() HttpService::build()
.h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .h1(|_| ok::<_, ()>(Response::Ok().finish()))
.tcp() .tcp()
}) })
.await; .await;
@ -319,7 +320,7 @@ async fn test_http1_keepalive_disabled() {
let srv = test_server(|| { let srv = test_server(|| {
HttpService::build() HttpService::build()
.keep_alive(KeepAlive::Disabled) .keep_alive(KeepAlive::Disabled)
.h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .h1(|_| ok::<_, ()>(Response::Ok().finish()))
.tcp() .tcp()
}) })
.await; .await;
@ -354,7 +355,7 @@ async fn test_content_length() {
StatusCode::OK, StatusCode::OK,
StatusCode::NOT_FOUND, StatusCode::NOT_FOUND,
]; ];
future::ok::<_, ()>(Response::new(statuses[indx])) ok::<_, ()>(Response::new(statuses[indx]))
}) })
.tcp() .tcp()
}) })
@ -409,7 +410,7 @@ async fn test_h1_headers() {
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ", TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
)); ));
} }
future::ok::<_, ()>(builder.body(data.clone())) ok::<_, ()>(builder.body(data.clone()))
}).tcp() }).tcp()
}).await; }).await;
@ -645,7 +646,7 @@ async fn test_h1_response_http_error_handling() {
async fn test_h1_service_error() { async fn test_h1_service_error() {
let mut srv = test_server(|| { let mut srv = test_server(|| {
HttpService::build() HttpService::build()
.h1(|_| future::err::<Response, Error>(error::ErrorBadRequest("error"))) .h1(|_| err::<Response, _>(error::ErrorBadRequest("error")))
.tcp() .tcp()
}) })
.await; .await;
@ -667,7 +668,7 @@ async fn test_h1_on_connect() {
}) })
.h1(|req: Request| { .h1(|req: Request| {
assert!(req.extensions().contains::<isize>()); assert!(req.extensions().contains::<isize>());
future::ok::<_, ()>(Response::Ok().finish()) ok::<_, ()>(Response::Ok().finish())
}) })
.tcp() .tcp()
}) })

View File

@ -9,11 +9,12 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_http::{body, h1, ws, Error, HttpService, Request, Response}; use actix_http::{body, h1, ws, Error, HttpService, Request, Response};
use actix_http_test::test_server; use actix_http_test::test_server;
use actix_service::{fn_factory, Service}; use actix_service::{fn_factory, Service};
use actix_utils::dispatcher::Dispatcher; use actix_utils::future;
use bytes::Bytes; use bytes::Bytes;
use futures_util::future;
use futures_util::{SinkExt as _, StreamExt as _}; use futures_util::{SinkExt as _, StreamExt as _};
use crate::ws::Dispatcher;
struct WsService<T>(Arc<Mutex<(PhantomData<T>, Cell<bool>)>>); struct WsService<T>(Arc<Mutex<(PhantomData<T>, Cell<bool>)>>);
impl<T> WsService<T> { impl<T> WsService<T> {
@ -58,7 +59,7 @@ where
.await .await
.unwrap(); .unwrap();
Dispatcher::new(framed.replace_codec(ws::Codec::new()), service) Dispatcher::with(framed.replace_codec(ws::Codec::new()), service)
.await .await
.map_err(|_| panic!()) .map_err(|_| panic!())
}; };

View File

@ -17,12 +17,14 @@ path = "src/lib.rs"
[dependencies] [dependencies]
actix-web = { version = "4.0.0-beta.4", default-features = false } actix-web = { version = "4.0.0-beta.4", default-features = false }
actix-utils = "3.0.0-beta.2" actix-utils = "3.0.0-beta.4"
bytes = "1" bytes = "1"
derive_more = "0.99.5" derive_more = "0.99.5"
httparse = "1.3" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] } futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] }
httparse = "1.3"
local-waker = "0.1"
log = "0.4" log = "0.4"
mime = "0.3" mime = "0.3"
twoway = "0.2" twoway = "0.2"

View File

@ -1,21 +1,22 @@
//! Multipart payload support //! Multipart payload support
use actix_utils::future::{ready, Ready};
use actix_web::{dev::Payload, Error, FromRequest, HttpRequest}; use actix_web::{dev::Payload, Error, FromRequest, HttpRequest};
use futures_util::future::{ok, Ready};
use crate::server::Multipart; use crate::server::Multipart;
/// Get request's payload as multipart stream /// Get request's payload as multipart stream.
/// ///
/// Content-type: multipart/form-data; /// Content-type: multipart/form-data;
/// ///
/// ## Server example /// ## Server example
/// ///
/// ``` /// ```
/// use futures_util::stream::{Stream, StreamExt};
/// use actix_web::{web, HttpResponse, Error}; /// use actix_web::{web, HttpResponse, Error};
/// use actix_multipart as mp; /// use actix_multipart::Multipart;
/// use futures_util::stream::StreamExt as _;
/// ///
/// async fn index(mut payload: mp::Multipart) -> Result<HttpResponse, Error> { /// async fn index(mut payload: Multipart) -> Result<HttpResponse, Error> {
/// // iterate over multipart stream /// // iterate over multipart stream
/// while let Some(item) = payload.next().await { /// while let Some(item) = payload.next().await {
/// let mut field = item?; /// let mut field = item?;
@ -25,9 +26,9 @@ use crate::server::Multipart;
/// println!("-- CHUNK: \n{:?}", std::str::from_utf8(&chunk?)); /// println!("-- CHUNK: \n{:?}", std::str::from_utf8(&chunk?));
/// } /// }
/// } /// }
///
/// Ok(HttpResponse::Ok().into()) /// Ok(HttpResponse::Ok().into())
/// } /// }
/// # fn main() {}
/// ``` /// ```
impl FromRequest for Multipart { impl FromRequest for Multipart {
type Error = Error; type Error = Error;
@ -36,9 +37,9 @@ impl FromRequest for Multipart {
#[inline] #[inline]
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
ok(match Multipart::boundary(req.headers()) { ready(Ok(match Multipart::boundary(req.headers()) {
Ok(boundary) => Multipart::from_boundary(boundary, payload.take()), Ok(boundary) => Multipart::from_boundary(boundary, payload.take()),
Err(err) => Multipart::from_error(err), Err(err) => Multipart::from_error(err),
}) }))
} }
} }

View File

@ -1,4 +1,4 @@
//! Multipart payload support //! Multipart response payload support.
use std::cell::{Cell, RefCell, RefMut}; use std::cell::{Cell, RefCell, RefMut};
use std::convert::TryFrom; use std::convert::TryFrom;
@ -8,12 +8,12 @@ use std::rc::Rc;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::{cmp, fmt}; use std::{cmp, fmt};
use bytes::{Bytes, BytesMut};
use futures_util::stream::{LocalBoxStream, Stream, StreamExt};
use actix_utils::task::LocalWaker;
use actix_web::error::{ParseError, PayloadError}; use actix_web::error::{ParseError, PayloadError};
use actix_web::http::header::{self, ContentDisposition, HeaderMap, HeaderName, HeaderValue}; use actix_web::http::header::{self, ContentDisposition, HeaderMap, HeaderName, HeaderValue};
use bytes::{Bytes, BytesMut};
use futures_core::stream::{LocalBoxStream, Stream};
use futures_util::stream::StreamExt as _;
use local_waker::LocalWaker;
use crate::error::MultipartError; use crate::error::MultipartError;

View File

@ -31,5 +31,6 @@ tokio = { version = "1", features = ["sync"] }
actix-rt = "2.2" actix-rt = "2.2"
actix-test = "0.0.1" actix-test = "0.0.1"
awc = { version = "3.0.0-beta.3", default-features = false }
env_logger = "0.8" env_logger = "0.8"
futures-util = { version = "0.3.7", default-features = false } futures-util = { version = "0.3.7", default-features = false }

View File

@ -1,8 +1,11 @@
use actix::prelude::*; use actix::prelude::*;
use actix_web::{web, App, HttpRequest}; use actix_web::{
http::{header, StatusCode},
test, web, App, HttpRequest, HttpResponse,
};
use actix_web_actors::*; use actix_web_actors::*;
use bytes::Bytes; use bytes::Bytes;
use futures_util::{SinkExt, StreamExt}; use futures_util::{SinkExt as _, StreamExt as _};
struct Ws; struct Ws;
@ -56,3 +59,51 @@ async fn test_simple() {
let item = framed.next().await.unwrap().unwrap(); let item = framed.next().await.unwrap().unwrap();
assert_eq!(item, ws::Frame::Close(Some(ws::CloseCode::Normal.into()))); assert_eq!(item, ws::Frame::Close(Some(ws::CloseCode::Normal.into())));
} }
#[actix_rt::test]
async fn test_with_credentials() {
let mut srv = test::start(|| {
App::new().service(web::resource("/").to(
|req: HttpRequest, stream: web::Payload| async move {
if req.headers().contains_key("Authorization") {
ws::start(Ws, &req, stream)
} else {
Ok(HttpResponse::new(StatusCode::UNAUTHORIZED))
}
},
))
});
// client service without credentials
match srv.ws().await {
Ok(_) => panic!("WebSocket client without credentials should panic"),
Err(awc::error::WsClientError::InvalidResponseStatus(status)) => {
assert_eq!(status, StatusCode::UNAUTHORIZED)
}
Err(e) => panic!("Invalid error from WebSocket client: {}", e),
}
let headers = srv.client_headers().unwrap();
headers.insert(
header::AUTHORIZATION,
header::HeaderValue::from_static("Bearer Something"),
);
// client service with credentials
let client = srv.ws();
let mut framed = client.await.unwrap();
framed.send(ws::Message::Text("text".into())).await.unwrap();
let item = framed.next().await.unwrap().unwrap();
assert_eq!(item, ws::Frame::Text(Bytes::from_static(b"text")));
framed
.send(ws::Message::Close(Some(ws::CloseCode::Normal.into())))
.await
.unwrap();
let item = framed.next().await.unwrap().unwrap();
assert_eq!(item, ws::Frame::Close(Some(ws::CloseCode::Normal.into())));
}

View File

@ -20,9 +20,10 @@ proc-macro2 = "1"
[dev-dependencies] [dev-dependencies]
actix-rt = "2.2" actix-rt = "2.2"
actix-web = "4.0.0-beta.4"
actix-test = "0.0.1" actix-test = "0.0.1"
actix-utils = "3.0.0-beta.4"
actix-web = "4.0.0-beta.4"
futures-util = { version = "0.3.7", default-features = false } futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
trybuild = "1" trybuild = "1"
rustversion = "1" rustversion = "1"

View File

@ -1,11 +1,12 @@
use std::future::Future; use std::future::Future;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use actix_utils::future;
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform}; use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
use actix_web::http::header::{HeaderName, HeaderValue}; use actix_web::http::header::{HeaderName, HeaderValue};
use actix_web::{http, web::Path, App, Error, HttpResponse, Responder}; use actix_web::{http, web::Path, App, Error, HttpResponse, Responder};
use actix_web_codegen::{connect, delete, get, head, options, patch, post, put, route, trace}; use actix_web_codegen::{connect, delete, get, head, options, patch, post, put, route, trace};
use futures_util::future::{self, LocalBoxFuture}; use futures_core::future::LocalBoxFuture;
// Make sure that we can name function as 'config' // Make sure that we can name function as 'config'
#[get("/config")] #[get("/config")]

View File

@ -1,11 +1,17 @@
# Changes # Changes
## Unreleased - 2021-xx-xx ## Unreleased - 2021-xx-xx
### Added
* Add `Client::headers` to get default mut reference of `HeaderMap` of client object. [#2114]
### Changed ### Changed
* `ConnectorService` type is renamed to `BoxConnectorService`. [#2081] * `ConnectorService` type is renamed to `BoxConnectorService`. [#2081]
* Fix http/https encoding when enabling `compress` feature. [#2116] * Fix http/https encoding when enabling `compress` feature. [#2116]
* Rename `TestResponse::header` to `append_header`, `set` to `insert_header`. `TestResponse` header methods now take `IntoHeaderPair` tuples. [#2094]
[#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 [#2116]: https://github.com/actix/actix-web/pull/2116

View File

@ -69,7 +69,7 @@ tls-rustls = { version = "0.19.0", package = "rustls", optional = true, features
actix-web = { version = "4.0.0-beta.4", features = ["openssl"] } actix-web = { version = "4.0.0-beta.4", features = ["openssl"] }
actix-http = { version = "3.0.0-beta.4", features = ["openssl"] } actix-http = { version = "3.0.0-beta.4", features = ["openssl"] }
actix-http-test = { version = "3.0.0-beta.3", features = ["openssl"] } actix-http-test = { version = "3.0.0-beta.3", features = ["openssl"] }
actix-utils = "3.0.0-beta.1" actix-utils = "3.0.0-beta.4"
actix-server = "2.0.0-beta.3" actix-server = "2.0.0-beta.3"
actix-tls = { version = "3.0.0-beta.5", features = ["openssl", "rustls"] } actix-tls = { version = "3.0.0-beta.5", features = ["openssl", "rustls"] }
actix-test = { version = "0.0.1", features = ["openssl", "rustls"] } actix-test = { version = "0.0.1", features = ["openssl", "rustls"] }

View File

@ -286,4 +286,12 @@ impl Client {
} }
req req
} }
/// Get default HeaderMap of Client.
///
/// Returns Some(&mut HeaderMap) when Client object is unique
/// (No other clone of client exists at the same time).
pub fn headers(&mut self) -> Option<&mut HeaderMap> {
Rc::get_mut(&mut self.0.headers)
}
} }

View File

@ -189,12 +189,12 @@ impl ClientRequest {
/// # #[actix_rt::main] /// # #[actix_rt::main]
/// # async fn main() { /// # async fn main() {
/// # use awc::Client; /// # use awc::Client;
/// use awc::http::header::ContentType; /// use awc::http::header::CONTENT_TYPE;
/// ///
/// Client::new() /// Client::new()
/// .get("http://www.rust-lang.org") /// .get("http://www.rust-lang.org")
/// .insert_header(("X-TEST", "value")) /// .insert_header(("X-TEST", "value"))
/// .insert_header(ContentType(mime::APPLICATION_JSON)); /// .insert_header((CONTENT_TYPE, mime::APPLICATION_JSON));
/// # } /// # }
/// ``` /// ```
pub fn append_header<H>(mut self, header: H) -> Self pub fn append_header<H>(mut self, header: H) -> Self
@ -548,6 +548,8 @@ impl fmt::Debug for ClientRequest {
mod tests { mod tests {
use std::time::SystemTime; use std::time::SystemTime;
use actix_http::http::header::HttpDate;
use super::*; use super::*;
use crate::Client; use crate::Client;
@ -564,7 +566,7 @@ mod tests {
let req = Client::new() let req = Client::new()
.put("/") .put("/")
.version(Version::HTTP_2) .version(Version::HTTP_2)
.insert_header(header::Date(SystemTime::now().into())) .insert_header((header::DATE, HttpDate::from(SystemTime::now())))
.content_type("plain/text") .content_type("plain/text")
.append_header((header::SERVER, "awc")); .append_header((header::SERVER, "awc"));

View File

@ -449,13 +449,13 @@ mod tests {
#[actix_rt::test] #[actix_rt::test]
async fn test_body() { async fn test_body() {
let mut req = TestResponse::with_header(header::CONTENT_LENGTH, "xxxx").finish(); let mut req = TestResponse::with_header((header::CONTENT_LENGTH, "xxxx")).finish();
match req.body().await.err().unwrap() { match req.body().await.err().unwrap() {
PayloadError::UnknownLength => {} PayloadError::UnknownLength => {}
_ => unreachable!("error"), _ => unreachable!("error"),
} }
let mut req = TestResponse::with_header(header::CONTENT_LENGTH, "10000000").finish(); let mut req = TestResponse::with_header((header::CONTENT_LENGTH, "10000000")).finish();
match req.body().await.err().unwrap() { match req.body().await.err().unwrap() {
PayloadError::Overflow => {} PayloadError::Overflow => {}
_ => unreachable!("error"), _ => unreachable!("error"),
@ -497,23 +497,23 @@ mod tests {
assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));
let mut req = TestResponse::default() let mut req = TestResponse::default()
.header( .insert_header((
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("application/text"), header::HeaderValue::from_static("application/text"),
) ))
.finish(); .finish();
let json = JsonBody::<_, MyObject>::new(&mut req).await; let json = JsonBody::<_, MyObject>::new(&mut req).await;
assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));
let mut req = TestResponse::default() let mut req = TestResponse::default()
.header( .insert_header((
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"), header::HeaderValue::from_static("application/json"),
) ))
.header( .insert_header((
header::CONTENT_LENGTH, header::CONTENT_LENGTH,
header::HeaderValue::from_static("10000"), header::HeaderValue::from_static("10000"),
) ))
.finish(); .finish();
let json = JsonBody::<_, MyObject>::new(&mut req).limit(100).await; let json = JsonBody::<_, MyObject>::new(&mut req).limit(100).await;
@ -523,14 +523,14 @@ mod tests {
)); ));
let mut req = TestResponse::default() let mut req = TestResponse::default()
.header( .insert_header((
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"), header::HeaderValue::from_static("application/json"),
) ))
.header( .insert_header((
header::CONTENT_LENGTH, header::CONTENT_LENGTH,
header::HeaderValue::from_static("16"), header::HeaderValue::from_static("16"),
) ))
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
.finish(); .finish();

View File

@ -1,8 +1,6 @@
//! Test helpers for actix http client to use during testing. //! Test helpers for actix http client to use during testing.
use std::convert::TryFrom; use actix_http::http::header::IntoHeaderPair;
use actix_http::http::{StatusCode, Version};
use actix_http::http::header::{Header, IntoHeaderValue};
use actix_http::http::{Error as HttpError, HeaderName, StatusCode, Version};
#[cfg(feature = "cookies")] #[cfg(feature = "cookies")]
use actix_http::{ use actix_http::{
cookie::{Cookie, CookieJar}, cookie::{Cookie, CookieJar},
@ -34,13 +32,11 @@ impl Default for TestResponse {
impl TestResponse { impl TestResponse {
/// Create TestResponse and set header /// Create TestResponse and set header
pub fn with_header<K, V>(key: K, value: V) -> Self pub fn with_header<H>(header: H) -> Self
where where
HeaderName: TryFrom<K>, H: IntoHeaderPair,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue,
{ {
Self::default().header(key, value) Self::default().insert_header(header)
} }
/// Set HTTP version of this response /// Set HTTP version of this response
@ -49,27 +45,26 @@ impl TestResponse {
self self
} }
/// Set a header /// Insert a header
pub fn set<H: Header>(mut self, hdr: H) -> Self { pub fn insert_header<H>(mut self, header: H) -> Self
if let Ok(value) = hdr.try_into_value() { where
self.head.headers.append(H::name(), value); H: IntoHeaderPair,
{
if let Ok((key, value)) = header.try_into_header_pair() {
self.head.headers.insert(key, value);
return self; return self;
} }
panic!("Can not set header"); panic!("Can not set header");
} }
/// Append a header /// Append a header
pub fn header<K, V>(mut self, key: K, value: V) -> Self pub fn append_header<H>(mut self, header: H) -> Self
where where
HeaderName: TryFrom<K>, H: IntoHeaderPair,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue,
{ {
if let Ok(key) = HeaderName::try_from(key) { if let Ok((key, value)) = header.try_into_header_pair() {
if let Ok(value) = value.try_into_value() { self.head.headers.append(key, value);
self.head.headers.append(key, value); return self;
return self;
}
} }
panic!("Can not create header"); panic!("Can not create header");
} }
@ -115,6 +110,8 @@ impl TestResponse {
mod tests { mod tests {
use std::time::SystemTime; use std::time::SystemTime;
use actix_http::http::header::HttpDate;
use super::*; use super::*;
use crate::{cookie, http::header}; use crate::{cookie, http::header};
@ -122,7 +119,7 @@ mod tests {
fn test_basics() { fn test_basics() {
let res = TestResponse::default() let res = TestResponse::default()
.version(Version::HTTP_2) .version(Version::HTTP_2)
.set(header::Date(SystemTime::now().into())) .insert_header((header::DATE, HttpDate::from(SystemTime::now())))
.cookie(cookie::Cookie::build("name", "value").finish()) .cookie(cookie::Cookie::build("name", "value").finish())
.finish(); .finish();
assert!(res.headers().contains_key(header::SET_COOKIE)); assert!(res.headers().contains_key(header::SET_COOKIE));

View File

@ -6,7 +6,7 @@
//! //!
//! ```no_run //! ```no_run
//! use awc::{Client, ws}; //! use awc::{Client, ws};
//! use futures_util::{sink::SinkExt, stream::StreamExt}; //! use futures_util::{sink::SinkExt as _, stream::StreamExt as _};
//! //!
//! #[actix_rt::main] //! #[actix_rt::main]
//! async fn main() { //! async fn main() {

View File

@ -5,12 +5,13 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use actix_utils::future::ok;
use brotli2::write::BrotliEncoder; use brotli2::write::BrotliEncoder;
use bytes::Bytes; use bytes::Bytes;
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
use flate2::write::GzEncoder; use flate2::write::GzEncoder;
use flate2::Compression; use flate2::Compression;
use futures_util::{future::ok, stream}; use futures_util::stream;
use rand::Rng; use rand::Rng;
use actix_http::{ use actix_http::{
@ -159,7 +160,7 @@ async fn test_timeout_override() {
#[actix_rt::test] #[actix_rt::test]
async fn test_response_timeout() { async fn test_response_timeout() {
use futures_util::stream::{once, StreamExt}; use futures_util::stream::{once, StreamExt as _};
let srv = actix_test::start(|| { let srv = actix_test::start(|| {
App::new().service(web::resource("/").route(web::to(|| async { App::new().service(web::resource("/").route(web::to(|| async {

View File

@ -13,8 +13,8 @@ use std::{
use actix_http::HttpService; use actix_http::HttpService;
use actix_http_test::test_server; use actix_http_test::test_server;
use actix_service::{map_config, pipeline_factory, ServiceFactoryExt}; use actix_service::{map_config, pipeline_factory, ServiceFactoryExt};
use actix_utils::future::ok;
use actix_web::{dev::AppConfig, http::Version, web, App, HttpResponse}; use actix_web::{dev::AppConfig, http::Version, web, App, HttpResponse};
use futures_util::future::ok;
use rustls::internal::pemfile::{certs, pkcs8_private_keys}; use rustls::internal::pemfile::{certs, pkcs8_private_keys};
use rustls::{ClientConfig, NoClientAuth, ServerConfig}; use rustls::{ClientConfig, NoClientAuth, ServerConfig};

View File

@ -8,9 +8,9 @@ use std::sync::Arc;
use actix_http::HttpService; use actix_http::HttpService;
use actix_http_test::test_server; use actix_http_test::test_server;
use actix_service::{map_config, pipeline_factory, ServiceFactoryExt}; use actix_service::{map_config, pipeline_factory, ServiceFactoryExt};
use actix_utils::future::ok;
use actix_web::http::Version; use actix_web::http::Version;
use actix_web::{dev::AppConfig, web, App, HttpResponse}; use actix_web::{dev::AppConfig, web, App, HttpResponse};
use futures_util::future::ok;
use openssl::{ use openssl::{
pkey::PKey, pkey::PKey,
ssl::{SslAcceptor, SslConnector, SslMethod, SslVerifyMode}, ssl::{SslAcceptor, SslConnector, SslMethod, SslVerifyMode},

View File

@ -3,9 +3,9 @@ use std::io;
use actix_codec::Framed; use actix_codec::Framed;
use actix_http::{body::BodySize, h1, ws, Error, HttpService, Request, Response}; use actix_http::{body::BodySize, h1, ws, Error, HttpService, Request, Response};
use actix_http_test::test_server; use actix_http_test::test_server;
use actix_utils::future::ok;
use bytes::Bytes; use bytes::Bytes;
use futures_util::future::ok; use futures_util::{SinkExt as _, StreamExt as _};
use futures_util::{SinkExt, StreamExt};
async fn ws_service(req: ws::Frame) -> Result<ws::Message, io::Error> { async fn ws_service(req: ws::Frame) -> Result<ws::Message, io::Error> {
match req { match req {

View File

@ -1,12 +1,12 @@
use std::future::Future; use std::{future::Future, time::Instant};
use std::time::Instant;
use actix_http::Response; use actix_http::Response;
use actix_utils::future::{ready, Ready};
use actix_web::http::StatusCode; use actix_web::http::StatusCode;
use actix_web::test::TestRequest; use actix_web::test::TestRequest;
use actix_web::{error, Error, HttpRequest, HttpResponse, Responder}; use actix_web::{error, Error, HttpRequest, HttpResponse, Responder};
use criterion::{criterion_group, criterion_main, Criterion}; use criterion::{criterion_group, criterion_main, Criterion};
use futures_util::future::{ready, Either, Ready}; use futures_util::future::{join_all, Either};
// responder simulate the old responder trait. // responder simulate the old responder trait.
trait FutureResponder { trait FutureResponder {
@ -79,7 +79,7 @@ fn future_responder(c: &mut Criterion) {
.await .await
}); });
let futs = futures_util::future::join_all(futs); let futs = join_all(futs);
let start = Instant::now(); let start = Instant::now();

View File

@ -1,19 +1,37 @@
digraph { digraph {
rankdir=TB
subgraph cluster_net { subgraph cluster_net {
label="actix/actix-net"; label="actix-net"
"actix-codec" "actix-macros" "actix-rt" "actix-server" "actix-service" "actix-codec" "actix-macros" "actix-rt" "actix-server" "actix-service"
"actix-tls" "actix-tracing" "actix-utils" "actix-router" "actix-tls" "actix-tracing" "actix-utils" "actix-router"
"local-channel" "local-waker"
} }
"actix-codec" -> { "actix-rt" "actix-service" "local-channel" "tokio" } subgraph cluster_other {
"actix-utils" -> { "actix-rt" "actix-service" "local-waker" } label="other actix owned crates"
{ rank=same; "local-channel" "local-waker" "bytestring" }
}
subgraph cluster_tokio {
label="tokio"
"tokio" "tokio-util"
}
"actix-codec" -> { "tokio" }
"actix-codec" -> { "tokio-util" }[color=red]
"actix-utils" -> { "local-waker" }
"actix-tracing" -> { "actix-service" } "actix-tracing" -> { "actix-service" }
"actix-tls" -> { "actix-service" "actix-codec" "actix-utils" "actix-rt" } "actix-tls" -> { "actix-service" "actix-codec" "actix-utils" "actix-rt" }
"actix-server" -> { "actix-service" "actix-rt" "actix-codec" "actix-utils" "tokio" } "actix-tls" -> { "tokio-util" }[color="#009900"]
"actix-server" -> { "actix-service" "actix-rt" "actix-utils" "tokio" }
"actix-rt" -> { "actix-macros" "tokio" } "actix-rt" -> { "actix-macros" "tokio" }
"actix-router" -> { "bytestring" }
"local-channel" -> { "local-waker" } "local-channel" -> { "local-waker" }
"tokio" [fontcolor = darkgreen] // invisible edges to force nicer layout
edge [style=invis]
"actix-macros" -> "tokio"
"actix-service" -> "bytestring"
"actix-macros" -> "bytestring"
} }

View File

@ -10,7 +10,7 @@ use actix_service::boxed::{self, BoxServiceFactory};
use actix_service::{ use actix_service::{
apply, apply_fn_factory, IntoServiceFactory, ServiceFactory, ServiceFactoryExt, Transform, apply, apply_fn_factory, IntoServiceFactory, ServiceFactory, ServiceFactoryExt, Transform,
}; };
use futures_util::future::FutureExt; use futures_util::future::FutureExt as _;
use crate::app_service::{AppEntry, AppInit, AppRoutingFactory}; use crate::app_service::{AppEntry, AppInit, AppRoutingFactory};
use crate::config::ServiceConfig; use crate::config::ServiceConfig;
@ -465,8 +465,8 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use actix_service::Service; use actix_service::Service;
use actix_utils::future::{err, ok};
use bytes::Bytes; use bytes::Bytes;
use futures_util::future::{err, ok};
use super::*; use super::*;
use crate::http::{header, HeaderValue, Method, StatusCode}; use crate::http::{header, HeaderValue, Method, StatusCode};

View File

@ -4,7 +4,8 @@ use std::sync::Arc;
use actix_http::error::{Error, ErrorInternalServerError}; use actix_http::error::{Error, ErrorInternalServerError};
use actix_http::Extensions; use actix_http::Extensions;
use futures_util::future::{err, ok, LocalBoxFuture, Ready}; use actix_utils::future::{err, ok, Ready};
use futures_core::future::LocalBoxFuture;
use serde::Serialize; use serde::Serialize;
use crate::dev::Payload; use crate::dev::Payload;
@ -147,14 +148,20 @@ impl<T: ?Sized + 'static> DataFactory for Data<T> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::sync::atomic::{AtomicUsize, Ordering};
use actix_service::Service; use actix_service::Service;
use super::*; use super::*;
use crate::dev::Service;
use crate::http::StatusCode;
use crate::test::{self, init_service, TestRequest};
use crate::{ use crate::{
http::StatusCode, http::StatusCode,
test::{init_service, TestRequest}, test::{init_service, TestRequest},
web, App, HttpResponse, web, App, HttpResponse,
}; };
use crate::{web, App, HttpResponse};
#[actix_rt::test] #[actix_rt::test]
async fn test_data_extractor() { async fn test_data_extractor() {

View File

@ -6,10 +6,8 @@ use std::{
task::{Context, Poll}, task::{Context, Poll},
}; };
use futures_util::{ use actix_utils::future::{ready, Ready};
future::{ready, Ready}, use futures_core::ready;
ready,
};
use crate::{dev::Payload, Error, HttpRequest}; use crate::{dev::Payload, Error, HttpRequest};

View File

@ -5,8 +5,8 @@ use std::task::{Context, Poll};
use actix_http::{Error, Response}; use actix_http::{Error, Response};
use actix_service::{Service, ServiceFactory}; use actix_service::{Service, ServiceFactory};
use futures_util::future::{ready, Ready}; use actix_utils::future::{ready, Ready};
use futures_util::ready; use futures_core::ready;
use pin_project::pin_project; use pin_project::pin_project;
use crate::extract::FromRequest; use crate::extract::FromRequest;

View File

@ -2,10 +2,10 @@ use std::cmp::Ordering;
use mime::Mime; use mime::Mime;
use crate::header::{qitem, QualityItem}; use super::{qitem, QualityItem};
use crate::http::header; use crate::http::header;
header! { crate::header! {
/// `Accept` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.2) /// `Accept` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.2)
/// ///
/// The `Accept` header field can be used by user agents to specify /// The `Accept` header field can be used by user agents to specify
@ -33,10 +33,10 @@ header! {
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{Accept, qitem}; /// use actix_web::http::header::{Accept, qitem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// Accept(vec![ /// Accept(vec![
/// qitem(mime::TEXT_HTML), /// qitem(mime::TEXT_HTML),
@ -45,10 +45,10 @@ header! {
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{Accept, qitem}; /// use actix_web::http::header::{Accept, qitem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// Accept(vec![ /// Accept(vec![
/// qitem(mime::APPLICATION_JSON), /// qitem(mime::APPLICATION_JSON),
@ -57,10 +57,10 @@ header! {
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{Accept, QualityItem, q, qitem}; /// use actix_web::http::header::{Accept, QualityItem, q, qitem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// Accept(vec![ /// Accept(vec![
/// qitem(mime::TEXT_HTML), /// qitem(mime::TEXT_HTML),
@ -116,8 +116,8 @@ header! {
#[test] #[test]
fn test_fuzzing1() { fn test_fuzzing1() {
use crate::test::TestRequest; use actix_http::test::TestRequest;
let req = TestRequest::default().insert_header((crate::header::ACCEPT, "chunk#;e")).finish(); let req = TestRequest::default().insert_header((crate::http::header::ACCEPT, "chunk#;e")).finish();
let header = Accept::parse(&req); let header = Accept::parse(&req);
assert!(header.is_ok()); assert!(header.is_ok());
} }
@ -213,7 +213,7 @@ impl Accept {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::header::q; use crate::http::header::q;
#[test] #[test]
fn test_mime_precedence() { fn test_mime_precedence() {

View File

@ -1,6 +1,6 @@
use crate::header::{Charset, QualityItem, ACCEPT_CHARSET}; use super::{Charset, QualityItem, ACCEPT_CHARSET};
header! { crate::header! {
/// `Accept-Charset` header, defined in /// `Accept-Charset` header, defined in
/// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.3) /// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.3)
/// ///
@ -22,20 +22,20 @@ header! {
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{AcceptCharset, Charset, qitem}; /// use actix_web::http::header::{AcceptCharset, Charset, qitem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// AcceptCharset(vec![qitem(Charset::Us_Ascii)]) /// AcceptCharset(vec![qitem(Charset::Us_Ascii)])
/// ); /// );
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{AcceptCharset, Charset, q, QualityItem}; /// use actix_web::http::header::{AcceptCharset, Charset, q, QualityItem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// AcceptCharset(vec![ /// AcceptCharset(vec![
/// QualityItem::new(Charset::Us_Ascii, q(900)), /// QualityItem::new(Charset::Us_Ascii, q(900)),
@ -45,10 +45,10 @@ header! {
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{AcceptCharset, Charset, qitem}; /// use actix_web::http::header::{AcceptCharset, Charset, qitem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// AcceptCharset(vec![qitem(Charset::Ext("utf-8".to_owned()))]) /// AcceptCharset(vec![qitem(Charset::Ext("utf-8".to_owned()))])
/// ); /// );

View File

@ -26,18 +26,20 @@ header! {
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use hyper::header::{Headers, AcceptEncoding, Encoding, qitem}; /// use actix_web::HttpResponse;
/// use actix_web::http::header::{AcceptEncoding, Encoding, qitem};
/// ///
/// let mut headers = Headers::new(); /// let mut builder = HttpResponse::new();
/// headers.set( /// builder.insert_header(
/// AcceptEncoding(vec![qitem(Encoding::Chunked)]) /// AcceptEncoding(vec![qitem(Encoding::Chunked)])
/// ); /// );
/// ``` /// ```
/// ``` /// ```
/// use hyper::header::{Headers, AcceptEncoding, Encoding, qitem}; /// use actix_web::HttpResponse;
/// use actix_web::http::header::{AcceptEncoding, Encoding, qitem};
/// ///
/// let mut headers = Headers::new(); /// let mut builder = HttpResponse::new();
/// headers.set( /// builder.insert_header(
/// AcceptEncoding(vec![ /// AcceptEncoding(vec![
/// qitem(Encoding::Chunked), /// qitem(Encoding::Chunked),
/// qitem(Encoding::Gzip), /// qitem(Encoding::Gzip),
@ -46,10 +48,11 @@ header! {
/// ); /// );
/// ``` /// ```
/// ``` /// ```
/// use hyper::header::{Headers, AcceptEncoding, Encoding, QualityItem, q, qitem}; /// use actix_web::HttpResponse;
/// use actix_web::http::header::{AcceptEncoding, Encoding, QualityItem, q, qitem};
/// ///
/// let mut headers = Headers::new(); /// let mut builder = HttpResponse::new();
/// headers.set( /// builder.insert_header(
/// AcceptEncoding(vec![ /// AcceptEncoding(vec![
/// qitem(Encoding::Chunked), /// qitem(Encoding::Chunked),
/// QualityItem::new(Encoding::Gzip, q(600)), /// QualityItem::new(Encoding::Gzip, q(600)),

View File

@ -1,7 +1,7 @@
use crate::header::{QualityItem, ACCEPT_LANGUAGE}; use super::{QualityItem, ACCEPT_LANGUAGE};
use language_tags::LanguageTag; use language_tags::LanguageTag;
header! { crate::header! {
/// `Accept-Language` header, defined in /// `Accept-Language` header, defined in
/// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.5) /// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.5)
/// ///
@ -24,10 +24,10 @@ header! {
/// ///
/// ``` /// ```
/// use language_tags::langtag; /// use language_tags::langtag;
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{AcceptLanguage, LanguageTag, qitem}; /// use actix_web::http::header::{AcceptLanguage, LanguageTag, qitem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// let mut langtag: LanguageTag = Default::default(); /// let mut langtag: LanguageTag = Default::default();
/// langtag.language = Some("en".to_owned()); /// langtag.language = Some("en".to_owned());
/// langtag.region = Some("US".to_owned()); /// langtag.region = Some("US".to_owned());
@ -40,10 +40,10 @@ header! {
/// ///
/// ``` /// ```
/// use language_tags::langtag; /// use language_tags::langtag;
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{AcceptLanguage, QualityItem, q, qitem}; /// use actix_web::http::header::{AcceptLanguage, QualityItem, q, qitem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// AcceptLanguage(vec![ /// AcceptLanguage(vec![
/// qitem(langtag!(da)), /// qitem(langtag!(da)),

View File

@ -1,7 +1,7 @@
use http::header; use actix_http::http::Method;
use http::Method; use crate::http::header;
header! { crate::header! {
/// `Allow` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.4.1) /// `Allow` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.4.1)
/// ///
/// The `Allow` header field lists the set of methods advertised as /// The `Allow` header field lists the set of methods advertised as
@ -23,20 +23,20 @@ header! {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::{header::Allow, Method}; /// use actix_web::http::{header::Allow, Method};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// Allow(vec![Method::GET]) /// Allow(vec![Method::GET])
/// ); /// );
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::{header::Allow, Method}; /// use actix_web::http::{header::Allow, Method};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// Allow(vec![ /// Allow(vec![
/// Method::GET, /// Method::GET,

View File

@ -1,12 +1,12 @@
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use std::str::FromStr; use std::str::FromStr;
use http::header; use super::{
use crate::header::{
fmt_comma_delimited, from_comma_delimited, Header, IntoHeaderValue, Writer, fmt_comma_delimited, from_comma_delimited, Header, IntoHeaderValue, Writer,
}; };
use crate::http::header;
/// `Cache-Control` header, defined in [RFC7234](https://tools.ietf.org/html/rfc7234#section-5.2) /// `Cache-Control` header, defined in [RFC7234](https://tools.ietf.org/html/rfc7234#section-5.2)
/// ///
/// The `Cache-Control` header field is used to specify directives for /// The `Cache-Control` header field is used to specify directives for
@ -29,18 +29,18 @@ use crate::header::{
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{CacheControl, CacheDirective}; /// use actix_web::http::header::{CacheControl, CacheDirective};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header(CacheControl(vec![CacheDirective::MaxAge(86400u32)])); /// builder.insert_header(CacheControl(vec![CacheDirective::MaxAge(86400u32)]));
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{CacheControl, CacheDirective}; /// use actix_web::http::header::{CacheControl, CacheDirective};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header(CacheControl(vec![ /// builder.insert_header(CacheControl(vec![
/// CacheDirective::NoCache, /// CacheDirective::NoCache,
/// CacheDirective::Private, /// CacheDirective::Private,
@ -191,8 +191,8 @@ impl FromStr for CacheDirective {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::header::Header; use crate::http::header::Header;
use crate::test::TestRequest; use actix_http::test::TestRequest;
#[test] #[test]
fn test_parse_multiple_headers() { fn test_parse_multiple_headers() {

View File

@ -10,7 +10,8 @@ use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use crate::header::{self, ExtendedValue, Header, IntoHeaderValue, Writer}; use crate::http::header;
use super::{ExtendedValue, Header, IntoHeaderValue, Writer};
/// Split at the index of the first `needle` if it exists or at the end. /// Split at the index of the first `needle` if it exists or at the end.
fn split_once(haystack: &str, needle: char) -> (&str, &str) { fn split_once(haystack: &str, needle: char) -> (&str, &str) {
@ -63,7 +64,7 @@ impl<'a> From<&'a str> for DispositionType {
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use actix_http::http::header::DispositionParam; /// use actix_web::http::header::DispositionParam;
/// ///
/// let param = DispositionParam::Filename(String::from("sample.txt")); /// let param = DispositionParam::Filename(String::from("sample.txt"));
/// assert!(param.is_filename()); /// assert!(param.is_filename());
@ -240,7 +241,7 @@ impl DispositionParam {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// use actix_http::http::header::{ /// use actix_web::http::header::{
/// Charset, ContentDisposition, DispositionParam, DispositionType, /// Charset, ContentDisposition, DispositionParam, DispositionType,
/// ExtendedValue, /// ExtendedValue,
/// }; /// };
@ -554,8 +555,8 @@ impl fmt::Display for ContentDisposition {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{ContentDisposition, DispositionParam, DispositionType}; use super::{ContentDisposition, DispositionParam, DispositionType};
use crate::header::shared::Charset; use crate::http::header::Charset;
use crate::header::{ExtendedValue, HeaderValue}; use crate::http::header::{ExtendedValue, HeaderValue};
#[test] #[test]
fn test_from_raw_basic() { fn test_from_raw_basic() {

View File

@ -1,7 +1,7 @@
use crate::header::{QualityItem, CONTENT_LANGUAGE}; use super::{QualityItem, CONTENT_LANGUAGE};
use language_tags::LanguageTag; use language_tags::LanguageTag;
header! { crate::header! {
/// `Content-Language` header, defined in /// `Content-Language` header, defined in
/// [RFC7231](https://tools.ietf.org/html/rfc7231#section-3.1.3.2) /// [RFC7231](https://tools.ietf.org/html/rfc7231#section-3.1.3.2)
/// ///
@ -25,10 +25,10 @@ header! {
/// ///
/// ``` /// ```
/// use language_tags::langtag; /// use language_tags::langtag;
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{ContentLanguage, qitem}; /// use actix_web::http::header::{ContentLanguage, qitem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// ContentLanguage(vec![ /// ContentLanguage(vec![
/// qitem(langtag!(en)), /// qitem(langtag!(en)),
@ -38,10 +38,10 @@ header! {
/// ///
/// ``` /// ```
/// use language_tags::langtag; /// use language_tags::langtag;
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{ContentLanguage, qitem}; /// use actix_web::http::header::{ContentLanguage, qitem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// ContentLanguage(vec![ /// ContentLanguage(vec![
/// qitem(langtag!(da)), /// qitem(langtag!(da)),

View File

@ -2,11 +2,11 @@ use std::fmt::{self, Display, Write};
use std::str::FromStr; use std::str::FromStr;
use crate::error::ParseError; use crate::error::ParseError;
use crate::header::{ use super::{
HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer, CONTENT_RANGE, HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer, CONTENT_RANGE,
}; };
header! { crate::header! {
/// `Content-Range` header, defined in /// `Content-Range` header, defined in
/// [RFC7233](http://tools.ietf.org/html/rfc7233#section-4.2) /// [RFC7233](http://tools.ietf.org/html/rfc7233#section-4.2)
(ContentRange, CONTENT_RANGE) => [ContentRangeSpec] (ContentRange, CONTENT_RANGE) => [ContentRangeSpec]

View File

@ -1,7 +1,7 @@
use crate::header::CONTENT_TYPE; use super::CONTENT_TYPE;
use mime::Mime; use mime::Mime;
header! { crate::header! {
/// `Content-Type` header, defined in /// `Content-Type` header, defined in
/// [RFC7231](http://tools.ietf.org/html/rfc7231#section-3.1.1.5) /// [RFC7231](http://tools.ietf.org/html/rfc7231#section-3.1.1.5)
/// ///
@ -31,20 +31,20 @@ header! {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::ContentType; /// use actix_web::http::header::ContentType;
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// ContentType::json() /// ContentType::json()
/// ); /// );
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::ContentType; /// use actix_web::http::header::ContentType;
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// ContentType(mime::TEXT_HTML) /// ContentType(mime::TEXT_HTML)
/// ); /// );

View File

@ -1,7 +1,7 @@
use crate::header::{HttpDate, DATE}; use super::{HttpDate, DATE};
use std::time::SystemTime; use std::time::SystemTime;
header! { crate::header! {
/// `Date` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.1.1.2) /// `Date` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.1.1.2)
/// ///
/// The `Date` header field represents the date and time at which the /// The `Date` header field represents the date and time at which the
@ -21,10 +21,10 @@ header! {
/// ///
/// ``` /// ```
/// use std::time::SystemTime; /// use std::time::SystemTime;
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::Date; /// use actix_web::http::header::Date;
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// Date(SystemTime::now().into()) /// Date(SystemTime::now().into())
/// ); /// );

View File

@ -1,7 +1,7 @@
use std::fmt::{self, Display, Write}; use std::fmt::{self, Display, Write};
use std::str::FromStr; use std::str::FromStr;
use crate::header::{HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer}; use super::{HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer};
/// check that each char in the slice is either: /// check that each char in the slice is either:
/// 1. `%x21`, or /// 1. `%x21`, or

View File

@ -1,6 +1,6 @@
use crate::header::{EntityTag, ETAG}; use super::{EntityTag, ETAG};
header! { crate::header! {
/// `ETag` header, defined in [RFC7232](http://tools.ietf.org/html/rfc7232#section-2.3) /// `ETag` header, defined in [RFC7232](http://tools.ietf.org/html/rfc7232#section-2.3)
/// ///
/// The `ETag` header field in a response provides the current entity-tag /// The `ETag` header field in a response provides the current entity-tag
@ -28,20 +28,20 @@ header! {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{ETag, EntityTag}; /// use actix_web::http::header::{ETag, EntityTag};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// ETag(EntityTag::new(false, "xyzzy".to_owned())) /// ETag(EntityTag::new(false, "xyzzy".to_owned()))
/// ); /// );
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{ETag, EntityTag}; /// use actix_web::http::header::{ETag, EntityTag};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// ETag(EntityTag::new(true, "xyzzy".to_owned())) /// ETag(EntityTag::new(true, "xyzzy".to_owned()))
/// ); /// );

View File

@ -1,6 +1,6 @@
use crate::header::{HttpDate, EXPIRES}; use super::{HttpDate, EXPIRES};
header! { crate::header! {
/// `Expires` header, defined in [RFC7234](http://tools.ietf.org/html/rfc7234#section-5.3) /// `Expires` header, defined in [RFC7234](http://tools.ietf.org/html/rfc7234#section-5.3)
/// ///
/// The `Expires` header field gives the date/time after which the /// The `Expires` header field gives the date/time after which the
@ -23,10 +23,10 @@ header! {
/// ///
/// ``` /// ```
/// use std::time::{SystemTime, Duration}; /// use std::time::{SystemTime, Duration};
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::Expires; /// use actix_web::http::header::Expires;
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// let expiration = SystemTime::now() + Duration::from_secs(60 * 60 * 24); /// let expiration = SystemTime::now() + Duration::from_secs(60 * 60 * 24);
/// builder.insert_header( /// builder.insert_header(
/// Expires(expiration.into()) /// Expires(expiration.into())

View File

@ -1,6 +1,6 @@
use crate::header::{EntityTag, IF_MATCH}; use super::{EntityTag, IF_MATCH};
header! { crate::header! {
/// `If-Match` header, defined in /// `If-Match` header, defined in
/// [RFC7232](https://tools.ietf.org/html/rfc7232#section-3.1) /// [RFC7232](https://tools.ietf.org/html/rfc7232#section-3.1)
/// ///
@ -30,18 +30,18 @@ header! {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::IfMatch; /// use actix_web::http::header::IfMatch;
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header(IfMatch::Any); /// builder.insert_header(IfMatch::Any);
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{IfMatch, EntityTag}; /// use actix_web::http::header::{IfMatch, EntityTag};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// IfMatch::Items(vec![ /// IfMatch::Items(vec![
/// EntityTag::new(false, "xyzzy".to_owned()), /// EntityTag::new(false, "xyzzy".to_owned()),

View File

@ -1,6 +1,6 @@
use crate::header::{HttpDate, IF_MODIFIED_SINCE}; use super::{HttpDate, IF_MODIFIED_SINCE};
header! { crate::header! {
/// `If-Modified-Since` header, defined in /// `If-Modified-Since` header, defined in
/// [RFC7232](http://tools.ietf.org/html/rfc7232#section-3.3) /// [RFC7232](http://tools.ietf.org/html/rfc7232#section-3.3)
/// ///
@ -23,10 +23,10 @@ header! {
/// ///
/// ``` /// ```
/// use std::time::{SystemTime, Duration}; /// use std::time::{SystemTime, Duration};
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::IfModifiedSince; /// use actix_web::http::header::IfModifiedSince;
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24); /// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24);
/// builder.insert_header( /// builder.insert_header(
/// IfModifiedSince(modified.into()) /// IfModifiedSince(modified.into())

View File

@ -1,6 +1,6 @@
use crate::header::{EntityTag, IF_NONE_MATCH}; use super::{EntityTag, IF_NONE_MATCH};
header! { crate::header! {
/// `If-None-Match` header, defined in /// `If-None-Match` header, defined in
/// [RFC7232](https://tools.ietf.org/html/rfc7232#section-3.2) /// [RFC7232](https://tools.ietf.org/html/rfc7232#section-3.2)
/// ///
@ -32,18 +32,18 @@ header! {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::IfNoneMatch; /// use actix_web::http::header::IfNoneMatch;
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header(IfNoneMatch::Any); /// builder.insert_header(IfNoneMatch::Any);
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{IfNoneMatch, EntityTag}; /// use actix_web::http::header::{IfNoneMatch, EntityTag};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// IfNoneMatch::Items(vec![ /// IfNoneMatch::Items(vec![
/// EntityTag::new(false, "xyzzy".to_owned()), /// EntityTag::new(false, "xyzzy".to_owned()),
@ -66,8 +66,8 @@ header! {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::IfNoneMatch; use super::IfNoneMatch;
use crate::header::{EntityTag, Header, IF_NONE_MATCH}; use crate::http::header::{EntityTag, Header, IF_NONE_MATCH};
use crate::test::TestRequest; use actix_http::test::TestRequest;
#[test] #[test]
fn test_if_none_match() { fn test_if_none_match() {

View File

@ -1,8 +1,9 @@
use std::fmt::{self, Display, Write}; use std::fmt::{self, Display, Write};
use crate::http::header;
use crate::error::ParseError; use crate::error::ParseError;
use crate::header::{ use super::{
self, from_one_raw_str, EntityTag, Header, HeaderName, HeaderValue, HttpDate, from_one_raw_str, EntityTag, Header, HeaderName, HeaderValue, HttpDate,
IntoHeaderValue, InvalidHeaderValue, Writer, IntoHeaderValue, InvalidHeaderValue, Writer,
}; };
use crate::HttpMessage; use crate::HttpMessage;
@ -36,10 +37,10 @@ use crate::HttpMessage;
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{EntityTag, IfRange}; /// use actix_web::http::header::{EntityTag, IfRange};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// IfRange::EntityTag( /// IfRange::EntityTag(
/// EntityTag::new(false, "abc".to_owned()) /// EntityTag::new(false, "abc".to_owned())
@ -49,9 +50,9 @@ use crate::HttpMessage;
/// ///
/// ``` /// ```
/// use std::time::{Duration, SystemTime}; /// use std::time::{Duration, SystemTime};
/// use actix_http::{http::header::IfRange, Response}; /// use actix_web::{http::header::IfRange, HttpResponse};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// let fetched = SystemTime::now() - Duration::from_secs(60 * 60 * 24); /// let fetched = SystemTime::now() - Duration::from_secs(60 * 60 * 24);
/// builder.insert_header( /// builder.insert_header(
/// IfRange::Date(fetched.into()) /// IfRange::Date(fetched.into())
@ -111,7 +112,7 @@ impl IntoHeaderValue for IfRange {
#[cfg(test)] #[cfg(test)]
mod test_if_range { mod test_if_range {
use super::IfRange as HeaderField; use super::IfRange as HeaderField;
use crate::header::*; use crate::http::header::*;
use std::str; use std::str;
test_header!(test1, vec![b"Sat, 29 Oct 1994 19:43:31 GMT"]); test_header!(test1, vec![b"Sat, 29 Oct 1994 19:43:31 GMT"]);

View File

@ -1,6 +1,6 @@
use crate::header::{HttpDate, IF_UNMODIFIED_SINCE}; use super::{HttpDate, IF_UNMODIFIED_SINCE};
header! { crate::header! {
/// `If-Unmodified-Since` header, defined in /// `If-Unmodified-Since` header, defined in
/// [RFC7232](http://tools.ietf.org/html/rfc7232#section-3.4) /// [RFC7232](http://tools.ietf.org/html/rfc7232#section-3.4)
/// ///
@ -24,10 +24,10 @@ header! {
/// ///
/// ``` /// ```
/// use std::time::{SystemTime, Duration}; /// use std::time::{SystemTime, Duration};
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::IfUnmodifiedSince; /// use actix_web::http::header::IfUnmodifiedSince;
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24); /// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24);
/// builder.insert_header( /// builder.insert_header(
/// IfUnmodifiedSince(modified.into()) /// IfUnmodifiedSince(modified.into())

View File

@ -1,6 +1,6 @@
use crate::header::{HttpDate, LAST_MODIFIED}; use super::{HttpDate, LAST_MODIFIED};
header! { crate::header! {
/// `Last-Modified` header, defined in /// `Last-Modified` header, defined in
/// [RFC7232](http://tools.ietf.org/html/rfc7232#section-2.2) /// [RFC7232](http://tools.ietf.org/html/rfc7232#section-2.2)
/// ///
@ -23,10 +23,10 @@ header! {
/// ///
/// ``` /// ```
/// use std::time::{SystemTime, Duration}; /// use std::time::{SystemTime, Duration};
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::LastModified; /// use actix_web::http::header::LastModified;
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24); /// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24);
/// builder.insert_header( /// builder.insert_header(
/// LastModified(modified.into()) /// LastModified(modified.into())

View File

@ -7,6 +7,10 @@
//! is used, such as `ContentType(pub Mime)`. //! is used, such as `ContentType(pub Mime)`.
#![cfg_attr(rustfmt, rustfmt_skip)] #![cfg_attr(rustfmt, rustfmt_skip)]
use std::fmt;
use bytes::{BytesMut, Bytes};
pub use actix_http::http::header::*;
pub use self::accept_charset::AcceptCharset; pub use self::accept_charset::AcceptCharset;
//pub use self::accept_encoding::AcceptEncoding; //pub use self::accept_encoding::AcceptEncoding;
pub use self::accept::Accept; pub use self::accept::Accept;
@ -18,7 +22,6 @@ pub use self::content_disposition::{
}; };
pub use self::content_language::ContentLanguage; pub use self::content_language::ContentLanguage;
pub use self::content_range::{ContentRange, ContentRangeSpec}; pub use self::content_range::{ContentRange, ContentRangeSpec};
pub use self::content_encoding::{ContentEncoding};
pub use self::content_type::ContentType; pub use self::content_type::ContentType;
pub use self::date::Date; pub use self::date::Date;
pub use self::etag::ETag; pub use self::etag::ETag;
@ -29,7 +32,39 @@ pub use self::if_none_match::IfNoneMatch;
pub use self::if_range::IfRange; pub use self::if_range::IfRange;
pub use self::if_unmodified_since::IfUnmodifiedSince; pub use self::if_unmodified_since::IfUnmodifiedSince;
pub use self::last_modified::LastModified; pub use self::last_modified::LastModified;
pub use self::encoding::Encoding;
pub use self::entity::EntityTag;
//pub use self::range::{Range, ByteRangeSpec}; //pub use self::range::{Range, ByteRangeSpec};
pub(crate) use actix_http::http::header::{fmt_comma_delimited, from_comma_delimited, from_one_raw_str};
#[derive(Debug, Default)]
struct Writer {
buf: BytesMut,
}
impl Writer {
pub fn new() -> Writer {
Writer::default()
}
pub fn take(&mut self) -> Bytes {
self.buf.split().freeze()
}
}
impl fmt::Write for Writer {
#[inline]
fn write_str(&mut self, s: &str) -> fmt::Result {
self.buf.extend_from_slice(s.as_bytes());
Ok(())
}
#[inline]
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
fmt::write(self, args)
}
}
#[doc(hidden)] #[doc(hidden)]
#[macro_export] #[macro_export]
@ -61,9 +96,9 @@ macro_rules! __hyper__tm {
#[cfg(test)] #[cfg(test)]
mod $tm{ mod $tm{
use std::str; use std::str;
use http::Method; use actix_http::http::Method;
use mime::*; use mime::*;
use $crate::header::*; use $crate::http::header::*;
use super::$id as HeaderField; use super::$id as HeaderField;
$($tf)* $($tf)*
} }
@ -77,8 +112,7 @@ macro_rules! test_header {
($id:ident, $raw:expr) => { ($id:ident, $raw:expr) => {
#[test] #[test]
fn $id() { fn $id() {
use super::*; use actix_http::test;
use $crate::test;
let raw = $raw; let raw = $raw;
let a: Vec<Vec<u8>> = raw.iter().map(|x| x.to_vec()).collect(); let a: Vec<Vec<u8>> = raw.iter().map(|x| x.to_vec()).collect();
@ -106,7 +140,7 @@ macro_rules! test_header {
($id:ident, $raw:expr, $typed:expr) => { ($id:ident, $raw:expr, $typed:expr) => {
#[test] #[test]
fn $id() { fn $id() {
use $crate::test; use actix_http::test;
let a: Vec<Vec<u8>> = $raw.iter().map(|x| x.to_vec()).collect(); let a: Vec<Vec<u8>> = $raw.iter().map(|x| x.to_vec()).collect();
let mut req = test::TestRequest::default(); let mut req = test::TestRequest::default();
@ -134,6 +168,7 @@ macro_rules! test_header {
}; };
} }
#[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! header { macro_rules! header {
// $a:meta: Attributes associated with the header item (usually docs) // $a:meta: Attributes associated with the header item (usually docs)
@ -341,7 +376,6 @@ mod allow;
mod cache_control; mod cache_control;
mod content_disposition; mod content_disposition;
mod content_language; mod content_language;
mod content_encoding;
mod content_range; mod content_range;
mod content_type; mod content_type;
mod date; mod date;
@ -353,3 +387,5 @@ mod if_none_match;
mod if_range; mod if_range;
mod if_unmodified_since; mod if_unmodified_since;
mod last_modified; mod last_modified;
mod encoding;
mod entity;

View File

@ -1,8 +1,8 @@
use std::fmt::{self, Display}; use std::fmt::{self, Display};
use std::str::FromStr; use std::str::FromStr;
use header::parsing::from_one_raw_str; use super::parsing::from_one_raw_str;
use header::{Header, Raw}; use super::{Header, Raw};
/// `Range` header, defined in [RFC7233](https://tools.ietf.org/html/rfc7233#section-3.1) /// `Range` header, defined in [RFC7233](https://tools.ietf.org/html/rfc7233#section-3.1)
/// ///

2
src/http/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod header;
pub use actix_http::http::*;

View File

@ -79,6 +79,7 @@ pub mod error;
mod extract; mod extract;
pub mod guard; pub mod guard;
mod handler; mod handler;
pub mod http;
mod info; mod info;
pub mod middleware; pub mod middleware;
mod request; mod request;
@ -97,7 +98,7 @@ pub mod web;
#[cfg(feature = "cookies")] #[cfg(feature = "cookies")]
pub use actix_http::cookie; pub use actix_http::cookie;
pub use actix_http::Response as HttpResponse; pub use actix_http::Response as HttpResponse;
pub use actix_http::{body, http, Error, HttpMessage, ResponseError, Result}; pub use actix_http::{body, Error, HttpMessage, ResponseError, Result};
pub use actix_rt as rt; pub use actix_rt as rt;
pub use actix_web_codegen::*; pub use actix_web_codegen::*;

View File

@ -16,8 +16,8 @@ use actix_http::{
Error, Error,
}; };
use actix_service::{Service, Transform}; use actix_service::{Service, Transform};
use actix_utils::future::{ok, Ready};
use futures_core::ready; use futures_core::ready;
use futures_util::future::{ok, Ready};
use pin_project::pin_project; use pin_project::pin_project;
use crate::{ use crate::{

View File

@ -3,7 +3,9 @@
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use actix_service::{Service, Transform}; use actix_service::{Service, Transform};
use futures_util::future::{Either, FutureExt, LocalBoxFuture}; use actix_utils::future::Either;
use futures_core::future::LocalBoxFuture;
use futures_util::future::FutureExt as _;
/// Middleware for conditionally enabling other middleware. /// Middleware for conditionally enabling other middleware.
/// ///
@ -85,8 +87,8 @@ where
fn call(&self, req: Req) -> Self::Future { fn call(&self, req: Req) -> Self::Future {
match self { match self {
ConditionMiddleware::Enable(service) => Either::Left(service.call(req)), ConditionMiddleware::Enable(service) => Either::left(service.call(req)),
ConditionMiddleware::Disable(service) => Either::Right(service.call(req)), ConditionMiddleware::Disable(service) => Either::right(service.call(req)),
} }
} }
} }
@ -94,7 +96,7 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use actix_service::IntoService; use actix_service::IntoService;
use futures_util::future::ok; use actix_utils::future::ok;
use super::*; use super::*;
use crate::{ use crate::{

View File

@ -9,10 +9,8 @@ use std::{
task::{Context, Poll}, task::{Context, Poll},
}; };
use futures_util::{ use actix_utils::future::{ready, Ready};
future::{ready, Ready}, use futures_core::ready;
ready,
};
use crate::{ use crate::{
dev::{Service, Transform}, dev::{Service, Transform},
@ -188,7 +186,7 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use actix_service::IntoService; use actix_service::IntoService;
use futures_util::future::ok; use actix_utils::future::ok;
use super::*; use super::*;
use crate::{ use crate::{

View File

@ -175,7 +175,8 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use actix_service::IntoService; use actix_service::IntoService;
use futures_util::future::{ok, FutureExt}; use actix_utils::future::ok;
use futures_util::future::FutureExt as _;
use super::*; use super::*;
use crate::http::{header::CONTENT_TYPE, HeaderValue, StatusCode}; use crate::http::{header::CONTENT_TYPE, HeaderValue, StatusCode};

View File

@ -13,8 +13,9 @@ use std::{
}; };
use actix_service::{Service, Transform}; use actix_service::{Service, Transform};
use actix_utils::future::{ok, Ready};
use bytes::Bytes; use bytes::Bytes;
use futures_util::future::{ok, Ready}; use futures_core::ready;
use log::{debug, warn}; use log::{debug, warn};
use regex::{Regex, RegexSet}; use regex::{Regex, RegexSet};
use time::OffsetDateTime; use time::OffsetDateTime;
@ -269,7 +270,7 @@ where
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project(); let this = self.project();
let res = match futures_util::ready!(this.fut.poll(cx)) { let res = match ready!(this.fut.poll(cx)) {
Ok(res) => res, Ok(res) => res,
Err(e) => return Poll::Ready(Err(e)), Err(e) => return Poll::Ready(Err(e)),
}; };
@ -588,7 +589,7 @@ impl<'a> fmt::Display for FormatDisplay<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use actix_service::{IntoService, Service, Transform}; use actix_service::{IntoService, Service, Transform};
use futures_util::future::ok; use actix_utils::future::ok;
use super::*; use super::*;
use crate::http::{header, StatusCode}; use crate::http::{header, StatusCode};

View File

@ -2,8 +2,8 @@
use actix_http::http::{PathAndQuery, Uri}; use actix_http::http::{PathAndQuery, Uri};
use actix_service::{Service, Transform}; use actix_service::{Service, Transform};
use actix_utils::future::{ready, Ready};
use bytes::Bytes; use bytes::Bytes;
use futures_util::future::{ready, Ready};
use regex::Regex; use regex::Regex;
use crate::{ use crate::{

View File

@ -5,7 +5,7 @@ use std::{fmt, net};
use actix_http::http::{HeaderMap, Method, Uri, Version}; use actix_http::http::{HeaderMap, Method, Uri, Version};
use actix_http::{Error, Extensions, HttpMessage, Message, Payload, RequestHead}; use actix_http::{Error, Extensions, HttpMessage, Message, Payload, RequestHead};
use actix_router::{Path, Url}; use actix_router::{Path, Url};
use futures_util::future::{ok, Ready}; use actix_utils::future::{ok, Ready};
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::app_service::AppInitServiceState; use crate::app_service::AppInitServiceState;

View File

@ -1,7 +1,7 @@
use std::{any::type_name, ops::Deref}; use std::{any::type_name, ops::Deref};
use actix_http::error::{Error, ErrorInternalServerError}; use actix_http::error::{Error, ErrorInternalServerError};
use futures_util::future; use actix_utils::future::{err, ok, Ready};
use crate::{dev::Payload, FromRequest, HttpRequest}; use crate::{dev::Payload, FromRequest, HttpRequest};
@ -67,11 +67,11 @@ impl<T: Clone + 'static> Deref for ReqData<T> {
impl<T: Clone + 'static> FromRequest for ReqData<T> { impl<T: Clone + 'static> FromRequest for ReqData<T> {
type Config = (); type Config = ();
type Error = Error; type Error = Error;
type Future = future::Ready<Result<Self, Error>>; type Future = Ready<Result<Self, Error>>;
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
if let Some(st) = req.extensions().get::<T>() { if let Some(st) = req.extensions().get::<T>() {
future::ok(ReqData(st.clone())) ok(ReqData(st.clone()))
} else { } else {
log::debug!( log::debug!(
"Failed to construct App-level ReqData extractor. \ "Failed to construct App-level ReqData extractor. \
@ -79,7 +79,7 @@ impl<T: Clone + 'static> FromRequest for ReqData<T> {
req.path(), req.path(),
type_name::<T>(), type_name::<T>(),
); );
future::err(ErrorInternalServerError( err(ErrorInternalServerError(
"Missing expected request extension data", "Missing expected request extension data",
)) ))
} }

View File

@ -519,7 +519,7 @@ mod tests {
use actix_rt::time::sleep; use actix_rt::time::sleep;
use actix_service::Service; use actix_service::Service;
use futures_util::future::ok; use actix_utils::future::ok;
use crate::http::{header, HeaderValue, Method, StatusCode}; use crate::http::{header, HeaderValue, Method, StatusCode};
use crate::middleware::DefaultHeaders; use crate::middleware::DefaultHeaders;

View File

@ -575,8 +575,8 @@ impl ServiceFactory<ServiceRequest> for ScopeEndpoint {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use actix_service::Service; use actix_service::Service;
use actix_utils::future::ok;
use bytes::Bytes; use bytes::Bytes;
use futures_util::future::ok;
use crate::dev::{Body, ResponseBody}; use crate::dev::{Body, ResponseBody};
use crate::http::{header, HeaderValue, Method, StatusCode}; use crate::http::{header, HeaderValue, Method, StatusCode};

View File

@ -400,6 +400,7 @@ where
})) }))
.rustls(config.clone()) .rustls(config.clone())
})?; })?;
Ok(self) Ok(self)
} }

View File

@ -608,7 +608,7 @@ mod tests {
use crate::test::{init_service, TestRequest}; use crate::test::{init_service, TestRequest};
use crate::{guard, http, web, App, HttpResponse}; use crate::{guard, http, web, App, HttpResponse};
use actix_service::Service; use actix_service::Service;
use futures_util::future::ok; use actix_utils::future::ok;
#[actix_rt::test] #[actix_rt::test]
async fn test_service() { async fn test_service() {

View File

@ -6,17 +6,15 @@ use std::{net::SocketAddr, rc::Rc};
use actix_http::cookie::Cookie; use actix_http::cookie::Cookie;
pub use actix_http::test::TestBuffer; pub use actix_http::test::TestBuffer;
use actix_http::{ use actix_http::{
http::{ http::{header::IntoHeaderPair, Method, StatusCode, Uri, Version},
header::{ContentType, IntoHeaderPair},
Method, StatusCode, Uri, Version,
},
test::TestRequest as HttpTestRequest, test::TestRequest as HttpTestRequest,
Extensions, Request, Extensions, Request,
}; };
use actix_router::{Path, ResourceDef, Url}; use actix_router::{Path, ResourceDef, Url};
use actix_service::{IntoService, IntoServiceFactory, Service, ServiceFactory}; use actix_service::{IntoService, IntoServiceFactory, Service, ServiceFactory};
use actix_utils::future::ok;
use futures_core::Stream; use futures_core::Stream;
use futures_util::{future::ok, StreamExt}; use futures_util::StreamExt as _;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use crate::{ use crate::{
@ -24,6 +22,7 @@ use crate::{
config::AppConfig, config::AppConfig,
data::Data, data::Data,
dev::{Body, MessageBody, Payload}, dev::{Body, MessageBody, Payload},
http::header::ContentType,
rmap::ResourceMap, rmap::ResourceMap,
service::{ServiceRequest, ServiceResponse}, service::{ServiceRequest, ServiceResponse},
web::{Bytes, BytesMut}, web::{Bytes, BytesMut},
@ -156,7 +155,7 @@ where
let mut resp = app let mut resp = app
.call(req) .call(req)
.await .await
.expect("read_response failed at application call"); .unwrap_or_else(|e| panic!("read_response failed at application call: {}", e));
let mut body = resp.take_body(); let mut body = resp.take_body();
let mut bytes = BytesMut::new(); let mut bytes = BytesMut::new();
@ -250,8 +249,12 @@ where
{ {
let body = read_body(res).await; let body = read_body(res).await;
serde_json::from_slice(&body) serde_json::from_slice(&body).unwrap_or_else(|e| {
.unwrap_or_else(|e| panic!("read_response_json failed during deserialization: {}", e)) panic!(
"read_response_json failed during deserialization of body: {:?}, {}",
body, e
)
})
} }
pub async fn load_stream<S>(mut stream: S) -> Result<Bytes, Error> pub async fn load_stream<S>(mut stream: S) -> Result<Bytes, Error>
@ -307,8 +310,12 @@ where
{ {
let body = read_response(app, req).await; let body = read_response(app, req).await;
serde_json::from_slice(&body) serde_json::from_slice(&body).unwrap_or_else(|_| {
.unwrap_or_else(|_| panic!("read_response_json failed during deserialization")) panic!(
"read_response_json failed during deserialization of body: {:?}",
body
)
})
} }
/// Test `Request` builder. /// Test `Request` builder.
@ -563,9 +570,10 @@ impl TestRequest {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::time::SystemTime;
use actix_http::HttpMessage; use actix_http::HttpMessage;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::time::SystemTime;
use super::*; use super::*;
use crate::{http::header, web, App, HttpResponse, Responder}; use crate::{http::header, web, App, HttpResponse, Responder};

Some files were not shown because too many files have changed in this diff Show More