1
0
mirror of https://github.com/actix/actix-extras.git synced 2025-01-23 15:24:36 +01:00

Merge branch 'master' into master

This commit is contained in:
Yuki Okushi 2019-12-06 16:57:42 +09:00 committed by GitHub
commit f2ba389496
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
107 changed files with 1857 additions and 1575 deletions

View File

@ -1,5 +1,12 @@
# Changes # Changes
## [2.0.0-alpha.3] - 2019-12-xx
### Changed
* Migrate to tokio 0.2
## [2.0.0-alpha.1] - 2019-11-22 ## [2.0.0-alpha.1] - 2019-11-22
### Changed ### Changed

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-web" name = "actix-web"
version = "2.0.0-alpha.1" version = "2.0.0-alpha.3"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust."
readme = "README.md" readme = "README.md"
@ -63,35 +63,36 @@ secure-cookies = ["actix-http/secure-cookies"]
fail = ["actix-http/fail"] fail = ["actix-http/fail"]
# openssl # openssl
openssl = ["open-ssl", "actix-server/openssl", "awc/openssl"] openssl = ["open-ssl", "actix-tls/openssl", "awc/openssl"]
# rustls # rustls
# rustls = ["rust-tls", "actix-server/rustls", "awc/rustls"] rustls = ["rust-tls", "actix-tls/rustls", "awc/rustls"]
[dependencies] [dependencies]
actix-codec = "0.2.0-alpha.1" actix-codec = "0.2.0-alpha.3"
actix-service = "1.0.0-alpha.1" actix-service = "1.0.0-alpha.3"
actix-utils = "0.5.0-alpha.1" actix-utils = "1.0.0-alpha.3"
actix-router = "0.1.5" actix-router = "0.2.0"
actix-rt = "1.0.0-alpha.1" actix-rt = "1.0.0-alpha.3"
actix-web-codegen = "0.2.0-alpha.1" actix-server = "1.0.0-alpha.3"
actix-http = "0.3.0-alpha.1" actix-testing = "1.0.0-alpha.3"
actix-server = "0.8.0-alpha.1" actix-threadpool = "0.3.0"
actix-server-config = "0.3.0-alpha.1" actix-tls = { version = "1.0.0-alpha.3" }
actix-testing = "0.3.0-alpha.1"
actix-threadpool = "0.2.0-alpha.1"
awc = { version = "0.3.0-alpha.1", optional = true }
bytes = "0.4" actix-web-codegen = "0.2.0-alpha.2"
actix-http = "0.3.0-alpha.2"
awc = { version = "0.3.0-alpha.2", optional = true }
bytes = "0.5.2"
derive_more = "0.99.2" derive_more = "0.99.2"
encoding_rs = "0.8" encoding_rs = "0.8"
futures = "0.3.1" futures = "0.3.1"
hashbrown = "0.6.3" fxhash = "0.2.1"
log = "0.4" log = "0.4"
mime = "0.3" mime = "0.3"
net2 = "0.2.33" net2 = "0.2.33"
parking_lot = "0.9" parking_lot = "0.9"
pin-project = "0.4.5" pin-project = "0.4.6"
regex = "1.0" regex = "1.0"
serde = { version = "1.0", features=["derive"] } serde = { version = "1.0", features=["derive"] }
serde_json = "1.0" serde_json = "1.0"
@ -101,12 +102,12 @@ url = "2.1"
# ssl support # ssl support
open-ssl = { version="0.10", package="openssl", optional = true } open-ssl = { version="0.10", package="openssl", optional = true }
# rust-tls = { version = "0.16", package="rustls", optional = true } rust-tls = { version = "0.16", package="rustls", optional = true }
[dev-dependencies] [dev-dependencies]
# actix = "0.8.3" # actix = "0.8.3"
actix-connect = "0.3.0-alpha.1" actix-connect = "1.0.0-alpha.2"
actix-http-test = "0.3.0-alpha.1" actix-http-test = "0.3.0-alpha.2"
rand = "0.7" rand = "0.7"
env_logger = "0.6" env_logger = "0.6"
serde_derive = "1.0" serde_derive = "1.0"
@ -123,7 +124,6 @@ actix-web = { path = "." }
actix-http = { path = "actix-http" } actix-http = { path = "actix-http" }
actix-http-test = { path = "test-server" } actix-http-test = { path = "test-server" }
actix-web-codegen = { path = "actix-web-codegen" } actix-web-codegen = { path = "actix-web-codegen" }
# actix-web-actors = { path = "actix-web-actors" }
actix-cors = { path = "actix-cors" } actix-cors = { path = "actix-cors" }
actix-identity = { path = "actix-identity" } actix-identity = { path = "actix-identity" }
actix-session = { path = "actix-session" } actix-session = { path = "actix-session" }
@ -134,9 +134,9 @@ awc = { path = "awc" }
actix-codec = { git = "https://github.com/actix/actix-net.git" } actix-codec = { git = "https://github.com/actix/actix-net.git" }
actix-connect = { git = "https://github.com/actix/actix-net.git" } actix-connect = { git = "https://github.com/actix/actix-net.git" }
actix-rt = { git = "https://github.com/actix/actix-net.git" } actix-rt = { git = "https://github.com/actix/actix-net.git" }
actix-macros = { git = "https://github.com/actix/actix-net.git" }
actix-server = { git = "https://github.com/actix/actix-net.git" } actix-server = { git = "https://github.com/actix/actix-net.git" }
actix-server-config = { git = "https://github.com/actix/actix-net.git" }
actix-service = { git = "https://github.com/actix/actix-net.git" } actix-service = { git = "https://github.com/actix/actix-net.git" }
actix-testing = { git = "https://github.com/actix/actix-net.git" } actix-testing = { git = "https://github.com/actix/actix-net.git" }
actix-utils = { git = "https://github.com/actix/actix-net.git" } actix-tls = { git = "https://github.com/actix/actix-net.git" }
actix-utils = { git = "https://github.com/actix/actix-net.git" }
actix-router = { git = "https://github.com/actix/actix-net.git" }

View File

@ -1,8 +1,10 @@
# Changes # Changes
## [0.1.1] - unreleased ## [0.2.0-alpha.3] - unreleased
* Bump `derive_more` crate version to 0.15.0 * Migrate to actix-web 2.0.0
* Bump `derive_more` crate version to 0.99.0
## [0.1.0] - 2019-06-15 ## [0.1.0] - 2019-06-15

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-cors" name = "actix-cors"
version = "0.2.0-alpha.1" version = "0.2.0-alpha.3"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Cross-origin resource sharing (CORS) for Actix applications." description = "Cross-origin resource sharing (CORS) for Actix applications."
readme = "README.md" readme = "README.md"
@ -18,9 +18,9 @@ path = "src/lib.rs"
[dependencies] [dependencies]
actix-web = "2.0.0-alpha.1" actix-web = "2.0.0-alpha.1"
actix-service = "1.0.0-alpha.1" actix-service = "1.0.0-alpha.2"
derive_more = "0.99.2" derive_more = "0.99.2"
futures = "0.3.1" futures = "0.3.1"
[dev-dependencies] [dev-dependencies]
actix-rt = "1.0.0-alpha.1" actix-rt = "1.0.0-alpha.2"

View File

@ -40,6 +40,7 @@
//! //!
//! Cors middleware automatically handle *OPTIONS* preflight request. //! Cors middleware automatically handle *OPTIONS* preflight request.
use std::collections::HashSet; use std::collections::HashSet;
use std::convert::TryFrom;
use std::iter::FromIterator; use std::iter::FromIterator;
use std::rc::Rc; use std::rc::Rc;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
@ -48,7 +49,7 @@ use actix_service::{Service, Transform};
use actix_web::dev::{RequestHead, ServiceRequest, ServiceResponse}; use actix_web::dev::{RequestHead, ServiceRequest, ServiceResponse};
use actix_web::error::{Error, ResponseError, Result}; use actix_web::error::{Error, ResponseError, Result};
use actix_web::http::header::{self, HeaderName, HeaderValue}; use actix_web::http::header::{self, HeaderName, HeaderValue};
use actix_web::http::{self, HttpTryFrom, Method, StatusCode, Uri}; use actix_web::http::{self, Error as HttpError, Method, StatusCode, Uri};
use actix_web::HttpResponse; use actix_web::HttpResponse;
use derive_more::Display; use derive_more::Display;
use futures::future::{ok, Either, FutureExt, LocalBoxFuture, Ready}; use futures::future::{ok, Either, FutureExt, LocalBoxFuture, Ready};
@ -274,7 +275,8 @@ impl Cors {
pub fn allowed_methods<U, M>(mut self, methods: U) -> Cors pub fn allowed_methods<U, M>(mut self, methods: U) -> Cors
where where
U: IntoIterator<Item = M>, U: IntoIterator<Item = M>,
Method: HttpTryFrom<M>, Method: TryFrom<M>,
<Method as TryFrom<M>>::Error: Into<HttpError>,
{ {
self.methods = true; self.methods = true;
if let Some(cors) = cors(&mut self.cors, &self.error) { if let Some(cors) = cors(&mut self.cors, &self.error) {
@ -296,7 +298,8 @@ impl Cors {
/// Set an allowed header /// Set an allowed header
pub fn allowed_header<H>(mut self, header: H) -> Cors pub fn allowed_header<H>(mut self, header: H) -> Cors
where where
HeaderName: HttpTryFrom<H>, HeaderName: TryFrom<H>,
<HeaderName as TryFrom<H>>::Error: Into<HttpError>,
{ {
if let Some(cors) = cors(&mut self.cors, &self.error) { if let Some(cors) = cors(&mut self.cors, &self.error) {
match HeaderName::try_from(header) { match HeaderName::try_from(header) {
@ -328,7 +331,8 @@ impl Cors {
pub fn allowed_headers<U, H>(mut self, headers: U) -> Cors pub fn allowed_headers<U, H>(mut self, headers: U) -> Cors
where where
U: IntoIterator<Item = H>, U: IntoIterator<Item = H>,
HeaderName: HttpTryFrom<H>, HeaderName: TryFrom<H>,
<HeaderName as TryFrom<H>>::Error: Into<HttpError>,
{ {
if let Some(cors) = cors(&mut self.cors, &self.error) { if let Some(cors) = cors(&mut self.cors, &self.error) {
for h in headers { for h in headers {
@ -362,7 +366,8 @@ impl Cors {
pub fn expose_headers<U, H>(mut self, headers: U) -> Cors pub fn expose_headers<U, H>(mut self, headers: U) -> Cors
where where
U: IntoIterator<Item = H>, U: IntoIterator<Item = H>,
HeaderName: HttpTryFrom<H>, HeaderName: TryFrom<H>,
<HeaderName as TryFrom<H>>::Error: Into<HttpError>,
{ {
for h in headers { for h in headers {
match HeaderName::try_from(h) { match HeaderName::try_from(h) {

View File

@ -1,5 +1,9 @@
# Changes # Changes
## [0.2.0-alpha.2] - 2019-12-03
* Migrate to `std::future`
## [0.1.7] - 2019-11-06 ## [0.1.7] - 2019-11-06
* Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151) * Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151)

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-files" name = "actix-files"
version = "0.2.0-alpha.1" version = "0.2.0-alpha.3"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Static files support for actix web." description = "Static files support for actix web."
readme = "README.md" readme = "README.md"
@ -18,11 +18,11 @@ name = "actix_files"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
actix-web = { version = "2.0.0-alpha.1", default-features = false } actix-web = { version = "2.0.0-alpha.2", default-features = false }
actix-http = "0.3.0-alpha.1" actix-http = "0.3.0-alpha.2"
actix-service = "1.0.0-alpha.1" actix-service = "1.0.0-alpha.3"
bitflags = "1" bitflags = "1"
bytes = "0.4" bytes = "0.5.2"
futures = "0.3.1" futures = "0.3.1"
derive_more = "0.99.2" derive_more = "0.99.2"
log = "0.4" log = "0.4"
@ -32,5 +32,5 @@ percent-encoding = "2.1"
v_htmlescape = "0.4" v_htmlescape = "0.4"
[dev-dependencies] [dev-dependencies]
actix-rt = "1.0.0-alpha.1" actix-rt = "1.0.0-alpha.2"
actix-web = { version = "2.0.0-alpha.1", features=["openssl"] } actix-web = { version = "2.0.0-alpha.2", features=["openssl"] }

View File

@ -18,7 +18,7 @@ use actix_web::dev::{
AppService, HttpServiceFactory, Payload, ResourceDef, ServiceRequest, AppService, HttpServiceFactory, Payload, ResourceDef, ServiceRequest,
ServiceResponse, ServiceResponse,
}; };
use actix_web::error::{Canceled, Error, ErrorInternalServerError}; use actix_web::error::{BlockingError, Error, ErrorInternalServerError};
use actix_web::guard::Guard; use actix_web::guard::Guard;
use actix_web::http::header::{self, DispositionType}; use actix_web::http::header::{self, DispositionType};
use actix_web::http::Method; use actix_web::http::Method;
@ -50,6 +50,12 @@ pub fn file_extension_to_mime(ext: &str) -> mime::Mime {
from_ext(ext).first_or_octet_stream() from_ext(ext).first_or_octet_stream()
} }
fn handle_error(err: BlockingError<io::Error>) -> Error {
match err {
BlockingError::Error(err) => err.into(),
BlockingError::Canceled => ErrorInternalServerError("Unexpected error"),
}
}
#[doc(hidden)] #[doc(hidden)]
/// A helper created from a `std::fs::File` which reads the file /// A helper created from a `std::fs::File` which reads the file
/// chunk-by-chunk on a `ThreadPool`. /// chunk-by-chunk on a `ThreadPool`.
@ -57,9 +63,8 @@ pub struct ChunkedReadFile {
size: u64, size: u64,
offset: u64, offset: u64,
file: Option<File>, file: Option<File>,
fut: Option< fut:
LocalBoxFuture<'static, Result<Result<(File, Bytes), io::Error>, Canceled>>, Option<LocalBoxFuture<'static, Result<(File, Bytes), BlockingError<io::Error>>>>,
>,
counter: u64, counter: u64,
} }
@ -72,18 +77,14 @@ impl Stream for ChunkedReadFile {
) -> Poll<Option<Self::Item>> { ) -> Poll<Option<Self::Item>> {
if let Some(ref mut fut) = self.fut { if let Some(ref mut fut) = self.fut {
return match Pin::new(fut).poll(cx) { return match Pin::new(fut).poll(cx) {
Poll::Ready(Err(_)) => Poll::Ready(Some(Err(ErrorInternalServerError( Poll::Ready(Ok((file, bytes))) => {
"Unexpected error",
)
.into()))),
Poll::Ready(Ok(Ok((file, bytes)))) => {
self.fut.take(); self.fut.take();
self.file = Some(file); self.file = Some(file);
self.offset += bytes.len() as u64; self.offset += bytes.len() as u64;
self.counter += bytes.len() as u64; self.counter += bytes.len() as u64;
Poll::Ready(Some(Ok(bytes))) Poll::Ready(Some(Ok(bytes)))
} }
Poll::Ready(Ok(Err(e))) => Poll::Ready(Some(Err(e.into()))), Poll::Ready(Err(e)) => Poll::Ready(Some(Err(handle_error(e)))),
Poll::Pending => Poll::Pending, Poll::Pending => Poll::Pending,
}; };
} }
@ -415,7 +416,7 @@ impl ServiceFactory for Files {
type InitError = (); type InitError = ();
type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>; type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: &()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
let mut srv = FilesService { let mut srv = FilesService {
directory: self.directory.clone(), directory: self.directory.clone(),
index: self.index.clone(), index: self.index.clone(),
@ -430,7 +431,7 @@ impl ServiceFactory for Files {
if let Some(ref default) = *self.default.borrow() { if let Some(ref default) = *self.default.borrow() {
default default
.new_service(&()) .new_service(())
.map(move |result| match result { .map(move |result| match result {
Ok(default) => { Ok(default) => {
srv.default = Some(default); srv.default = Some(default);
@ -1262,7 +1263,7 @@ mod tests {
.default_handler(|req: ServiceRequest| { .default_handler(|req: ServiceRequest| {
ok(req.into_response(HttpResponse::Ok().body("default content"))) ok(req.into_response(HttpResponse::Ok().body("default content")))
}) })
.new_service(&()) .new_service(())
.await .await
.unwrap(); .unwrap();
let req = TestRequest::with_uri("/missing").to_srv_request(); let req = TestRequest::with_uri("/missing").to_srv_request();

View File

@ -20,20 +20,19 @@ name = "actix_framed"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
actix-codec = "0.2.0-alpha.1" actix-codec = "0.2.0-alpha.3"
actix-service = "1.0.0-alpha.1" actix-service = "1.0.0-alpha.3"
actix-router = "0.1.2" actix-router = "0.2.0"
actix-rt = "1.0.0-alpha.1" actix-rt = "1.0.0-alpha.3"
actix-http = "0.3.0-alpha.1" actix-http = "0.3.0-alpha.3"
actix-server-config = "0.3.0-alpha.1"
bytes = "0.4" bytes = "0.5.2"
futures = "0.3.1" futures = "0.3.1"
pin-project = "0.4.6" pin-project = "0.4.6"
log = "0.4" log = "0.4"
[dev-dependencies] [dev-dependencies]
actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } actix-server = { version = "1.0.0-alpha.3" }
actix-connect = { version = "0.3.0-alpha.1", features=["openssl"] } actix-connect = { version = "1.0.0-alpha.3", features=["openssl"] }
actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } actix-http-test = { version = "0.3.0-alpha.3", features=["openssl"] }
actix-utils = "0.5.0-alpha.1" actix-utils = "1.0.0-alpha.3"

View File

@ -7,7 +7,6 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_http::h1::{Codec, SendResponse}; use actix_http::h1::{Codec, SendResponse};
use actix_http::{Error, Request, Response}; use actix_http::{Error, Request, Response};
use actix_router::{Path, Router, Url}; use actix_router::{Path, Router, Url};
use actix_server_config::ServerConfig;
use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use actix_service::{IntoServiceFactory, Service, ServiceFactory};
use futures::future::{ok, FutureExt, LocalBoxFuture}; use futures::future::{ok, FutureExt, LocalBoxFuture};
@ -97,7 +96,7 @@ where
T: AsyncRead + AsyncWrite + Unpin + 'static, T: AsyncRead + AsyncWrite + Unpin + 'static,
S: 'static, S: 'static,
{ {
type Config = ServerConfig; type Config = ();
type Request = (Request, Framed<T, Codec>); type Request = (Request, Framed<T, Codec>);
type Response = (); type Response = ();
type Error = Error; type Error = Error;
@ -105,7 +104,7 @@ where
type Service = FramedAppService<T, S>; type Service = FramedAppService<T, S>;
type Future = CreateService<T, S>; type Future = CreateService<T, S>;
fn new_service(&self, _: &ServerConfig) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
CreateService { CreateService {
fut: self fut: self
.services .services
@ -113,7 +112,7 @@ where
.map(|(path, service)| { .map(|(path, service)| {
CreateServiceItem::Future( CreateServiceItem::Future(
Some(path.clone()), Some(path.clone()),
service.new_service(&()), service.new_service(()),
) )
}) })
.collect(), .collect(),

View File

@ -56,8 +56,8 @@ where
type Service = BoxedHttpService<T::Request>; type Service = BoxedHttpService<T::Request>;
type Future = LocalBoxFuture<'static, Result<Self::Service, ()>>; type Future = LocalBoxFuture<'static, Result<Self::Service, ()>>;
fn new_service(&self, _: &()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
let fut = self.0.new_service(&()); let fut = self.0.new_service(());
async move { async move {
fut.await.map_err(|_| ()).map(|service| { fut.await.map_err(|_| ()).map(|service| {

View File

@ -123,7 +123,9 @@ impl<Io, S> FramedRequest<Io, S> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use actix_http::http::{HeaderName, HeaderValue, HttpTryFrom}; use std::convert::TryFrom;
use actix_http::http::{HeaderName, HeaderValue};
use actix_http::test::{TestBuffer, TestRequest}; use actix_http::test::{TestBuffer, TestRequest};
use super::*; use super::*;

View File

@ -113,7 +113,7 @@ where
type Service = FramedRouteService<Io, S, F, R, E>; type Service = FramedRouteService<Io, S, F, R, E>;
type Future = Ready<Result<Self::Service, Self::InitError>>; type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: &()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
ok(FramedRouteService { ok(FramedRouteService {
handler: self.handler.clone(), handler: self.handler.clone(),
methods: self.methods.clone(), methods: self.methods.clone(),

View File

@ -33,7 +33,7 @@ impl<T, C> ServiceFactory for VerifyWebSockets<T, C> {
type Service = VerifyWebSockets<T, C>; type Service = VerifyWebSockets<T, C>;
type Future = Ready<Result<Self::Service, Self::InitError>>; type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: &C) -> Self::Future { fn new_service(&self, _: C) -> Self::Future {
ok(VerifyWebSockets { _t: PhantomData }) ok(VerifyWebSockets { _t: PhantomData })
} }
} }
@ -83,7 +83,7 @@ where
type Service = SendError<T, R, E, C>; type Service = SendError<T, R, E, C>;
type Future = Ready<Result<Self::Service, Self::InitError>>; type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: &C) -> Self::Future { fn new_service(&self, _: C) -> Self::Future {
ok(SendError(PhantomData)) ok(SendError(PhantomData))
} }
} }

View File

@ -1,10 +1,11 @@
//! Various helpers for Actix applications to use during testing. //! Various helpers for Actix applications to use during testing.
use std::convert::TryFrom;
use std::future::Future; use std::future::Future;
use actix_codec::Framed; use actix_codec::Framed;
use actix_http::h1::Codec; use actix_http::h1::Codec;
use actix_http::http::header::{Header, HeaderName, IntoHeaderValue}; use actix_http::http::header::{Header, HeaderName, IntoHeaderValue};
use actix_http::http::{HttpTryFrom, Method, Uri, Version}; use actix_http::http::{Error as HttpError, Method, Uri, Version};
use actix_http::test::{TestBuffer, TestRequest as HttpTestRequest}; use actix_http::test::{TestBuffer, TestRequest as HttpTestRequest};
use actix_router::{Path, Url}; use actix_router::{Path, Url};
@ -41,7 +42,8 @@ impl TestRequest<()> {
/// Create TestRequest and set header /// Create TestRequest and set header
pub fn with_header<K, V>(key: K, value: V) -> Self pub fn with_header<K, V>(key: K, value: V) -> Self
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
Self::default().header(key, value) Self::default().header(key, value)
@ -96,7 +98,8 @@ impl<S> TestRequest<S> {
/// Set a header /// Set a header
pub fn header<K, V>(mut self, key: K, value: V) -> Self pub fn header<K, V>(mut self, key: K, value: V) -> Self
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
self.req.header(key, value); self.req.header(key, value);

View File

@ -3,7 +3,7 @@ use actix_http::{body, http::StatusCode, ws, Error, HttpService, Response};
use actix_http_test::TestServer; use actix_http_test::TestServer;
use actix_service::{pipeline_factory, IntoServiceFactory, ServiceFactory}; use actix_service::{pipeline_factory, IntoServiceFactory, ServiceFactory};
use actix_utils::framed::FramedTransport; use actix_utils::framed::FramedTransport;
use bytes::{Bytes, BytesMut}; use bytes::BytesMut;
use futures::{future, SinkExt, StreamExt}; use futures::{future, SinkExt, StreamExt};
use actix_framed::{FramedApp, FramedRequest, FramedRoute, SendError, VerifyWebSockets}; use actix_framed::{FramedApp, FramedRequest, FramedRoute, SendError, VerifyWebSockets};
@ -46,6 +46,7 @@ async fn test_simple() {
FramedApp::new().service(FramedRoute::get("/index.html").to(ws_service)), FramedApp::new().service(FramedRoute::get("/index.html").to(ws_service)),
) )
.finish(|_| future::ok::<_, Error>(Response::NotFound())) .finish(|_| future::ok::<_, Error>(Response::NotFound()))
.tcp()
}); });
assert!(srv.ws_at("/test").await.is_err()); assert!(srv.ws_at("/test").await.is_err());
@ -69,7 +70,7 @@ async fn test_simple() {
let (item, mut framed) = framed.into_future().await; let (item, mut framed) = framed.into_future().await;
assert_eq!( assert_eq!(
item.unwrap().unwrap(), item.unwrap().unwrap(),
ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) ws::Frame::Binary(Some(BytesMut::from(&b"text"[..])))
); );
framed.send(ws::Message::Ping("text".into())).await.unwrap(); framed.send(ws::Message::Ping("text".into())).await.unwrap();
@ -135,7 +136,7 @@ async fn test_service() {
let (item, mut framed) = framed.into_future().await; let (item, mut framed) = framed.into_future().await;
assert_eq!( assert_eq!(
item.unwrap().unwrap(), item.unwrap().unwrap(),
ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) ws::Frame::Binary(Some(BytesMut::from(&b"text"[..])))
); );
framed.send(ws::Message::Ping("text".into())).await.unwrap(); framed.send(ws::Message::Ping("text".into())).await.unwrap();

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-http" name = "actix-http"
version = "0.3.0-alpha.1" version = "0.3.0-alpha.3"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix http primitives" description = "Actix http primitives"
readme = "README.md" readme = "README.md"
@ -16,7 +16,7 @@ edition = "2018"
workspace = ".." workspace = ".."
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = ["openssl", "fail", "brotli", "flate2-zlib", "secure-cookies"] features = ["openssl", "rustls", "fail", "brotli", "flate2-zlib", "secure-cookies"]
[lib] [lib]
name = "actix_http" name = "actix_http"
@ -26,10 +26,10 @@ path = "src/lib.rs"
default = [] default = []
# openssl # openssl
openssl = ["open-ssl", "actix-connect/openssl", "tokio-openssl"] openssl = ["actix-tls/openssl", "actix-connect/openssl"]
# rustls support # rustls support
# rustls = ["rust-tls", "webpki-roots", "actix-connect/rustls"] rustls = ["actix-tls/rustls", "actix-connect/rustls"]
# brotli encoding, requires c compiler # brotli encoding, requires c compiler
brotli = ["brotli2"] brotli = ["brotli2"]
@ -47,26 +47,26 @@ fail = ["failure"]
secure-cookies = ["ring"] secure-cookies = ["ring"]
[dependencies] [dependencies]
actix-service = "1.0.0-alpha.1" actix-service = "1.0.0-alpha.3"
actix-codec = "0.2.0-alpha.1" actix-codec = "0.2.0-alpha.3"
actix-connect = "1.0.0-alpha.1" actix-connect = "1.0.0-alpha.3"
actix-utils = "0.5.0-alpha.1" actix-utils = "1.0.0-alpha.3"
actix-server-config = "0.3.0-alpha.1" actix-rt = "1.0.0-alpha.3"
actix-rt = "1.0.0-alpha.1" actix-threadpool = "0.3.0"
actix-threadpool = "0.2.0-alpha.1" actix-tls = { version = "1.0.0-alpha.3", optional = true }
base64 = "0.10" base64 = "0.11"
bitflags = "1.0" bitflags = "1.0"
bytes = "0.4" bytes = "0.5.2"
copyless = "0.1.4" copyless = "0.1.4"
chrono = "0.4.6" chrono = "0.4.6"
derive_more = "0.99.2" derive_more = "0.99.2"
either = "1.5.2" either = "1.5.2"
encoding_rs = "0.8" encoding_rs = "0.8"
futures = "0.3.1" futures = "0.3.1"
hashbrown = "0.6.3" fxhash = "0.2.1"
h2 = "0.2.0-alpha.3" h2 = "0.2.0"
http = "0.1.17" http = "0.2.0"
httparse = "1.3" httparse = "1.3"
indexmap = "1.2" indexmap = "1.2"
lazy_static = "1.0" lazy_static = "1.0"
@ -74,9 +74,9 @@ language-tags = "0.2"
log = "0.4" log = "0.4"
mime = "0.3" mime = "0.3"
percent-encoding = "2.1" percent-encoding = "2.1"
pin-project = "0.4.5" pin-project = "0.4.6"
rand = "0.7" rand = "0.7"
regex = "1.0" regex = "1.3"
serde = "1.0" serde = "1.0"
serde_json = "1.0" serde_json = "1.0"
sha1 = "0.6" sha1 = "0.6"
@ -84,9 +84,6 @@ slab = "0.4"
serde_urlencoded = "0.6.1" serde_urlencoded = "0.6.1"
time = "0.1.42" time = "0.1.42"
tokio-net = "=0.2.0-alpha.6"
trust-dns-resolver = { version="0.18.0-alpha.1", default-features = false }
# for secure cookie # for secure cookie
ring = { version = "0.16.9", optional = true } ring = { version = "0.16.9", optional = true }
@ -96,17 +93,13 @@ flate2 = { version="1.0.7", optional = true, default-features = false }
# optional deps # optional deps
failure = { version = "0.1.5", optional = true } failure = { version = "0.1.5", optional = true }
open-ssl = { version="0.10", package="openssl", optional = true }
tokio-openssl = { version = "0.4.0-alpha.6", optional = true }
# rust-tls = { version = "0.16.0", package="rustls", optional = true }
# webpki-roots = { version = "0.18", optional = true }
[dev-dependencies] [dev-dependencies]
#actix-server = { version = "0.8.0-alpha.1", features=["openssl", "rustls"] } actix-server = { version = "1.0.0-alpha.3" }
actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } actix-connect = { version = "1.0.0-alpha.3", features=["openssl"] }
actix-connect = { version = "1.0.0-alpha.1", features=["openssl"] } actix-http-test = { version = "0.3.0-alpha.3", features=["openssl"] }
actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } actix-tls = { version = "1.0.0-alpha.3", features=["openssl"] }
env_logger = "0.6" env_logger = "0.6"
serde_derive = "1.0" serde_derive = "1.0"
open-ssl = { version="0.10", package="openssl" } open-ssl = { version="0.10", package = "openssl" }
rust-tls = { version="0.16", package = "rustls" }

View File

@ -34,6 +34,7 @@ fn main() -> io::Result<()> {
) )
} }
}) })
.tcp()
})? })?
.run() .run()
} }

View File

@ -25,7 +25,7 @@ fn main() -> io::Result<()> {
Server::build() Server::build()
.bind("echo", "127.0.0.1:8080", || { .bind("echo", "127.0.0.1:8080", || {
HttpService::build().finish(handle_request) HttpService::build().finish(handle_request).tcp()
})? })?
.run() .run()
} }

View File

@ -21,6 +21,7 @@ fn main() -> io::Result<()> {
res.header("x-head", HeaderValue::from_static("dummy value!")); res.header("x-head", HeaderValue::from_static("dummy value!"));
future::ok::<_, ()>(res.body("Hello world!")) future::ok::<_, ()>(res.body("Hello world!"))
}) })
.tcp()
})? })?
.run() .run()
} }

View File

@ -133,7 +133,7 @@ pub enum Body {
impl Body { impl Body {
/// Create body from slice (copy) /// Create body from slice (copy)
pub fn from_slice(s: &[u8]) -> Body { pub fn from_slice(s: &[u8]) -> Body {
Body::Bytes(Bytes::from(s)) Body::Bytes(Bytes::copy_from_slice(s))
} }
/// Create body from generic message body. /// Create body from generic message body.
@ -226,7 +226,7 @@ impl From<String> for Body {
impl<'a> From<&'a String> for Body { impl<'a> From<&'a String> for Body {
fn from(s: &'a String) -> Body { fn from(s: &'a String) -> Body {
Body::Bytes(Bytes::from(AsRef::<[u8]>::as_ref(&s))) Body::Bytes(Bytes::copy_from_slice(AsRef::<[u8]>::as_ref(&s)))
} }
} }

View File

@ -1,9 +1,8 @@
use std::fmt;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::rc::Rc; use std::rc::Rc;
use std::{fmt, net};
use actix_codec::Framed; use actix_codec::Framed;
use actix_server_config::ServerConfig as SrvConfig;
use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use actix_service::{IntoServiceFactory, Service, ServiceFactory};
use crate::body::MessageBody; use crate::body::MessageBody;
@ -24,6 +23,8 @@ pub struct HttpServiceBuilder<T, S, X = ExpectHandler, U = UpgradeHandler<T>> {
keep_alive: KeepAlive, keep_alive: KeepAlive,
client_timeout: u64, client_timeout: u64,
client_disconnect: u64, client_disconnect: u64,
secure: bool,
local_addr: Option<net::SocketAddr>,
expect: X, expect: X,
upgrade: Option<U>, upgrade: Option<U>,
on_connect: Option<Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>, on_connect: Option<Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>,
@ -32,7 +33,7 @@ pub struct HttpServiceBuilder<T, S, X = ExpectHandler, U = UpgradeHandler<T>> {
impl<T, S> HttpServiceBuilder<T, S, ExpectHandler, UpgradeHandler<T>> impl<T, S> HttpServiceBuilder<T, S, ExpectHandler, UpgradeHandler<T>>
where where
S: ServiceFactory<Config = SrvConfig, Request = Request>, S: ServiceFactory<Config = (), Request = Request>,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
<S::Service as Service>::Future: 'static, <S::Service as Service>::Future: 'static,
@ -43,6 +44,8 @@ where
keep_alive: KeepAlive::Timeout(5), keep_alive: KeepAlive::Timeout(5),
client_timeout: 5000, client_timeout: 5000,
client_disconnect: 0, client_disconnect: 0,
secure: false,
local_addr: None,
expect: ExpectHandler, expect: ExpectHandler,
upgrade: None, upgrade: None,
on_connect: None, on_connect: None,
@ -53,19 +56,15 @@ where
impl<T, S, X, U> HttpServiceBuilder<T, S, X, U> impl<T, S, X, U> HttpServiceBuilder<T, S, X, U>
where where
S: ServiceFactory<Config = SrvConfig, Request = Request>, S: ServiceFactory<Config = (), Request = Request>,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
<S::Service as Service>::Future: 'static, <S::Service as Service>::Future: 'static,
X: ServiceFactory<Config = SrvConfig, Request = Request, Response = Request>, X: ServiceFactory<Config = (), Request = Request, Response = Request>,
X::Error: Into<Error>, X::Error: Into<Error>,
X::InitError: fmt::Debug, X::InitError: fmt::Debug,
<X::Service as Service>::Future: 'static, <X::Service as Service>::Future: 'static,
U: ServiceFactory< U: ServiceFactory<Config = (), Request = (Request, Framed<T, Codec>), Response = ()>,
Config = SrvConfig,
Request = (Request, Framed<T, Codec>),
Response = (),
>,
U::Error: fmt::Display, U::Error: fmt::Display,
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
<U::Service as Service>::Future: 'static, <U::Service as Service>::Future: 'static,
@ -78,6 +77,18 @@ where
self self
} }
/// Set connection secure state
pub fn secure(mut self) -> Self {
self.secure = true;
self
}
/// Set the local address that this service is bound to.
pub fn local_addr(mut self, addr: net::SocketAddr) -> Self {
self.local_addr = Some(addr);
self
}
/// Set server client timeout in milliseconds for first request. /// Set server client timeout in milliseconds for first request.
/// ///
/// Defines a timeout for reading client request header. If a client does not transmit /// Defines a timeout for reading client request header. If a client does not transmit
@ -113,7 +124,7 @@ where
pub fn expect<F, X1>(self, expect: F) -> HttpServiceBuilder<T, S, X1, U> pub fn expect<F, X1>(self, expect: F) -> HttpServiceBuilder<T, S, X1, U>
where where
F: IntoServiceFactory<X1>, F: IntoServiceFactory<X1>,
X1: ServiceFactory<Config = SrvConfig, Request = Request, Response = Request>, X1: ServiceFactory<Config = (), Request = Request, Response = Request>,
X1::Error: Into<Error>, X1::Error: Into<Error>,
X1::InitError: fmt::Debug, X1::InitError: fmt::Debug,
<X1::Service as Service>::Future: 'static, <X1::Service as Service>::Future: 'static,
@ -122,6 +133,8 @@ where
keep_alive: self.keep_alive, keep_alive: self.keep_alive,
client_timeout: self.client_timeout, client_timeout: self.client_timeout,
client_disconnect: self.client_disconnect, client_disconnect: self.client_disconnect,
secure: self.secure,
local_addr: self.local_addr,
expect: expect.into_factory(), expect: expect.into_factory(),
upgrade: self.upgrade, upgrade: self.upgrade,
on_connect: self.on_connect, on_connect: self.on_connect,
@ -137,7 +150,7 @@ where
where where
F: IntoServiceFactory<U1>, F: IntoServiceFactory<U1>,
U1: ServiceFactory< U1: ServiceFactory<
Config = SrvConfig, Config = (),
Request = (Request, Framed<T, Codec>), Request = (Request, Framed<T, Codec>),
Response = (), Response = (),
>, >,
@ -149,6 +162,8 @@ where
keep_alive: self.keep_alive, keep_alive: self.keep_alive,
client_timeout: self.client_timeout, client_timeout: self.client_timeout,
client_disconnect: self.client_disconnect, client_disconnect: self.client_disconnect,
secure: self.secure,
local_addr: self.local_addr,
expect: self.expect, expect: self.expect,
upgrade: Some(upgrade.into_factory()), upgrade: Some(upgrade.into_factory()),
on_connect: self.on_connect, on_connect: self.on_connect,
@ -170,7 +185,7 @@ where
} }
/// Finish service configuration and create *http service* for HTTP/1 protocol. /// Finish service configuration and create *http service* for HTTP/1 protocol.
pub fn h1<F, P, B>(self, service: F) -> H1Service<T, P, S, B, X, U> pub fn h1<F, B>(self, service: F) -> H1Service<T, S, B, X, U>
where where
B: MessageBody, B: MessageBody,
F: IntoServiceFactory<S>, F: IntoServiceFactory<S>,
@ -182,6 +197,8 @@ where
self.keep_alive, self.keep_alive,
self.client_timeout, self.client_timeout,
self.client_disconnect, self.client_disconnect,
self.secure,
self.local_addr,
); );
H1Service::with_config(cfg, service.into_factory()) H1Service::with_config(cfg, service.into_factory())
.expect(self.expect) .expect(self.expect)
@ -190,7 +207,7 @@ where
} }
/// Finish service configuration and create *http service* for HTTP/2 protocol. /// Finish service configuration and create *http service* for HTTP/2 protocol.
pub fn h2<F, P, B>(self, service: F) -> H2Service<T, P, S, B> pub fn h2<F, B>(self, service: F) -> H2Service<T, S, B>
where where
B: MessageBody + 'static, B: MessageBody + 'static,
F: IntoServiceFactory<S>, F: IntoServiceFactory<S>,
@ -203,12 +220,14 @@ where
self.keep_alive, self.keep_alive,
self.client_timeout, self.client_timeout,
self.client_disconnect, self.client_disconnect,
self.secure,
self.local_addr,
); );
H2Service::with_config(cfg, service.into_factory()).on_connect(self.on_connect) H2Service::with_config(cfg, service.into_factory()).on_connect(self.on_connect)
} }
/// Finish service configuration and create `HttpService` instance. /// Finish service configuration and create `HttpService` instance.
pub fn finish<F, P, B>(self, service: F) -> HttpService<T, P, S, B, X, U> pub fn finish<F, B>(self, service: F) -> HttpService<T, S, B, X, U>
where where
B: MessageBody + 'static, B: MessageBody + 'static,
F: IntoServiceFactory<S>, F: IntoServiceFactory<S>,
@ -221,6 +240,8 @@ where
self.keep_alive, self.keep_alive,
self.client_timeout, self.client_timeout,
self.client_disconnect, self.client_disconnect,
self.secure,
self.local_addr,
); );
HttpService::with_config(cfg, service.into_factory()) HttpService::with_config(cfg, service.into_factory())
.expect(self.expect) .expect(self.expect)

View File

@ -1,6 +1,6 @@
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::{fmt, io, time}; use std::{fmt, io, mem, time};
use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_codec::{AsyncRead, AsyncWrite, Framed};
use bytes::{Buf, Bytes}; use bytes::{Buf, Bytes};
@ -228,7 +228,10 @@ where
} }
} }
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { unsafe fn prepare_uninitialized_buffer(
&self,
buf: &mut [mem::MaybeUninit<u8>],
) -> bool {
match self { match self {
EitherIo::A(ref val) => val.prepare_uninitialized_buffer(buf), EitherIo::A(ref val) => val.prepare_uninitialized_buffer(buf),
EitherIo::B(ref val) => val.prepare_uninitialized_buffer(buf), EitherIo::B(ref val) => val.prepare_uninitialized_buffer(buf),

View File

@ -6,10 +6,10 @@ use actix_codec::{AsyncRead, AsyncWrite};
use actix_connect::{ use actix_connect::{
default_connector, Connect as TcpConnect, Connection as TcpConnection, default_connector, Connect as TcpConnect, Connection as TcpConnection,
}; };
use actix_rt::net::TcpStream;
use actix_service::{apply_fn, Service}; use actix_service::{apply_fn, Service};
use actix_utils::timeout::{TimeoutError, TimeoutService}; use actix_utils::timeout::{TimeoutError, TimeoutService};
use http::Uri; use http::Uri;
use tokio_net::tcp::TcpStream;
use super::connection::Connection; use super::connection::Connection;
use super::error::ConnectError; use super::error::ConnectError;
@ -17,10 +17,10 @@ use super::pool::{ConnectionPool, Protocol};
use super::Connect; use super::Connect;
#[cfg(feature = "openssl")] #[cfg(feature = "openssl")]
use open_ssl::ssl::SslConnector as OpensslConnector; use actix_connect::ssl::openssl::SslConnector as OpensslConnector;
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
use rust_tls::ClientConfig; use actix_connect::ssl::rustls::ClientConfig;
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
use std::sync::Arc; use std::sync::Arc;
@ -74,7 +74,7 @@ impl Connector<(), ()> {
let ssl = { let ssl = {
#[cfg(feature = "openssl")] #[cfg(feature = "openssl")]
{ {
use open_ssl::ssl::SslMethod; use actix_connect::ssl::openssl::SslMethod;
let mut ssl = OpensslConnector::builder(SslMethod::tls()).unwrap(); let mut ssl = OpensslConnector::builder(SslMethod::tls()).unwrap();
let _ = ssl let _ = ssl
@ -87,9 +87,9 @@ impl Connector<(), ()> {
let protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; let protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
let mut config = ClientConfig::new(); let mut config = ClientConfig::new();
config.set_protocols(&protos); config.set_protocols(&protos);
config config.root_store.add_server_trust_anchors(
.root_store &actix_connect::ssl::rustls::TLS_SERVER_ROOTS,
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); );
SslConnector::Rustls(Arc::new(config)) SslConnector::Rustls(Arc::new(config))
} }
#[cfg(not(any(feature = "openssl", feature = "rustls")))] #[cfg(not(any(feature = "openssl", feature = "rustls")))]
@ -242,12 +242,10 @@ where
{ {
const H2: &[u8] = b"h2"; const H2: &[u8] = b"h2";
#[cfg(feature = "openssl")] #[cfg(feature = "openssl")]
use actix_connect::ssl::OpensslConnector; use actix_connect::ssl::openssl::OpensslConnector;
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
use actix_connect::ssl::RustlsConnector; use actix_connect::ssl::rustls::{RustlsConnector, Session};
use actix_service::{boxed::service, pipeline}; use actix_service::{boxed::service, pipeline};
#[cfg(feature = "rustls")]
use rust_tls::Session;
let ssl_service = TimeoutService::new( let ssl_service = TimeoutService::new(
self.timeout, self.timeout,

View File

@ -1,10 +1,10 @@
use std::io; use std::io;
use actix_connect::resolver::ResolveError;
use derive_more::{Display, From}; use derive_more::{Display, From};
use trust_dns_resolver::error::ResolveError;
#[cfg(feature = "openssl")] #[cfg(feature = "openssl")]
use open_ssl::ssl::{Error as SslError, HandshakeError}; use actix_connect::ssl::openssl::{HandshakeError, SslError};
use crate::error::{Error, ParseError, ResponseError}; use crate::error::{Error, ParseError, ResponseError};
use crate::http::{Error as HttpError, StatusCode}; use crate::http::{Error as HttpError, StatusCode};
@ -21,6 +21,11 @@ pub enum ConnectError {
#[display(fmt = "{}", _0)] #[display(fmt = "{}", _0)]
SslError(SslError), SslError(SslError),
/// SSL Handshake error
#[cfg(feature = "openssl")]
#[display(fmt = "{}", _0)]
SslHandshakeError(String),
/// Failed to resolve the hostname /// Failed to resolve the hostname
#[display(fmt = "Failed resolving hostname: {}", _0)] #[display(fmt = "Failed resolving hostname: {}", _0)]
Resolver(ResolveError), Resolver(ResolveError),
@ -63,13 +68,9 @@ impl From<actix_connect::ConnectError> for ConnectError {
} }
#[cfg(feature = "openssl")] #[cfg(feature = "openssl")]
impl<T> From<HandshakeError<T>> for ConnectError { impl<T: std::fmt::Debug> From<HandshakeError<T>> for ConnectError {
fn from(err: HandshakeError<T>) -> ConnectError { fn from(err: HandshakeError<T>) -> ConnectError {
match err { ConnectError::SslHandshakeError(format!("{:?}", err))
HandshakeError::SetupFailure(stack) => SslError::from(stack).into(),
HandshakeError::Failure(stream) => stream.into_error().into(),
HandshakeError::WouldBlock(stream) => stream.into_error().into(),
}
} }
} }

View File

@ -1,10 +1,11 @@
use std::io::Write; use std::io::Write;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::{io, time}; use std::{io, mem, time};
use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_codec::{AsyncRead, AsyncWrite, Framed};
use bytes::{BufMut, Bytes, BytesMut}; use bytes::buf::BufMutExt;
use bytes::{Bytes, BytesMut};
use futures::future::poll_fn; use futures::future::poll_fn;
use futures::{SinkExt, Stream, StreamExt}; use futures::{SinkExt, Stream, StreamExt};
@ -43,7 +44,7 @@ where
Some(port) => write!(wrt, "{}:{}", host, port), Some(port) => write!(wrt, "{}:{}", host, port),
}; };
match wrt.get_mut().take().freeze().try_into() { match wrt.get_mut().split().freeze().try_into() {
Ok(value) => match head { Ok(value) => match head {
RequestHeadType::Owned(ref mut head) => { RequestHeadType::Owned(ref mut head) => {
head.headers.insert(HOST, value) head.headers.insert(HOST, value)
@ -199,7 +200,10 @@ where
} }
impl<T: AsyncRead + AsyncWrite + Unpin + 'static> AsyncRead for H1Connection<T> { impl<T: AsyncRead + AsyncWrite + Unpin + 'static> AsyncRead for H1Connection<T> {
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { unsafe fn prepare_uninitialized_buffer(
&self,
buf: &mut [mem::MaybeUninit<u8>],
) -> bool {
self.io.as_ref().unwrap().prepare_uninitialized_buffer(buf) self.io.as_ref().unwrap().prepare_uninitialized_buffer(buf)
} }

View File

@ -1,3 +1,4 @@
use std::convert::TryFrom;
use std::time; use std::time;
use actix_codec::{AsyncRead, AsyncWrite}; use actix_codec::{AsyncRead, AsyncWrite};
@ -5,7 +6,7 @@ use bytes::Bytes;
use futures::future::poll_fn; use futures::future::poll_fn;
use h2::{client::SendRequest, SendStream}; use h2::{client::SendRequest, SendStream};
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, TRANSFER_ENCODING}; use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, TRANSFER_ENCODING};
use http::{request::Request, HttpTryFrom, Method, Version}; use http::{request::Request, Method, Version};
use crate::body::{BodySize, MessageBody}; use crate::body::{BodySize, MessageBody};
use crate::header::HeaderMap; use crate::header::HeaderMap;

View File

@ -12,8 +12,8 @@ use actix_service::Service;
use actix_utils::{oneshot, task::LocalWaker}; use actix_utils::{oneshot, task::LocalWaker};
use bytes::Bytes; use bytes::Bytes;
use futures::future::{poll_fn, FutureExt, LocalBoxFuture}; use futures::future::{poll_fn, FutureExt, LocalBoxFuture};
use fxhash::FxHashMap;
use h2::client::{handshake, Connection, SendRequest}; use h2::client::{handshake, Connection, SendRequest};
use hashbrown::HashMap;
use http::uri::Authority; use http::uri::Authority;
use indexmap::IndexSet; use indexmap::IndexSet;
use slab::Slab; use slab::Slab;
@ -66,7 +66,7 @@ where
acquired: 0, acquired: 0,
waiters: Slab::new(), waiters: Slab::new(),
waiters_queue: IndexSet::new(), waiters_queue: IndexSet::new(),
available: HashMap::new(), available: FxHashMap::default(),
waker: LocalWaker::new(), waker: LocalWaker::new(),
})), })),
) )
@ -108,7 +108,7 @@ where
let inner = self.1.clone(); let inner = self.1.clone();
let fut = async move { let fut = async move {
let key = if let Some(authority) = req.uri.authority_part() { let key = if let Some(authority) = req.uri.authority() {
authority.clone().into() authority.clone().into()
} else { } else {
return Err(ConnectError::Unresolverd); return Err(ConnectError::Unresolverd);
@ -259,7 +259,7 @@ pub(crate) struct Inner<Io> {
disconnect_timeout: Option<Duration>, disconnect_timeout: Option<Duration>,
limit: usize, limit: usize,
acquired: usize, acquired: usize,
available: HashMap<Key, VecDeque<AvailableConnection<Io>>>, available: FxHashMap<Key, VecDeque<AvailableConnection<Io>>>,
waiters: Slab< waiters: Slab<
Option<( Option<(
Connect, Connect,
@ -299,7 +299,7 @@ where
) { ) {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
let key: Key = connect.uri.authority_part().unwrap().clone().into(); let key: Key = connect.uri.authority().unwrap().clone().into();
let entry = self.waiters.vacant_entry(); let entry = self.waiters.vacant_entry();
let token = entry.key(); let token = entry.key();
entry.insert(Some((connect, tx))); entry.insert(Some((connect, tx)));

View File

@ -1,10 +1,10 @@
use std::cell::UnsafeCell; use std::cell::UnsafeCell;
use std::fmt;
use std::fmt::Write; use std::fmt::Write;
use std::rc::Rc; use std::rc::Rc;
use std::time::{Duration, Instant}; use std::time::Duration;
use std::{fmt, net};
use actix_rt::time::{delay, delay_for, Delay}; use actix_rt::time::{delay_for, delay_until, Delay, Instant};
use bytes::BytesMut; use bytes::BytesMut;
use futures::{future, FutureExt}; use futures::{future, FutureExt};
use time; use time;
@ -47,6 +47,8 @@ struct Inner {
client_timeout: u64, client_timeout: u64,
client_disconnect: u64, client_disconnect: u64,
ka_enabled: bool, ka_enabled: bool,
secure: bool,
local_addr: Option<std::net::SocketAddr>,
timer: DateService, timer: DateService,
} }
@ -58,7 +60,7 @@ impl Clone for ServiceConfig {
impl Default for ServiceConfig { impl Default for ServiceConfig {
fn default() -> Self { fn default() -> Self {
Self::new(KeepAlive::Timeout(5), 0, 0) Self::new(KeepAlive::Timeout(5), 0, 0, false, None)
} }
} }
@ -68,6 +70,8 @@ impl ServiceConfig {
keep_alive: KeepAlive, keep_alive: KeepAlive,
client_timeout: u64, client_timeout: u64,
client_disconnect: u64, client_disconnect: u64,
secure: bool,
local_addr: Option<net::SocketAddr>,
) -> ServiceConfig { ) -> ServiceConfig {
let (keep_alive, ka_enabled) = match keep_alive { let (keep_alive, ka_enabled) = match keep_alive {
KeepAlive::Timeout(val) => (val as u64, true), KeepAlive::Timeout(val) => (val as u64, true),
@ -85,10 +89,24 @@ impl ServiceConfig {
ka_enabled, ka_enabled,
client_timeout, client_timeout,
client_disconnect, client_disconnect,
secure,
local_addr,
timer: DateService::new(), timer: DateService::new(),
})) }))
} }
#[inline]
/// Returns true if connection is secure(https)
pub fn secure(&self) -> bool {
self.0.secure
}
#[inline]
/// Returns the local address that this server is bound to.
pub fn local_addr(&self) -> Option<net::SocketAddr> {
self.0.local_addr
}
#[inline] #[inline]
/// Keep alive duration if configured. /// Keep alive duration if configured.
pub fn keep_alive(&self) -> Option<Duration> { pub fn keep_alive(&self) -> Option<Duration> {
@ -106,7 +124,7 @@ impl ServiceConfig {
pub fn client_timer(&self) -> Option<Delay> { pub fn client_timer(&self) -> Option<Delay> {
let delay_time = self.0.client_timeout; let delay_time = self.0.client_timeout;
if delay_time != 0 { if delay_time != 0 {
Some(delay( Some(delay_until(
self.0.timer.now() + Duration::from_millis(delay_time), self.0.timer.now() + Duration::from_millis(delay_time),
)) ))
} else { } else {
@ -138,7 +156,7 @@ impl ServiceConfig {
/// Return keep-alive timer delay is configured. /// Return keep-alive timer delay is configured.
pub fn keep_alive_timer(&self) -> Option<Delay> { pub fn keep_alive_timer(&self) -> Option<Delay> {
if let Some(ka) = self.0.keep_alive { if let Some(ka) = self.0.keep_alive {
Some(delay(self.0.timer.now() + ka)) Some(delay_until(self.0.timer.now() + ka))
} else { } else {
None None
} }
@ -271,7 +289,7 @@ mod tests {
#[actix_rt::test] #[actix_rt::test]
async fn test_date() { async fn test_date() {
let settings = ServiceConfig::new(KeepAlive::Os, 0, 0); let settings = ServiceConfig::new(KeepAlive::Os, 0, 0, false, None);
let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
settings.set_date(&mut buf1); settings.set_date(&mut buf1);
let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);

View File

@ -21,7 +21,7 @@ pub struct Decoder<S> {
decoder: Option<ContentDecoder>, decoder: Option<ContentDecoder>,
stream: S, stream: S,
eof: bool, eof: bool,
fut: Option<CpuFuture<Result<(Option<Bytes>, ContentDecoder), io::Error>>>, fut: Option<CpuFuture<(Option<Bytes>, ContentDecoder), io::Error>>,
} }
impl<S> Decoder<S> impl<S> Decoder<S>
@ -85,8 +85,7 @@ where
loop { loop {
if let Some(ref mut fut) = self.fut { if let Some(ref mut fut) = self.fut {
let (chunk, decoder) = match ready!(Pin::new(fut).poll(cx)) { let (chunk, decoder) = match ready!(Pin::new(fut).poll(cx)) {
Ok(Ok(item)) => item, Ok(item) => item,
Ok(Err(e)) => return Poll::Ready(Some(Err(e.into()))),
Err(e) => return Poll::Ready(Some(Err(e.into()))), Err(e) => return Poll::Ready(Some(Err(e.into()))),
}; };
self.decoder = Some(decoder); self.decoder = Some(decoder);

View File

@ -13,7 +13,7 @@ use flate2::write::{GzEncoder, ZlibEncoder};
use crate::body::{Body, BodySize, MessageBody, ResponseBody}; use crate::body::{Body, BodySize, MessageBody, ResponseBody};
use crate::http::header::{ContentEncoding, CONTENT_ENCODING}; use crate::http::header::{ContentEncoding, CONTENT_ENCODING};
use crate::http::{HeaderValue, HttpTryFrom, StatusCode}; use crate::http::{HeaderValue, StatusCode};
use crate::{Error, ResponseHead}; use crate::{Error, ResponseHead};
use super::Writer; use super::Writer;
@ -24,7 +24,7 @@ pub struct Encoder<B> {
eof: bool, eof: bool,
body: EncoderBody<B>, body: EncoderBody<B>,
encoder: Option<ContentEncoder>, encoder: Option<ContentEncoder>,
fut: Option<CpuFuture<Result<ContentEncoder, io::Error>>>, fut: Option<CpuFuture<ContentEncoder, io::Error>>,
} }
impl<B: MessageBody> Encoder<B> { impl<B: MessageBody> Encoder<B> {
@ -104,8 +104,7 @@ impl<B: MessageBody> MessageBody for Encoder<B> {
if let Some(ref mut fut) = self.fut { if let Some(ref mut fut) = self.fut {
let mut encoder = match futures::ready!(Pin::new(fut).poll(cx)) { let mut encoder = match futures::ready!(Pin::new(fut).poll(cx)) {
Ok(Ok(item)) => item, Ok(item) => item,
Ok(Err(e)) => return Poll::Ready(Some(Err(e.into()))),
Err(e) => return Poll::Ready(Some(Err(e.into()))), Err(e) => return Poll::Ready(Some(Err(e.into()))),
}; };
let chunk = encoder.take(); let chunk = encoder.take();
@ -169,7 +168,7 @@ impl<B: MessageBody> MessageBody for Encoder<B> {
fn update_head(encoding: ContentEncoding, head: &mut ResponseHead) { fn update_head(encoding: ContentEncoding, head: &mut ResponseHead) {
head.headers_mut().insert( head.headers_mut().insert(
CONTENT_ENCODING, CONTENT_ENCODING,
HeaderValue::try_from(Bytes::from_static(encoding.as_str().as_bytes())).unwrap(), HeaderValue::from_static(encoding.as_str()),
); );
} }

View File

@ -20,7 +20,7 @@ impl Writer {
} }
} }
fn take(&mut self) -> Bytes { fn take(&mut self) -> Bytes {
self.buf.take().freeze() self.buf.split().freeze()
} }
} }

View File

@ -6,6 +6,7 @@ use std::str::Utf8Error;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use std::{fmt, io, result}; use std::{fmt, io, result};
pub use actix_threadpool::BlockingError;
use actix_utils::timeout::TimeoutError; use actix_utils::timeout::TimeoutError;
use bytes::BytesMut; use bytes::BytesMut;
use derive_more::{Display, From}; use derive_more::{Display, From};
@ -112,12 +113,6 @@ impl fmt::Debug for Error {
} }
} }
impl From<()> for Error {
fn from(_: ()) -> Self {
Error::from(UnitError)
}
}
impl std::error::Error for Error { impl std::error::Error for Error {
fn description(&self) -> &str { fn description(&self) -> &str {
"actix-http::Error" "actix-http::Error"
@ -132,6 +127,12 @@ impl std::error::Error for Error {
} }
} }
impl From<()> for Error {
fn from(_: ()) -> Self {
Error::from(UnitError)
}
}
impl From<std::convert::Infallible> for Error { impl From<std::convert::Infallible> for Error {
fn from(_: std::convert::Infallible) -> Self { fn from(_: std::convert::Infallible) -> Self {
// `std::convert::Infallible` indicates an error // `std::convert::Infallible` indicates an error
@ -181,11 +182,11 @@ impl ResponseError for FormError {}
#[cfg(feature = "openssl")] #[cfg(feature = "openssl")]
/// `InternalServerError` for `openssl::ssl::Error` /// `InternalServerError` for `openssl::ssl::Error`
impl ResponseError for open_ssl::ssl::Error {} impl ResponseError for actix_connect::ssl::openssl::SslError {}
#[cfg(feature = "openssl")] #[cfg(feature = "openssl")]
/// `InternalServerError` for `openssl::ssl::HandshakeError` /// `InternalServerError` for `openssl::ssl::HandshakeError`
impl<T: std::fmt::Debug> ResponseError for open_ssl::ssl::HandshakeError<T> {} impl<T: std::fmt::Debug> ResponseError for actix_tls::openssl::HandshakeError<T> {}
/// Return `BAD_REQUEST` for `de::value::Error` /// Return `BAD_REQUEST` for `de::value::Error`
impl ResponseError for DeError { impl ResponseError for DeError {
@ -197,6 +198,9 @@ impl ResponseError for DeError {
/// `InternalServerError` for `Canceled` /// `InternalServerError` for `Canceled`
impl ResponseError for Canceled {} impl ResponseError for Canceled {}
/// `InternalServerError` for `BlockingError`
impl<E: fmt::Debug> ResponseError for BlockingError<E> {}
/// Return `BAD_REQUEST` for `Utf8Error` /// Return `BAD_REQUEST` for `Utf8Error`
impl ResponseError for Utf8Error { impl ResponseError for Utf8Error {
fn status_code(&self) -> StatusCode { fn status_code(&self) -> StatusCode {
@ -226,13 +230,6 @@ impl ResponseError for header::InvalidHeaderValue {
} }
} }
/// `BadRequest` for `InvalidHeaderValue`
impl ResponseError for header::InvalidHeaderValueBytes {
fn status_code(&self) -> StatusCode {
StatusCode::BAD_REQUEST
}
}
/// A set of errors that can occur during parsing HTTP streams /// A set of errors that can occur during parsing HTTP streams
#[derive(Debug, Display)] #[derive(Debug, Display)]
pub enum ParseError { pub enum ParseError {
@ -359,12 +356,15 @@ impl From<io::Error> for PayloadError {
} }
} }
impl From<Canceled> for PayloadError { impl From<BlockingError<io::Error>> for PayloadError {
fn from(_: Canceled) -> Self { fn from(err: BlockingError<io::Error>) -> Self {
PayloadError::Io(io::Error::new( match err {
io::ErrorKind::Other, BlockingError::Error(e) => PayloadError::Io(e),
"Operation is canceled", BlockingError::Canceled => PayloadError::Io(io::Error::new(
)) io::ErrorKind::Other,
"Operation is canceled",
)),
}
} }
} }

View File

@ -1,12 +1,12 @@
use std::any::{Any, TypeId}; use std::any::{Any, TypeId};
use std::fmt; use std::fmt;
use hashbrown::HashMap; use fxhash::FxHashMap;
#[derive(Default)] #[derive(Default)]
/// A type map of request extensions. /// A type map of request extensions.
pub struct Extensions { pub struct Extensions {
map: HashMap<TypeId, Box<dyn Any>>, map: FxHashMap<TypeId, Box<dyn Any>>,
} }
impl Extensions { impl Extensions {
@ -14,7 +14,7 @@ impl Extensions {
#[inline] #[inline]
pub fn new() -> Extensions { pub fn new() -> Extensions {
Extensions { Extensions {
map: HashMap::default(), map: FxHashMap::default(),
} }
} }

View File

@ -1,3 +1,4 @@
use std::convert::TryFrom;
use std::io; use std::io;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
@ -6,7 +7,7 @@ use std::task::Poll;
use actix_codec::Decoder; use actix_codec::Decoder;
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use http::header::{HeaderName, HeaderValue}; use http::header::{HeaderName, HeaderValue};
use http::{header, HttpTryFrom, Method, StatusCode, Uri, Version}; use http::{header, Method, StatusCode, Uri, Version};
use httparse; use httparse;
use log::{debug, error, trace}; use log::{debug, error, trace};
@ -79,8 +80,8 @@ pub(crate) trait MessageType: Sized {
// Unsafe: httparse check header value for valid utf-8 // Unsafe: httparse check header value for valid utf-8
let value = unsafe { let value = unsafe {
HeaderValue::from_shared_unchecked( HeaderValue::from_maybe_shared_unchecked(
slice.slice(idx.value.0, idx.value.1), slice.slice(idx.value.0..idx.value.1),
) )
}; };
match name { match name {
@ -428,7 +429,7 @@ impl Decoder for PayloadDecoder {
let len = src.len() as u64; let len = src.len() as u64;
let buf; let buf;
if *remaining > len { if *remaining > len {
buf = src.take().freeze(); buf = src.split().freeze();
*remaining -= len; *remaining -= len;
} else { } else {
buf = src.split_to(*remaining as usize).freeze(); buf = src.split_to(*remaining as usize).freeze();
@ -463,7 +464,7 @@ impl Decoder for PayloadDecoder {
if src.is_empty() { if src.is_empty() {
Ok(None) Ok(None)
} else { } else {
Ok(Some(PayloadItem::Chunk(src.take().freeze()))) Ok(Some(PayloadItem::Chunk(src.split().freeze())))
} }
} }
} }
@ -581,7 +582,7 @@ impl ChunkedState {
} else { } else {
let slice; let slice;
if *rem > len { if *rem > len {
slice = rdr.take().freeze(); slice = rdr.split().freeze();
*rem -= len; *rem -= len;
} else { } else {
slice = rdr.split_to(*rem as usize).freeze(); slice = rdr.split_to(*rem as usize).freeze();

View File

@ -2,12 +2,10 @@ use std::collections::VecDeque;
use std::future::Future; use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::time::Instant;
use std::{fmt, io, net}; use std::{fmt, io, net};
use actix_codec::{AsyncRead, Decoder, Encoder, Framed, FramedParts}; use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed, FramedParts};
use actix_rt::time::{delay, Delay}; use actix_rt::time::{delay_until, Delay, Instant};
use actix_server_config::IoStream;
use actix_service::Service; use actix_service::Service;
use bitflags::bitflags; use bitflags::bitflags;
use bytes::{BufMut, BytesMut}; use bytes::{BufMut, BytesMut};
@ -168,7 +166,7 @@ impl PartialEq for PollResponse {
impl<T, S, B, X, U> Dispatcher<T, S, B, X, U> impl<T, S, B, X, U> Dispatcher<T, S, B, X, U>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
S: Service<Request = Request>, S: Service<Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
@ -186,6 +184,7 @@ where
expect: CloneableService<X>, expect: CloneableService<X>,
upgrade: Option<CloneableService<U>>, upgrade: Option<CloneableService<U>>,
on_connect: Option<Box<dyn DataFactory>>, on_connect: Option<Box<dyn DataFactory>>,
peer_addr: Option<net::SocketAddr>,
) -> Self { ) -> Self {
Dispatcher::with_timeout( Dispatcher::with_timeout(
stream, stream,
@ -197,6 +196,7 @@ where
expect, expect,
upgrade, upgrade,
on_connect, on_connect,
peer_addr,
) )
} }
@ -211,6 +211,7 @@ where
expect: CloneableService<X>, expect: CloneableService<X>,
upgrade: Option<CloneableService<U>>, upgrade: Option<CloneableService<U>>,
on_connect: Option<Box<dyn DataFactory>>, on_connect: Option<Box<dyn DataFactory>>,
peer_addr: Option<net::SocketAddr>,
) -> Self { ) -> Self {
let keepalive = config.keep_alive_enabled(); let keepalive = config.keep_alive_enabled();
let flags = if keepalive { let flags = if keepalive {
@ -234,7 +235,6 @@ where
payload: None, payload: None,
state: State::None, state: State::None,
error: None, error: None,
peer_addr: io.peer_addr(),
messages: VecDeque::new(), messages: VecDeque::new(),
io, io,
codec, codec,
@ -244,6 +244,7 @@ where
upgrade, upgrade,
on_connect, on_connect,
flags, flags,
peer_addr,
ka_expire, ka_expire,
ka_timer, ka_timer,
}), }),
@ -253,7 +254,7 @@ where
impl<T, S, B, X, U> InnerDispatcher<T, S, B, X, U> impl<T, S, B, X, U> InnerDispatcher<T, S, B, X, U>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
S: Service<Request = Request>, S: Service<Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
@ -608,7 +609,7 @@ where
// shutdown timeout // shutdown timeout
if self.flags.contains(Flags::SHUTDOWN) { if self.flags.contains(Flags::SHUTDOWN) {
if let Some(interval) = self.codec.config().client_disconnect_timer() { if let Some(interval) = self.codec.config().client_disconnect_timer() {
self.ka_timer = Some(delay(interval)); self.ka_timer = Some(delay_until(interval));
} else { } else {
self.flags.insert(Flags::READ_DISCONNECT); self.flags.insert(Flags::READ_DISCONNECT);
if let Some(mut payload) = self.payload.take() { if let Some(mut payload) = self.payload.take() {
@ -682,7 +683,7 @@ where
impl<T, S, B, X, U> Unpin for Dispatcher<T, S, B, X, U> impl<T, S, B, X, U> Unpin for Dispatcher<T, S, B, X, U>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
S: Service<Request = Request>, S: Service<Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
@ -696,7 +697,7 @@ where
impl<T, S, B, X, U> Future for Dispatcher<T, S, B, X, U> impl<T, S, B, X, U> Future for Dispatcher<T, S, B, X, U>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
S: Service<Request = Request>, S: Service<Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
@ -907,6 +908,7 @@ mod tests {
CloneableService::new(ExpectHandler), CloneableService::new(ExpectHandler),
None, None,
None, None,
None,
); );
match Pin::new(&mut h1).poll(cx) { match Pin::new(&mut h1).poll(cx) {
Poll::Pending => panic!(), Poll::Pending => panic!(),

View File

@ -2,11 +2,13 @@
use std::fmt::Write as FmtWrite; use std::fmt::Write as FmtWrite;
use std::io::Write; use std::io::Write;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ptr::copy_nonoverlapping;
use std::rc::Rc; use std::rc::Rc;
use std::slice::from_raw_parts_mut;
use std::str::FromStr; use std::str::FromStr;
use std::{cmp, fmt, io, mem}; use std::{cmp, fmt, io, mem};
use bytes::{BufMut, Bytes, BytesMut}; use bytes::{buf::BufMutExt, BufMut, Bytes, BytesMut};
use crate::body::BodySize; use crate::body::BodySize;
use crate::config::ServiceConfig; use crate::config::ServiceConfig;
@ -144,8 +146,8 @@ pub(crate) trait MessageType: Sized {
// write headers // write headers
let mut pos = 0; let mut pos = 0;
let mut has_date = false; let mut has_date = false;
let mut remaining = dst.remaining_mut(); let mut remaining = dst.capacity() - dst.len();
let mut buf = unsafe { &mut *(dst.bytes_mut() as *mut [u8]) }; let mut buf = dst.bytes_mut().as_mut_ptr() as *mut u8;
for (key, value) in headers { for (key, value) in headers {
match *key { match *key {
CONNECTION => continue, CONNECTION => continue,
@ -159,61 +161,67 @@ pub(crate) trait MessageType: Sized {
match value { match value {
map::Value::One(ref val) => { map::Value::One(ref val) => {
let v = val.as_ref(); let v = val.as_ref();
let len = k.len() + v.len() + 4; let v_len = v.len();
let k_len = k.len();
let len = k_len + v_len + 4;
if len > remaining { if len > remaining {
unsafe { unsafe {
dst.advance_mut(pos); dst.advance_mut(pos);
} }
pos = 0; pos = 0;
dst.reserve(len * 2); dst.reserve(len * 2);
remaining = dst.remaining_mut(); remaining = dst.capacity() - dst.len();
unsafe { buf = dst.bytes_mut().as_mut_ptr() as *mut u8;
buf = &mut *(dst.bytes_mut() as *mut _);
}
} }
// use upper Camel-Case // use upper Camel-Case
if camel_case { unsafe {
write_camel_case(k, &mut buf[pos..pos + k.len()]); if camel_case {
} else { write_camel_case(k, from_raw_parts_mut(buf, k_len))
buf[pos..pos + k.len()].copy_from_slice(k); } else {
write_data(k, buf, k_len)
}
buf = buf.add(k_len);
write_data(b": ", buf, 2);
buf = buf.add(2);
write_data(v, buf, v_len);
buf = buf.add(v_len);
write_data(b"\r\n", buf, 2);
buf = buf.add(2);
pos += len;
remaining -= len;
} }
pos += k.len();
buf[pos..pos + 2].copy_from_slice(b": ");
pos += 2;
buf[pos..pos + v.len()].copy_from_slice(v);
pos += v.len();
buf[pos..pos + 2].copy_from_slice(b"\r\n");
pos += 2;
remaining -= len;
} }
map::Value::Multi(ref vec) => { map::Value::Multi(ref vec) => {
for val in vec { for val in vec {
let v = val.as_ref(); let v = val.as_ref();
let len = k.len() + v.len() + 4; let v_len = v.len();
let k_len = k.len();
let len = k_len + v_len + 4;
if len > remaining { if len > remaining {
unsafe { unsafe {
dst.advance_mut(pos); dst.advance_mut(pos);
} }
pos = 0; pos = 0;
dst.reserve(len * 2); dst.reserve(len * 2);
remaining = dst.remaining_mut(); remaining = dst.capacity() - dst.len();
unsafe { buf = dst.bytes_mut().as_mut_ptr() as *mut u8;
buf = &mut *(dst.bytes_mut() as *mut _);
}
} }
// use upper Camel-Case // use upper Camel-Case
if camel_case { unsafe {
write_camel_case(k, &mut buf[pos..pos + k.len()]); if camel_case {
} else { write_camel_case(k, from_raw_parts_mut(buf, k_len));
buf[pos..pos + k.len()].copy_from_slice(k); } else {
} write_data(k, buf, k_len);
pos += k.len(); }
buf[pos..pos + 2].copy_from_slice(b": "); buf = buf.add(k_len);
pos += 2; write_data(b": ", buf, 2);
buf[pos..pos + v.len()].copy_from_slice(v); buf = buf.add(2);
pos += v.len(); write_data(v, buf, v_len);
buf[pos..pos + 2].copy_from_slice(b"\r\n"); buf = buf.add(v_len);
pos += 2; write_data(b"\r\n", buf, 2);
buf = buf.add(2);
};
pos += len;
remaining -= len; remaining -= len;
} }
} }
@ -298,6 +306,12 @@ impl MessageType for RequestHeadType {
Version::HTTP_10 => "HTTP/1.0", Version::HTTP_10 => "HTTP/1.0",
Version::HTTP_11 => "HTTP/1.1", Version::HTTP_11 => "HTTP/1.1",
Version::HTTP_2 => "HTTP/2.0", Version::HTTP_2 => "HTTP/2.0",
Version::HTTP_3 => "HTTP/3.0",
_ =>
return Err(io::Error::new(
io::ErrorKind::Other,
"unsupported version"
)),
} }
) )
.map_err(|e| io::Error::new(io::ErrorKind::Other, e)) .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
@ -479,6 +493,10 @@ impl<'a> io::Write for Writer<'a> {
} }
} }
unsafe fn write_data(value: &[u8], buf: *mut u8, len: usize) {
copy_nonoverlapping(value.as_ptr(), buf, len);
}
fn write_camel_case(value: &[u8], buffer: &mut [u8]) { fn write_camel_case(value: &[u8], buffer: &mut [u8]) {
let mut index = 0; let mut index = 0;
let key = value; let key = value;
@ -525,7 +543,7 @@ mod tests {
assert!(enc.encode(b"", &mut bytes).ok().unwrap()); assert!(enc.encode(b"", &mut bytes).ok().unwrap());
} }
assert_eq!( assert_eq!(
bytes.take().freeze(), bytes.split().freeze(),
Bytes::from_static(b"4\r\ntest\r\n0\r\n\r\n") Bytes::from_static(b"4\r\ntest\r\n0\r\n\r\n")
); );
} }
@ -548,7 +566,8 @@ mod tests {
ConnectionType::Close, ConnectionType::Close,
&ServiceConfig::default(), &ServiceConfig::default(),
); );
let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap(); let data =
String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap();
assert!(data.contains("Content-Length: 0\r\n")); assert!(data.contains("Content-Length: 0\r\n"));
assert!(data.contains("Connection: close\r\n")); assert!(data.contains("Connection: close\r\n"));
assert!(data.contains("Content-Type: plain/text\r\n")); assert!(data.contains("Content-Type: plain/text\r\n"));
@ -561,7 +580,8 @@ mod tests {
ConnectionType::KeepAlive, ConnectionType::KeepAlive,
&ServiceConfig::default(), &ServiceConfig::default(),
); );
let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap(); let data =
String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap();
assert!(data.contains("Transfer-Encoding: chunked\r\n")); assert!(data.contains("Transfer-Encoding: chunked\r\n"));
assert!(data.contains("Content-Type: plain/text\r\n")); assert!(data.contains("Content-Type: plain/text\r\n"));
assert!(data.contains("Date: date\r\n")); assert!(data.contains("Date: date\r\n"));
@ -573,7 +593,8 @@ mod tests {
ConnectionType::KeepAlive, ConnectionType::KeepAlive,
&ServiceConfig::default(), &ServiceConfig::default(),
); );
let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap(); let data =
String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap();
assert!(data.contains("Content-Length: 100\r\n")); assert!(data.contains("Content-Length: 100\r\n"));
assert!(data.contains("Content-Type: plain/text\r\n")); assert!(data.contains("Content-Type: plain/text\r\n"));
assert!(data.contains("Date: date\r\n")); assert!(data.contains("Date: date\r\n"));
@ -594,7 +615,8 @@ mod tests {
ConnectionType::KeepAlive, ConnectionType::KeepAlive,
&ServiceConfig::default(), &ServiceConfig::default(),
); );
let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap(); let data =
String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap();
assert!(data.contains("transfer-encoding: chunked\r\n")); assert!(data.contains("transfer-encoding: chunked\r\n"));
assert!(data.contains("content-type: xml\r\n")); assert!(data.contains("content-type: xml\r\n"));
assert!(data.contains("content-type: plain/text\r\n")); assert!(data.contains("content-type: plain/text\r\n"));
@ -627,7 +649,8 @@ mod tests {
ConnectionType::Close, ConnectionType::Close,
&ServiceConfig::default(), &ServiceConfig::default(),
); );
let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap(); let data =
String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap();
assert!(data.contains("content-length: 0\r\n")); assert!(data.contains("content-length: 0\r\n"));
assert!(data.contains("connection: close\r\n")); assert!(data.contains("connection: close\r\n"));
assert!(data.contains("authorization: another authorization\r\n")); assert!(data.contains("authorization: another authorization\r\n"));

View File

@ -1,6 +1,5 @@
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use actix_server_config::ServerConfig;
use actix_service::{Service, ServiceFactory}; use actix_service::{Service, ServiceFactory};
use futures::future::{ok, Ready}; use futures::future::{ok, Ready};
@ -10,7 +9,7 @@ use crate::request::Request;
pub struct ExpectHandler; pub struct ExpectHandler;
impl ServiceFactory for ExpectHandler { impl ServiceFactory for ExpectHandler {
type Config = ServerConfig; type Config = ();
type Request = Request; type Request = Request;
type Response = Request; type Response = Request;
type Error = Error; type Error = Error;
@ -18,7 +17,7 @@ impl ServiceFactory for ExpectHandler {
type InitError = Error; type InitError = Error;
type Future = Ready<Result<Self::Service, Self::InitError>>; type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: &ServerConfig) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
ok(ExpectHandler) ok(ExpectHandler)
} }
} }

View File

@ -1,19 +1,19 @@
use std::fmt;
use std::future::Future; use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::{fmt, net};
use actix_codec::Framed; use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_server_config::{Io, IoStream, ServerConfig as SrvConfig}; use actix_rt::net::TcpStream;
use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory};
use futures::future::{ok, Ready}; use futures::future::{ok, Ready};
use futures::ready; use futures::ready;
use crate::body::MessageBody; use crate::body::MessageBody;
use crate::cloneable::CloneableService; use crate::cloneable::CloneableService;
use crate::config::{KeepAlive, ServiceConfig}; use crate::config::ServiceConfig;
use crate::error::{DispatchError, Error, ParseError}; use crate::error::{DispatchError, Error, ParseError};
use crate::helpers::DataFactory; use crate::helpers::DataFactory;
use crate::request::Request; use crate::request::Request;
@ -24,39 +24,25 @@ use super::dispatcher::Dispatcher;
use super::{ExpectHandler, Message, UpgradeHandler}; use super::{ExpectHandler, Message, UpgradeHandler};
/// `ServiceFactory` implementation for HTTP1 transport /// `ServiceFactory` implementation for HTTP1 transport
pub struct H1Service<T, P, S, B, X = ExpectHandler, U = UpgradeHandler<T>> { pub struct H1Service<T, S, B, X = ExpectHandler, U = UpgradeHandler<T>> {
srv: S, srv: S,
cfg: ServiceConfig, cfg: ServiceConfig,
expect: X, expect: X,
upgrade: Option<U>, upgrade: Option<U>,
on_connect: Option<Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>, on_connect: Option<Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>,
_t: PhantomData<(T, P, B)>, _t: PhantomData<(T, B)>,
} }
impl<T, P, S, B> H1Service<T, P, S, B> impl<T, S, B> H1Service<T, S, B>
where where
S: ServiceFactory<Config = SrvConfig, Request = Request>, S: ServiceFactory<Config = (), Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
B: MessageBody, B: MessageBody,
{ {
/// Create new `HttpService` instance with default config.
pub fn new<F: IntoServiceFactory<S>>(service: F) -> Self {
let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0);
H1Service {
cfg,
srv: service.into_factory(),
expect: ExpectHandler,
upgrade: None,
on_connect: None,
_t: PhantomData,
}
}
/// Create new `HttpService` instance with config. /// Create new `HttpService` instance with config.
pub fn with_config<F: IntoServiceFactory<S>>( pub(crate) fn with_config<F: IntoServiceFactory<S>>(
cfg: ServiceConfig, cfg: ServiceConfig,
service: F, service: F,
) -> Self { ) -> Self {
@ -71,15 +57,151 @@ where
} }
} }
impl<T, P, S, B, X, U> H1Service<T, P, S, B, X, U> impl<S, B, X, U> H1Service<TcpStream, S, B, X, U>
where where
S: ServiceFactory<Config = SrvConfig, Request = Request>, S: ServiceFactory<Config = (), Request = Request>,
S::Error: Into<Error>,
S::InitError: fmt::Debug,
S::Response: Into<Response<B>>,
B: MessageBody,
X: ServiceFactory<Config = (), Request = Request, Response = Request>,
X::Error: Into<Error>,
X::InitError: fmt::Debug,
U: ServiceFactory<
Config = (),
Request = (Request, Framed<TcpStream, Codec>),
Response = (),
>,
U::Error: fmt::Display,
U::InitError: fmt::Debug,
{
/// Create simple tcp stream service
pub fn tcp(
self,
) -> impl ServiceFactory<
Config = (),
Request = TcpStream,
Response = (),
Error = DispatchError,
InitError = (),
> {
pipeline_factory(|io: TcpStream| {
let peer_addr = io.peer_addr().ok();
ok((io, peer_addr))
})
.and_then(self)
}
}
#[cfg(feature = "openssl")]
mod openssl {
use super::*;
use actix_tls::openssl::{Acceptor, SslAcceptor, SslStream};
use actix_tls::{openssl::HandshakeError, SslError};
impl<S, B, X, U> H1Service<SslStream<TcpStream>, S, B, X, U>
where
S: ServiceFactory<Config = (), Request = Request>,
S::Error: Into<Error>,
S::InitError: fmt::Debug,
S::Response: Into<Response<B>>,
B: MessageBody,
X: ServiceFactory<Config = (), Request = Request, Response = Request>,
X::Error: Into<Error>,
X::InitError: fmt::Debug,
U: ServiceFactory<
Config = (),
Request = (Request, Framed<SslStream<TcpStream>, Codec>),
Response = (),
>,
U::Error: fmt::Display,
U::InitError: fmt::Debug,
{
/// Create openssl based service
pub fn openssl(
self,
acceptor: SslAcceptor,
) -> impl ServiceFactory<
Config = (),
Request = TcpStream,
Response = (),
Error = SslError<HandshakeError<TcpStream>, DispatchError>,
InitError = (),
> {
pipeline_factory(
Acceptor::new(acceptor)
.map_err(SslError::Ssl)
.map_init_err(|_| panic!()),
)
.and_then(|io: SslStream<TcpStream>| {
let peer_addr = io.get_ref().peer_addr().ok();
ok((io, peer_addr))
})
.and_then(self.map_err(SslError::Service))
}
}
}
#[cfg(feature = "rustls")]
mod rustls {
use super::*;
use actix_tls::rustls::{Acceptor, ServerConfig, TlsStream};
use actix_tls::SslError;
use std::{fmt, io};
impl<S, B, X, U> H1Service<TlsStream<TcpStream>, S, B, X, U>
where
S: ServiceFactory<Config = (), Request = Request>,
S::Error: Into<Error>,
S::InitError: fmt::Debug,
S::Response: Into<Response<B>>,
B: MessageBody,
X: ServiceFactory<Config = (), Request = Request, Response = Request>,
X::Error: Into<Error>,
X::InitError: fmt::Debug,
U: ServiceFactory<
Config = (),
Request = (Request, Framed<TlsStream<TcpStream>, Codec>),
Response = (),
>,
U::Error: fmt::Display,
U::InitError: fmt::Debug,
{
/// Create rustls based service
pub fn rustls(
self,
config: ServerConfig,
) -> impl ServiceFactory<
Config = (),
Request = TcpStream,
Response = (),
Error = SslError<io::Error, DispatchError>,
InitError = (),
> {
pipeline_factory(
Acceptor::new(config)
.map_err(SslError::Ssl)
.map_init_err(|_| panic!()),
)
.and_then(|io: TlsStream<TcpStream>| {
let peer_addr = io.get_ref().0.peer_addr().ok();
ok((io, peer_addr))
})
.and_then(self.map_err(SslError::Service))
}
}
}
impl<T, S, B, X, U> H1Service<T, S, B, X, U>
where
S: ServiceFactory<Config = (), Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
B: MessageBody, B: MessageBody,
{ {
pub fn expect<X1>(self, expect: X1) -> H1Service<T, P, S, B, X1, U> pub fn expect<X1>(self, expect: X1) -> H1Service<T, S, B, X1, U>
where where
X1: ServiceFactory<Request = Request, Response = Request>, X1: ServiceFactory<Request = Request, Response = Request>,
X1::Error: Into<Error>, X1::Error: Into<Error>,
@ -95,7 +217,7 @@ where
} }
} }
pub fn upgrade<U1>(self, upgrade: Option<U1>) -> H1Service<T, P, S, B, X, U1> pub fn upgrade<U1>(self, upgrade: Option<U1>) -> H1Service<T, S, B, X, U1>
where where
U1: ServiceFactory<Request = (Request, Framed<T, Codec>), Response = ()>, U1: ServiceFactory<Request = (Request, Framed<T, Codec>), Response = ()>,
U1::Error: fmt::Display, U1::Error: fmt::Display,
@ -121,38 +243,34 @@ where
} }
} }
impl<T, P, S, B, X, U> ServiceFactory for H1Service<T, P, S, B, X, U> impl<T, S, B, X, U> ServiceFactory for H1Service<T, S, B, X, U>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
S: ServiceFactory<Config = SrvConfig, Request = Request>, S: ServiceFactory<Config = (), Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
B: MessageBody, B: MessageBody,
X: ServiceFactory<Config = SrvConfig, Request = Request, Response = Request>, X: ServiceFactory<Config = (), Request = Request, Response = Request>,
X::Error: Into<Error>, X::Error: Into<Error>,
X::InitError: fmt::Debug, X::InitError: fmt::Debug,
U: ServiceFactory< U: ServiceFactory<Config = (), Request = (Request, Framed<T, Codec>), Response = ()>,
Config = SrvConfig,
Request = (Request, Framed<T, Codec>),
Response = (),
>,
U::Error: fmt::Display, U::Error: fmt::Display,
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
{ {
type Config = SrvConfig; type Config = ();
type Request = Io<T, P>; type Request = (T, Option<net::SocketAddr>);
type Response = (); type Response = ();
type Error = DispatchError; type Error = DispatchError;
type InitError = (); type InitError = ();
type Service = H1ServiceHandler<T, P, S::Service, B, X::Service, U::Service>; type Service = H1ServiceHandler<T, S::Service, B, X::Service, U::Service>;
type Future = H1ServiceResponse<T, P, S, B, X, U>; type Future = H1ServiceResponse<T, S, B, X, U>;
fn new_service(&self, cfg: &SrvConfig) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
H1ServiceResponse { H1ServiceResponse {
fut: self.srv.new_service(cfg), fut: self.srv.new_service(()),
fut_ex: Some(self.expect.new_service(cfg)), fut_ex: Some(self.expect.new_service(())),
fut_upg: self.upgrade.as_ref().map(|f| f.new_service(cfg)), fut_upg: self.upgrade.as_ref().map(|f| f.new_service(())),
expect: None, expect: None,
upgrade: None, upgrade: None,
on_connect: self.on_connect.clone(), on_connect: self.on_connect.clone(),
@ -164,7 +282,7 @@ where
#[doc(hidden)] #[doc(hidden)]
#[pin_project::pin_project] #[pin_project::pin_project]
pub struct H1ServiceResponse<T, P, S, B, X, U> pub struct H1ServiceResponse<T, S, B, X, U>
where where
S: ServiceFactory<Request = Request>, S: ServiceFactory<Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
@ -186,12 +304,12 @@ where
upgrade: Option<U::Service>, upgrade: Option<U::Service>,
on_connect: Option<Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>, on_connect: Option<Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>,
cfg: Option<ServiceConfig>, cfg: Option<ServiceConfig>,
_t: PhantomData<(T, P, B)>, _t: PhantomData<(T, B)>,
} }
impl<T, P, S, B, X, U> Future for H1ServiceResponse<T, P, S, B, X, U> impl<T, S, B, X, U> Future for H1ServiceResponse<T, S, B, X, U>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
S: ServiceFactory<Request = Request>, S: ServiceFactory<Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
@ -204,8 +322,7 @@ where
U::Error: fmt::Display, U::Error: fmt::Display,
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
{ {
type Output = type Output = Result<H1ServiceHandler<T, S::Service, B, X::Service, U::Service>, ()>;
Result<H1ServiceHandler<T, P, S::Service, B, X::Service, U::Service>, ()>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let mut this = self.as_mut().project(); let mut this = self.as_mut().project();
@ -247,16 +364,16 @@ where
} }
/// `Service` implementation for HTTP1 transport /// `Service` implementation for HTTP1 transport
pub struct H1ServiceHandler<T, P, S, B, X, U> { pub struct H1ServiceHandler<T, S, B, X, U> {
srv: CloneableService<S>, srv: CloneableService<S>,
expect: CloneableService<X>, expect: CloneableService<X>,
upgrade: Option<CloneableService<U>>, upgrade: Option<CloneableService<U>>,
on_connect: Option<Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>, on_connect: Option<Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>,
cfg: ServiceConfig, cfg: ServiceConfig,
_t: PhantomData<(T, P, B)>, _t: PhantomData<(T, B)>,
} }
impl<T, P, S, B, X, U> H1ServiceHandler<T, P, S, B, X, U> impl<T, S, B, X, U> H1ServiceHandler<T, S, B, X, U>
where where
S: Service<Request = Request>, S: Service<Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
@ -273,7 +390,7 @@ where
expect: X, expect: X,
upgrade: Option<U>, upgrade: Option<U>,
on_connect: Option<Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>, on_connect: Option<Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>,
) -> H1ServiceHandler<T, P, S, B, X, U> { ) -> H1ServiceHandler<T, S, B, X, U> {
H1ServiceHandler { H1ServiceHandler {
srv: CloneableService::new(srv), srv: CloneableService::new(srv),
expect: CloneableService::new(expect), expect: CloneableService::new(expect),
@ -285,9 +402,9 @@ where
} }
} }
impl<T, P, S, B, X, U> Service for H1ServiceHandler<T, P, S, B, X, U> impl<T, S, B, X, U> Service for H1ServiceHandler<T, S, B, X, U>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
S: Service<Request = Request>, S: Service<Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
@ -297,7 +414,7 @@ where
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>, U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
U::Error: fmt::Display, U::Error: fmt::Display,
{ {
type Request = Io<T, P>; type Request = (T, Option<net::SocketAddr>);
type Response = (); type Response = ();
type Error = DispatchError; type Error = DispatchError;
type Future = Dispatcher<T, S, B, X, U>; type Future = Dispatcher<T, S, B, X, U>;
@ -331,9 +448,7 @@ where
} }
} }
fn call(&mut self, req: Self::Request) -> Self::Future { fn call(&mut self, (io, addr): Self::Request) -> Self::Future {
let io = req.into_parts().0;
let on_connect = if let Some(ref on_connect) = self.on_connect { let on_connect = if let Some(ref on_connect) = self.on_connect {
Some(on_connect(&io)) Some(on_connect(&io))
} else { } else {
@ -347,20 +462,21 @@ where
self.expect.clone(), self.expect.clone(),
self.upgrade.clone(), self.upgrade.clone(),
on_connect, on_connect,
addr,
) )
} }
} }
/// `ServiceFactory` implementation for `OneRequestService` service /// `ServiceFactory` implementation for `OneRequestService` service
#[derive(Default)] #[derive(Default)]
pub struct OneRequest<T, P> { pub struct OneRequest<T> {
config: ServiceConfig, config: ServiceConfig,
_t: PhantomData<(T, P)>, _t: PhantomData<T>,
} }
impl<T, P> OneRequest<T, P> impl<T> OneRequest<T>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
{ {
/// Create new `H1SimpleService` instance. /// Create new `H1SimpleService` instance.
pub fn new() -> Self { pub fn new() -> Self {
@ -371,38 +487,38 @@ where
} }
} }
impl<T, P> ServiceFactory for OneRequest<T, P> impl<T> ServiceFactory for OneRequest<T>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
{ {
type Config = SrvConfig; type Config = ();
type Request = Io<T, P>; type Request = T;
type Response = (Request, Framed<T, Codec>); type Response = (Request, Framed<T, Codec>);
type Error = ParseError; type Error = ParseError;
type InitError = (); type InitError = ();
type Service = OneRequestService<T, P>; type Service = OneRequestService<T>;
type Future = Ready<Result<Self::Service, Self::InitError>>; type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: &SrvConfig) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
ok(OneRequestService { ok(OneRequestService {
config: self.config.clone(),
_t: PhantomData, _t: PhantomData,
config: self.config.clone(),
}) })
} }
} }
/// `Service` implementation for HTTP1 transport. Reads one request and returns /// `Service` implementation for HTTP1 transport. Reads one request and returns
/// request and framed object. /// request and framed object.
pub struct OneRequestService<T, P> { pub struct OneRequestService<T> {
_t: PhantomData<T>,
config: ServiceConfig, config: ServiceConfig,
_t: PhantomData<(T, P)>,
} }
impl<T, P> Service for OneRequestService<T, P> impl<T> Service for OneRequestService<T>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
{ {
type Request = Io<T, P>; type Request = T;
type Response = (Request, Framed<T, Codec>); type Response = (Request, Framed<T, Codec>);
type Error = ParseError; type Error = ParseError;
type Future = OneRequestServiceResponse<T>; type Future = OneRequestServiceResponse<T>;
@ -413,10 +529,7 @@ where
fn call(&mut self, req: Self::Request) -> Self::Future { fn call(&mut self, req: Self::Request) -> Self::Future {
OneRequestServiceResponse { OneRequestServiceResponse {
framed: Some(Framed::new( framed: Some(Framed::new(req, Codec::new(self.config.clone()))),
req.into_parts().0,
Codec::new(self.config.clone()),
)),
} }
} }
} }
@ -424,14 +537,14 @@ where
#[doc(hidden)] #[doc(hidden)]
pub struct OneRequestServiceResponse<T> pub struct OneRequestServiceResponse<T>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
{ {
framed: Option<Framed<T, Codec>>, framed: Option<Framed<T, Codec>>,
} }
impl<T> Future for OneRequestServiceResponse<T> impl<T> Future for OneRequestServiceResponse<T>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
{ {
type Output = Result<(Request, Framed<T, Codec>), ParseError>; type Output = Result<(Request, Framed<T, Codec>), ParseError>;

View File

@ -2,7 +2,6 @@ use std::marker::PhantomData;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use actix_codec::Framed; use actix_codec::Framed;
use actix_server_config::ServerConfig;
use actix_service::{Service, ServiceFactory}; use actix_service::{Service, ServiceFactory};
use futures::future::Ready; use futures::future::Ready;
@ -13,7 +12,7 @@ use crate::request::Request;
pub struct UpgradeHandler<T>(PhantomData<T>); pub struct UpgradeHandler<T>(PhantomData<T>);
impl<T> ServiceFactory for UpgradeHandler<T> { impl<T> ServiceFactory for UpgradeHandler<T> {
type Config = ServerConfig; type Config = ();
type Request = (Request, Framed<T, Codec>); type Request = (Request, Framed<T, Codec>);
type Response = (); type Response = ();
type Error = Error; type Error = Error;
@ -21,7 +20,7 @@ impl<T> ServiceFactory for UpgradeHandler<T> {
type InitError = Error; type InitError = Error;
type Future = Ready<Result<Self::Service, Self::InitError>>; type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: &ServerConfig) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
unimplemented!() unimplemented!()
} }
} }

View File

@ -1,14 +1,13 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use std::convert::TryFrom;
use std::future::Future; use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::time::Instant;
use std::{fmt, mem, net}; use std::{fmt, mem, net};
use actix_codec::{AsyncRead, AsyncWrite}; use actix_codec::{AsyncRead, AsyncWrite};
use actix_rt::time::Delay; use actix_rt::time::{Delay, Instant};
use actix_server_config::IoStream;
use actix_service::Service; use actix_service::Service;
use bitflags::bitflags; use bitflags::bitflags;
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
@ -18,7 +17,6 @@ use h2::{RecvStream, SendStream};
use http::header::{ use http::header::{
HeaderValue, ACCEPT_ENCODING, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING, HeaderValue, ACCEPT_ENCODING, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING,
}; };
use http::HttpTryFrom;
use log::{debug, error, trace}; use log::{debug, error, trace};
use crate::body::{Body, BodySize, MessageBody, ResponseBody}; use crate::body::{Body, BodySize, MessageBody, ResponseBody};
@ -36,7 +34,10 @@ const CHUNK_SIZE: usize = 16_384;
/// Dispatcher for HTTP/2 protocol /// Dispatcher for HTTP/2 protocol
#[pin_project::pin_project] #[pin_project::pin_project]
pub struct Dispatcher<T: IoStream, S: Service<Request = Request>, B: MessageBody> { pub struct Dispatcher<T, S: Service<Request = Request>, B: MessageBody>
where
T: AsyncRead + AsyncWrite + Unpin,
{
service: CloneableService<S>, service: CloneableService<S>,
connection: Connection<T, Bytes>, connection: Connection<T, Bytes>,
on_connect: Option<Box<dyn DataFactory>>, on_connect: Option<Box<dyn DataFactory>>,
@ -49,7 +50,7 @@ pub struct Dispatcher<T: IoStream, S: Service<Request = Request>, B: MessageBody
impl<T, S, B> Dispatcher<T, S, B> impl<T, S, B> Dispatcher<T, S, B>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
S: Service<Request = Request>, S: Service<Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
// S::Future: 'static, // S::Future: 'static,
@ -95,7 +96,7 @@ where
impl<T, S, B> Future for Dispatcher<T, S, B> impl<T, S, B> Future for Dispatcher<T, S, B>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
S: Service<Request = Request>, S: Service<Request = Request>,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::Future: 'static, S::Future: 'static,
@ -233,8 +234,9 @@ where
if !has_date { if !has_date {
let mut bytes = BytesMut::with_capacity(29); let mut bytes = BytesMut::with_capacity(29);
self.config.set_date_header(&mut bytes); self.config.set_date_header(&mut bytes);
res.headers_mut() res.headers_mut().insert(DATE, unsafe {
.insert(DATE, HeaderValue::try_from(bytes.freeze()).unwrap()); HeaderValue::from_maybe_shared_unchecked(bytes.freeze())
});
} }
res res

View File

@ -35,7 +35,7 @@ impl Stream for Payload {
match Pin::new(&mut this.pl).poll_data(cx) { match Pin::new(&mut this.pl).poll_data(cx) {
Poll::Ready(Some(Ok(chunk))) => { Poll::Ready(Some(Ok(chunk))) => {
let len = chunk.len(); let len = chunk.len();
if let Err(err) = this.pl.release_capacity().release_capacity(len) { if let Err(err) = this.pl.flow_control().release_capacity(len) {
Poll::Ready(Some(Err(err.into()))) Poll::Ready(Some(Err(err.into())))
} else { } else {
Poll::Ready(Some(Ok(chunk))) Poll::Ready(Some(Ok(chunk)))

View File

@ -6,8 +6,11 @@ use std::task::{Context, Poll};
use std::{io, net, rc}; use std::{io, net, rc};
use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_server_config::{Io, IoStream, ServerConfig as SrvConfig}; use actix_rt::net::TcpStream;
use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use actix_service::{
factory_fn, pipeline_factory, service_fn2, IntoServiceFactory, Service,
ServiceFactory,
};
use bytes::Bytes; use bytes::Bytes;
use futures::future::{ok, Ready}; use futures::future::{ok, Ready};
use futures::{ready, Stream}; use futures::{ready, Stream};
@ -23,39 +26,28 @@ use crate::helpers::DataFactory;
use crate::payload::Payload; use crate::payload::Payload;
use crate::request::Request; use crate::request::Request;
use crate::response::Response; use crate::response::Response;
use crate::Protocol;
use super::dispatcher::Dispatcher; use super::dispatcher::Dispatcher;
/// `ServiceFactory` implementation for HTTP2 transport /// `ServiceFactory` implementation for HTTP2 transport
pub struct H2Service<T, P, S, B> { pub struct H2Service<T, S, B> {
srv: S, srv: S,
cfg: ServiceConfig, cfg: ServiceConfig,
on_connect: Option<rc::Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>, on_connect: Option<rc::Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>,
_t: PhantomData<(T, P, B)>, _t: PhantomData<(T, B)>,
} }
impl<T, P, S, B> H2Service<T, P, S, B> impl<T, S, B> H2Service<T, S, B>
where where
S: ServiceFactory<Config = SrvConfig, Request = Request>, S: ServiceFactory<Config = (), Request = Request>,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::Response: Into<Response<B>> + 'static, S::Response: Into<Response<B>> + 'static,
<S::Service as Service>::Future: 'static, <S::Service as Service>::Future: 'static,
B: MessageBody + 'static, B: MessageBody + 'static,
{ {
/// Create new `HttpService` instance.
pub fn new<F: IntoServiceFactory<S>>(service: F) -> Self {
let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0);
H2Service {
cfg,
on_connect: None,
srv: service.into_factory(),
_t: PhantomData,
}
}
/// Create new `HttpService` instance with config. /// Create new `HttpService` instance with config.
pub fn with_config<F: IntoServiceFactory<S>>( pub(crate) fn with_config<F: IntoServiceFactory<S>>(
cfg: ServiceConfig, cfg: ServiceConfig,
service: F, service: F,
) -> Self { ) -> Self {
@ -77,26 +69,144 @@ where
} }
} }
impl<T, P, S, B> ServiceFactory for H2Service<T, P, S, B> impl<S, B> H2Service<TcpStream, S, B>
where where
T: IoStream, S: ServiceFactory<Config = (), Request = Request>,
S: ServiceFactory<Config = SrvConfig, Request = Request>,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::Response: Into<Response<B>> + 'static, S::Response: Into<Response<B>> + 'static,
<S::Service as Service>::Future: 'static, <S::Service as Service>::Future: 'static,
B: MessageBody + 'static, B: MessageBody + 'static,
{ {
type Config = SrvConfig; /// Create simple tcp based service
type Request = Io<T, P>; pub fn tcp(
self,
) -> impl ServiceFactory<
Config = (),
Request = TcpStream,
Response = (),
Error = DispatchError,
InitError = S::InitError,
> {
pipeline_factory(factory_fn(|| {
async {
Ok::<_, S::InitError>(service_fn2(|io: TcpStream| {
let peer_addr = io.peer_addr().ok();
ok::<_, DispatchError>((io, peer_addr))
}))
}
}))
.and_then(self)
}
}
#[cfg(feature = "openssl")]
mod openssl {
use actix_service::{factory_fn, service_fn2};
use actix_tls::openssl::{Acceptor, SslAcceptor, SslStream};
use actix_tls::{openssl::HandshakeError, SslError};
use super::*;
impl<S, B> H2Service<SslStream<TcpStream>, S, B>
where
S: ServiceFactory<Config = (), Request = Request>,
S::Error: Into<Error> + 'static,
S::Response: Into<Response<B>> + 'static,
<S::Service as Service>::Future: 'static,
B: MessageBody + 'static,
{
/// Create ssl based service
pub fn openssl(
self,
acceptor: SslAcceptor,
) -> impl ServiceFactory<
Config = (),
Request = TcpStream,
Response = (),
Error = SslError<HandshakeError<TcpStream>, DispatchError>,
InitError = S::InitError,
> {
pipeline_factory(
Acceptor::new(acceptor)
.map_err(SslError::Ssl)
.map_init_err(|_| panic!()),
)
.and_then(factory_fn(|| {
ok::<_, S::InitError>(service_fn2(|io: SslStream<TcpStream>| {
let peer_addr = io.get_ref().peer_addr().ok();
ok((io, peer_addr))
}))
}))
.and_then(self.map_err(SslError::Service))
}
}
}
#[cfg(feature = "rustls")]
mod rustls {
use super::*;
use actix_tls::rustls::{Acceptor, ServerConfig, Session, TlsStream};
use actix_tls::SslError;
use std::{fmt, io};
impl<S, B> H2Service<TlsStream<TcpStream>, S, B>
where
S: ServiceFactory<Config = (), Request = Request>,
S::Error: Into<Error> + 'static,
S::Response: Into<Response<B>> + 'static,
<S::Service as Service>::Future: 'static,
B: MessageBody + 'static,
{
/// Create openssl based service
pub fn rustls(
self,
mut config: ServerConfig,
) -> impl ServiceFactory<
Config = (),
Request = TcpStream,
Response = (),
Error = SslError<io::Error, DispatchError>,
InitError = S::InitError,
> {
let protos = vec!["h2".to_string().into()];
config.set_protocols(&protos);
pipeline_factory(
Acceptor::new(config)
.map_err(SslError::Ssl)
.map_init_err(|_| panic!()),
)
.and_then(factory_fn(|| {
ok::<_, S::InitError>(service_fn2(|io: TlsStream<TcpStream>| {
let peer_addr = io.get_ref().0.peer_addr().ok();
ok((io, peer_addr))
}))
}))
.and_then(self.map_err(SslError::Service))
}
}
}
impl<T, S, B> ServiceFactory for H2Service<T, S, B>
where
T: AsyncRead + AsyncWrite + Unpin,
S: ServiceFactory<Config = (), Request = Request>,
S::Error: Into<Error> + 'static,
S::Response: Into<Response<B>> + 'static,
<S::Service as Service>::Future: 'static,
B: MessageBody + 'static,
{
type Config = ();
type Request = (T, Option<net::SocketAddr>);
type Response = (); type Response = ();
type Error = DispatchError; type Error = DispatchError;
type InitError = S::InitError; type InitError = S::InitError;
type Service = H2ServiceHandler<T, P, S::Service, B>; type Service = H2ServiceHandler<T, S::Service, B>;
type Future = H2ServiceResponse<T, P, S, B>; type Future = H2ServiceResponse<T, S, B>;
fn new_service(&self, cfg: &SrvConfig) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
H2ServiceResponse { H2ServiceResponse {
fut: self.srv.new_service(cfg), fut: self.srv.new_service(()),
cfg: Some(self.cfg.clone()), cfg: Some(self.cfg.clone()),
on_connect: self.on_connect.clone(), on_connect: self.on_connect.clone(),
_t: PhantomData, _t: PhantomData,
@ -106,24 +216,24 @@ where
#[doc(hidden)] #[doc(hidden)]
#[pin_project::pin_project] #[pin_project::pin_project]
pub struct H2ServiceResponse<T, P, S: ServiceFactory, B> { pub struct H2ServiceResponse<T, S: ServiceFactory, B> {
#[pin] #[pin]
fut: S::Future, fut: S::Future,
cfg: Option<ServiceConfig>, cfg: Option<ServiceConfig>,
on_connect: Option<rc::Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>, on_connect: Option<rc::Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>,
_t: PhantomData<(T, P, B)>, _t: PhantomData<(T, B)>,
} }
impl<T, P, S, B> Future for H2ServiceResponse<T, P, S, B> impl<T, S, B> Future for H2ServiceResponse<T, S, B>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
S: ServiceFactory<Config = SrvConfig, Request = Request>, S: ServiceFactory<Config = (), Request = Request>,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::Response: Into<Response<B>> + 'static, S::Response: Into<Response<B>> + 'static,
<S::Service as Service>::Future: 'static, <S::Service as Service>::Future: 'static,
B: MessageBody + 'static, B: MessageBody + 'static,
{ {
type Output = Result<H2ServiceHandler<T, P, S::Service, B>, S::InitError>; type Output = Result<H2ServiceHandler<T, S::Service, B>, S::InitError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let this = self.as_mut().project(); let this = self.as_mut().project();
@ -140,14 +250,14 @@ where
} }
/// `Service` implementation for http/2 transport /// `Service` implementation for http/2 transport
pub struct H2ServiceHandler<T, P, S, B> { pub struct H2ServiceHandler<T, S, B> {
srv: CloneableService<S>, srv: CloneableService<S>,
cfg: ServiceConfig, cfg: ServiceConfig,
on_connect: Option<rc::Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>, on_connect: Option<rc::Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>,
_t: PhantomData<(T, P, B)>, _t: PhantomData<(T, B)>,
} }
impl<T, P, S, B> H2ServiceHandler<T, P, S, B> impl<T, S, B> H2ServiceHandler<T, S, B>
where where
S: Service<Request = Request>, S: Service<Request = Request>,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
@ -159,7 +269,7 @@ where
cfg: ServiceConfig, cfg: ServiceConfig,
on_connect: Option<rc::Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>, on_connect: Option<rc::Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>,
srv: S, srv: S,
) -> H2ServiceHandler<T, P, S, B> { ) -> H2ServiceHandler<T, S, B> {
H2ServiceHandler { H2ServiceHandler {
cfg, cfg,
on_connect, on_connect,
@ -169,16 +279,16 @@ where
} }
} }
impl<T, P, S, B> Service for H2ServiceHandler<T, P, S, B> impl<T, S, B> Service for H2ServiceHandler<T, S, B>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
S: Service<Request = Request>, S: Service<Request = Request>,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::Future: 'static, S::Future: 'static,
S::Response: Into<Response<B>> + 'static, S::Response: Into<Response<B>> + 'static,
B: MessageBody + 'static, B: MessageBody + 'static,
{ {
type Request = Io<T, P>; type Request = (T, Option<net::SocketAddr>);
type Response = (); type Response = ();
type Error = DispatchError; type Error = DispatchError;
type Future = H2ServiceHandlerResponse<T, S, B>; type Future = H2ServiceHandlerResponse<T, S, B>;
@ -191,9 +301,7 @@ where
}) })
} }
fn call(&mut self, req: Self::Request) -> Self::Future { fn call(&mut self, (io, addr): Self::Request) -> Self::Future {
let io = req.into_parts().0;
let peer_addr = io.peer_addr();
let on_connect = if let Some(ref on_connect) = self.on_connect { let on_connect = if let Some(ref on_connect) = self.on_connect {
Some(on_connect(&io)) Some(on_connect(&io))
} else { } else {
@ -204,7 +312,7 @@ where
state: State::Handshake( state: State::Handshake(
Some(self.srv.clone()), Some(self.srv.clone()),
Some(self.cfg.clone()), Some(self.cfg.clone()),
peer_addr, addr,
on_connect, on_connect,
server::handshake(io), server::handshake(io),
), ),
@ -212,8 +320,9 @@ where
} }
} }
enum State<T: IoStream, S: Service<Request = Request>, B: MessageBody> enum State<T, S: Service<Request = Request>, B: MessageBody>
where where
T: AsyncRead + AsyncWrite + Unpin,
S::Future: 'static, S::Future: 'static,
{ {
Incoming(Dispatcher<T, S, B>), Incoming(Dispatcher<T, S, B>),
@ -228,7 +337,7 @@ where
pub struct H2ServiceHandlerResponse<T, S, B> pub struct H2ServiceHandlerResponse<T, S, B>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
S: Service<Request = Request>, S: Service<Request = Request>,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::Future: 'static, S::Future: 'static,
@ -240,7 +349,7 @@ where
impl<T, S, B> Future for H2ServiceHandlerResponse<T, S, B> impl<T, S, B> Future for H2ServiceHandlerResponse<T, S, B>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
S: Service<Request = Request>, S: Service<Request = Request>,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::Future: 'static, S::Future: 'static,

View File

@ -80,12 +80,12 @@ impl fmt::Display for CacheControl {
} }
impl IntoHeaderValue for CacheControl { impl IntoHeaderValue for CacheControl {
type Error = header::InvalidHeaderValueBytes; type Error = header::InvalidHeaderValue;
fn try_into(self) -> Result<header::HeaderValue, Self::Error> { fn try_into(self) -> Result<header::HeaderValue, Self::Error> {
let mut writer = Writer::new(); let mut writer = Writer::new();
let _ = write!(&mut writer, "{}", self); let _ = write!(&mut writer, "{}", self);
header::HeaderValue::from_shared(writer.take()) header::HeaderValue::from_maybe_shared(writer.take())
} }
} }

View File

@ -462,12 +462,12 @@ impl ContentDisposition {
} }
impl IntoHeaderValue for ContentDisposition { impl IntoHeaderValue for ContentDisposition {
type Error = header::InvalidHeaderValueBytes; type Error = header::InvalidHeaderValue;
fn try_into(self) -> Result<header::HeaderValue, Self::Error> { fn try_into(self) -> Result<header::HeaderValue, Self::Error> {
let mut writer = Writer::new(); let mut writer = Writer::new();
let _ = write!(&mut writer, "{}", self); let _ = write!(&mut writer, "{}", self);
header::HeaderValue::from_shared(writer.take()) header::HeaderValue::from_maybe_shared(writer.take())
} }
} }
@ -768,9 +768,8 @@ mod tests {
Mainstream browsers like Firefox (gecko) and Chrome use UTF-8 directly as above. Mainstream browsers like Firefox (gecko) and Chrome use UTF-8 directly as above.
(And now, only UTF-8 is handled by this implementation.) (And now, only UTF-8 is handled by this implementation.)
*/ */
let a = let a = HeaderValue::from_str("form-data; name=upload; filename=\"文件.webp\"")
HeaderValue::from_str("form-data; name=upload; filename=\"文件.webp\"") .unwrap();
.unwrap();
let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap(); let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();
let b = ContentDisposition { let b = ContentDisposition {
disposition: DispositionType::FormData, disposition: DispositionType::FormData,
@ -884,7 +883,11 @@ mod tests {
assert!(ContentDisposition::from_raw(&a).is_err()); assert!(ContentDisposition::from_raw(&a).is_err());
let a = HeaderValue::from_static("inline; filename=\"\""); let a = HeaderValue::from_static("inline; filename=\"\"");
assert!(ContentDisposition::from_raw(&a).expect("parse cd").get_filename().expect("filename").is_empty()); assert!(ContentDisposition::from_raw(&a)
.expect("parse cd")
.get_filename()
.expect("filename")
.is_empty());
} }
#[test] #[test]

View File

@ -3,7 +3,7 @@ use std::str::FromStr;
use crate::error::ParseError; use crate::error::ParseError;
use crate::header::{ use crate::header::{
HeaderValue, IntoHeaderValue, InvalidHeaderValueBytes, Writer, CONTENT_RANGE, HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer, CONTENT_RANGE,
}; };
header! { header! {
@ -198,11 +198,11 @@ impl Display for ContentRangeSpec {
} }
impl IntoHeaderValue for ContentRangeSpec { impl IntoHeaderValue for ContentRangeSpec {
type Error = InvalidHeaderValueBytes; type Error = InvalidHeaderValue;
fn try_into(self) -> Result<HeaderValue, Self::Error> { fn try_into(self) -> Result<HeaderValue, Self::Error> {
let mut writer = Writer::new(); let mut writer = Writer::new();
let _ = write!(&mut writer, "{}", self); let _ = write!(&mut writer, "{}", self);
HeaderValue::from_shared(writer.take()) HeaderValue::from_maybe_shared(writer.take())
} }
} }

View File

@ -3,7 +3,7 @@ use std::fmt::{self, Display, Write};
use crate::error::ParseError; use crate::error::ParseError;
use crate::header::{ use crate::header::{
self, from_one_raw_str, EntityTag, Header, HeaderName, HeaderValue, HttpDate, self, from_one_raw_str, EntityTag, Header, HeaderName, HeaderValue, HttpDate,
IntoHeaderValue, InvalidHeaderValueBytes, Writer, IntoHeaderValue, InvalidHeaderValue, Writer,
}; };
use crate::httpmessage::HttpMessage; use crate::httpmessage::HttpMessage;
@ -96,12 +96,12 @@ impl Display for IfRange {
} }
impl IntoHeaderValue for IfRange { impl IntoHeaderValue for IfRange {
type Error = InvalidHeaderValueBytes; type Error = InvalidHeaderValue;
fn try_into(self) -> Result<HeaderValue, Self::Error> { fn try_into(self) -> Result<HeaderValue, Self::Error> {
let mut writer = Writer::new(); let mut writer = Writer::new();
let _ = write!(&mut writer, "{}", self); let _ = write!(&mut writer, "{}", self);
HeaderValue::from_shared(writer.take()) HeaderValue::from_maybe_shared(writer.take())
} }
} }

View File

@ -164,13 +164,13 @@ macro_rules! header {
} }
} }
impl $crate::http::header::IntoHeaderValue for $id { impl $crate::http::header::IntoHeaderValue for $id {
type Error = $crate::http::header::InvalidHeaderValueBytes; type Error = $crate::http::header::InvalidHeaderValue;
fn try_into(self) -> Result<$crate::http::header::HeaderValue, Self::Error> { fn try_into(self) -> Result<$crate::http::header::HeaderValue, Self::Error> {
use std::fmt::Write; use std::fmt::Write;
let mut writer = $crate::http::header::Writer::new(); let mut writer = $crate::http::header::Writer::new();
let _ = write!(&mut writer, "{}", self); let _ = write!(&mut writer, "{}", self);
$crate::http::header::HeaderValue::from_shared(writer.take()) $crate::http::header::HeaderValue::from_maybe_shared(writer.take())
} }
} }
}; };
@ -200,13 +200,13 @@ macro_rules! header {
} }
} }
impl $crate::http::header::IntoHeaderValue for $id { impl $crate::http::header::IntoHeaderValue for $id {
type Error = $crate::http::header::InvalidHeaderValueBytes; type Error = $crate::http::header::InvalidHeaderValue;
fn try_into(self) -> Result<$crate::http::header::HeaderValue, Self::Error> { fn try_into(self) -> Result<$crate::http::header::HeaderValue, Self::Error> {
use std::fmt::Write; use std::fmt::Write;
let mut writer = $crate::http::header::Writer::new(); let mut writer = $crate::http::header::Writer::new();
let _ = write!(&mut writer, "{}", self); let _ = write!(&mut writer, "{}", self);
$crate::http::header::HeaderValue::from_shared(writer.take()) $crate::http::header::HeaderValue::from_maybe_shared(writer.take())
} }
} }
}; };
@ -236,7 +236,7 @@ macro_rules! header {
} }
} }
impl $crate::http::header::IntoHeaderValue for $id { impl $crate::http::header::IntoHeaderValue for $id {
type Error = $crate::http::header::InvalidHeaderValueBytes; type Error = $crate::http::header::InvalidHeaderValue;
fn try_into(self) -> Result<$crate::http::header::HeaderValue, Self::Error> { fn try_into(self) -> Result<$crate::http::header::HeaderValue, Self::Error> {
self.0.try_into() self.0.try_into()
@ -285,13 +285,13 @@ macro_rules! header {
} }
} }
impl $crate::http::header::IntoHeaderValue for $id { impl $crate::http::header::IntoHeaderValue for $id {
type Error = $crate::http::header::InvalidHeaderValueBytes; type Error = $crate::http::header::InvalidHeaderValue;
fn try_into(self) -> Result<$crate::http::header::HeaderValue, Self::Error> { fn try_into(self) -> Result<$crate::http::header::HeaderValue, Self::Error> {
use std::fmt::Write; use std::fmt::Write;
let mut writer = $crate::http::header::Writer::new(); let mut writer = $crate::http::header::Writer::new();
let _ = write!(&mut writer, "{}", self); let _ = write!(&mut writer, "{}", self);
$crate::http::header::HeaderValue::from_shared(writer.take()) $crate::http::header::HeaderValue::from_maybe_shared(writer.take())
} }
} }
}; };

View File

@ -1,8 +1,9 @@
use std::collections::hash_map::{self, Entry};
use std::convert::TryFrom;
use either::Either; use either::Either;
use hashbrown::hash_map::{self, Entry}; use fxhash::FxHashMap;
use hashbrown::HashMap;
use http::header::{HeaderName, HeaderValue}; use http::header::{HeaderName, HeaderValue};
use http::HttpTryFrom;
/// A set of HTTP headers /// A set of HTTP headers
/// ///
@ -11,7 +12,7 @@ use http::HttpTryFrom;
/// [`HeaderName`]: struct.HeaderName.html /// [`HeaderName`]: struct.HeaderName.html
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct HeaderMap { pub struct HeaderMap {
pub(crate) inner: HashMap<HeaderName, Value>, pub(crate) inner: FxHashMap<HeaderName, Value>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -56,7 +57,7 @@ impl HeaderMap {
/// allocate. /// allocate.
pub fn new() -> Self { pub fn new() -> Self {
HeaderMap { HeaderMap {
inner: HashMap::new(), inner: FxHashMap::default(),
} }
} }
@ -70,7 +71,7 @@ impl HeaderMap {
/// More capacity than requested may be allocated. /// More capacity than requested may be allocated.
pub fn with_capacity(capacity: usize) -> HeaderMap { pub fn with_capacity(capacity: usize) -> HeaderMap {
HeaderMap { HeaderMap {
inner: HashMap::with_capacity(capacity), inner: FxHashMap::with_capacity_and_hasher(capacity, Default::default()),
} }
} }

View File

@ -1,6 +1,7 @@
//! Various http headers //! Various http headers
// This is mostly copy of [hyper](https://github.com/hyperium/hyper/tree/master/src/header) // This is mostly copy of [hyper](https://github.com/hyperium/hyper/tree/master/src/header)
use std::convert::TryFrom;
use std::{fmt, str::FromStr}; use std::{fmt, str::FromStr};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
@ -73,58 +74,58 @@ impl<'a> IntoHeaderValue for &'a [u8] {
} }
impl IntoHeaderValue for Bytes { impl IntoHeaderValue for Bytes {
type Error = InvalidHeaderValueBytes; type Error = InvalidHeaderValue;
#[inline] #[inline]
fn try_into(self) -> Result<HeaderValue, Self::Error> { fn try_into(self) -> Result<HeaderValue, Self::Error> {
HeaderValue::from_shared(self) HeaderValue::from_maybe_shared(self)
} }
} }
impl IntoHeaderValue for Vec<u8> { impl IntoHeaderValue for Vec<u8> {
type Error = InvalidHeaderValueBytes; type Error = InvalidHeaderValue;
#[inline] #[inline]
fn try_into(self) -> Result<HeaderValue, Self::Error> { fn try_into(self) -> Result<HeaderValue, Self::Error> {
HeaderValue::from_shared(Bytes::from(self)) HeaderValue::try_from(self)
} }
} }
impl IntoHeaderValue for String { impl IntoHeaderValue for String {
type Error = InvalidHeaderValueBytes; type Error = InvalidHeaderValue;
#[inline] #[inline]
fn try_into(self) -> Result<HeaderValue, Self::Error> { fn try_into(self) -> Result<HeaderValue, Self::Error> {
HeaderValue::from_shared(Bytes::from(self)) HeaderValue::try_from(self)
} }
} }
impl IntoHeaderValue for usize { impl IntoHeaderValue for usize {
type Error = InvalidHeaderValueBytes; type Error = InvalidHeaderValue;
#[inline] #[inline]
fn try_into(self) -> Result<HeaderValue, Self::Error> { fn try_into(self) -> Result<HeaderValue, Self::Error> {
let s = format!("{}", self); let s = format!("{}", self);
HeaderValue::from_shared(Bytes::from(s)) HeaderValue::try_from(s)
} }
} }
impl IntoHeaderValue for u64 { impl IntoHeaderValue for u64 {
type Error = InvalidHeaderValueBytes; type Error = InvalidHeaderValue;
#[inline] #[inline]
fn try_into(self) -> Result<HeaderValue, Self::Error> { fn try_into(self) -> Result<HeaderValue, Self::Error> {
let s = format!("{}", self); let s = format!("{}", self);
HeaderValue::from_shared(Bytes::from(s)) HeaderValue::try_from(s)
} }
} }
impl IntoHeaderValue for Mime { impl IntoHeaderValue for Mime {
type Error = InvalidHeaderValueBytes; type Error = InvalidHeaderValue;
#[inline] #[inline]
fn try_into(self) -> Result<HeaderValue, Self::Error> { fn try_into(self) -> Result<HeaderValue, Self::Error> {
HeaderValue::from_shared(Bytes::from(format!("{}", self))) HeaderValue::try_from(format!("{}", self))
} }
} }
@ -204,7 +205,7 @@ impl Writer {
} }
} }
fn take(&mut self) -> Bytes { fn take(&mut self) -> Bytes {
self.buf.take().freeze() self.buf.split().freeze()
} }
} }

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, InvalidHeaderValueBytes, Writer}; use crate::header::{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
@ -157,12 +157,12 @@ impl FromStr for EntityTag {
} }
impl IntoHeaderValue for EntityTag { impl IntoHeaderValue for EntityTag {
type Error = InvalidHeaderValueBytes; type Error = InvalidHeaderValue;
fn try_into(self) -> Result<HeaderValue, Self::Error> { fn try_into(self) -> Result<HeaderValue, Self::Error> {
let mut wrt = Writer::new(); let mut wrt = Writer::new();
write!(wrt, "{}", self).unwrap(); write!(wrt, "{}", self).unwrap();
HeaderValue::from_shared(wrt.take()) HeaderValue::from_maybe_shared(wrt.take())
} }
} }

View File

@ -3,8 +3,8 @@ use std::io::Write;
use std::str::FromStr; use std::str::FromStr;
use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::time::{Duration, SystemTime, UNIX_EPOCH};
use bytes::{BufMut, BytesMut}; use bytes::{buf::BufMutExt, BytesMut};
use http::header::{HeaderValue, InvalidHeaderValueBytes}; use http::header::{HeaderValue, InvalidHeaderValue};
use crate::error::ParseError; use crate::error::ParseError;
use crate::header::IntoHeaderValue; use crate::header::IntoHeaderValue;
@ -58,12 +58,12 @@ impl From<SystemTime> for HttpDate {
} }
impl IntoHeaderValue for HttpDate { impl IntoHeaderValue for HttpDate {
type Error = InvalidHeaderValueBytes; type Error = InvalidHeaderValue;
fn try_into(self) -> Result<HeaderValue, Self::Error> { fn try_into(self) -> Result<HeaderValue, Self::Error> {
let mut wrt = BytesMut::with_capacity(29).writer(); let mut wrt = BytesMut::with_capacity(29).writer();
write!(wrt, "{}", self.0.rfc822()).unwrap(); write!(wrt, "{}", self.0.rfc822()).unwrap();
HeaderValue::from_shared(wrt.get_mut().take().freeze()) HeaderValue::from_maybe_shared(wrt.get_mut().split().freeze())
} }
} }

View File

@ -60,7 +60,7 @@ pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesM
bytes.put_slice(&buf); bytes.put_slice(&buf);
if four { if four {
bytes.put(b' '); bytes.put_u8(b' ');
} }
} }
@ -203,33 +203,33 @@ mod tests {
let mut bytes = BytesMut::new(); let mut bytes = BytesMut::new();
bytes.reserve(50); bytes.reserve(50);
write_content_length(0, &mut bytes); write_content_length(0, &mut bytes);
assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 0\r\n"[..]); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 0\r\n"[..]);
bytes.reserve(50); bytes.reserve(50);
write_content_length(9, &mut bytes); write_content_length(9, &mut bytes);
assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 9\r\n"[..]); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 9\r\n"[..]);
bytes.reserve(50); bytes.reserve(50);
write_content_length(10, &mut bytes); write_content_length(10, &mut bytes);
assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 10\r\n"[..]); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 10\r\n"[..]);
bytes.reserve(50); bytes.reserve(50);
write_content_length(99, &mut bytes); write_content_length(99, &mut bytes);
assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 99\r\n"[..]); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 99\r\n"[..]);
bytes.reserve(50); bytes.reserve(50);
write_content_length(100, &mut bytes); write_content_length(100, &mut bytes);
assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 100\r\n"[..]); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 100\r\n"[..]);
bytes.reserve(50); bytes.reserve(50);
write_content_length(101, &mut bytes); write_content_length(101, &mut bytes);
assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 101\r\n"[..]); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 101\r\n"[..]);
bytes.reserve(50); bytes.reserve(50);
write_content_length(998, &mut bytes); write_content_length(998, &mut bytes);
assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 998\r\n"[..]); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 998\r\n"[..]);
bytes.reserve(50); bytes.reserve(50);
write_content_length(1000, &mut bytes); write_content_length(1000, &mut bytes);
assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 1000\r\n"[..]); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 1000\r\n"[..]);
bytes.reserve(50); bytes.reserve(50);
write_content_length(1001, &mut bytes); write_content_length(1001, &mut bytes);
assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 1001\r\n"[..]); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 1001\r\n"[..]);
bytes.reserve(50); bytes.reserve(50);
write_content_length(5909, &mut bytes); write_content_length(5909, &mut bytes);
assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 5909\r\n"[..]); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 5909\r\n"[..]);
} }
} }

View File

@ -51,7 +51,7 @@ pub mod http {
// re-exports // re-exports
pub use http::header::{HeaderName, HeaderValue}; pub use http::header::{HeaderName, HeaderValue};
pub use http::uri::PathAndQuery; pub use http::uri::PathAndQuery;
pub use http::{uri, Error, HttpTryFrom, Uri}; pub use http::{uri, Error, Uri};
pub use http::{Method, StatusCode, Version}; pub use http::{Method, StatusCode, Version};
pub use crate::cookie::{Cookie, CookieBuilder}; pub use crate::cookie::{Cookie, CookieBuilder};
@ -64,3 +64,10 @@ pub mod http {
pub use crate::header::ContentEncoding; pub use crate::header::ContentEncoding;
pub use crate::message::ConnectionType; pub use crate::message::ConnectionType;
} }
/// Http protocol
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Protocol {
Http1,
Http2,
}

View File

@ -187,7 +187,7 @@ impl<P> fmt::Debug for Request<P> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use http::HttpTryFrom; use std::convert::TryFrom;
#[test] #[test]
fn test_basics() { fn test_basics() {

View File

@ -1,12 +1,12 @@
//! Http response //! Http response
use std::cell::{Ref, RefMut}; use std::cell::{Ref, RefMut};
use std::convert::TryFrom;
use std::future::Future; use std::future::Future;
use std::io::Write;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::{fmt, str}; use std::{fmt, str};
use bytes::{BufMut, Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures::stream::Stream; use futures::stream::Stream;
use serde::Serialize; use serde::Serialize;
use serde_json; use serde_json;
@ -17,7 +17,7 @@ use crate::error::Error;
use crate::extensions::Extensions; use crate::extensions::Extensions;
use crate::header::{Header, IntoHeaderValue}; use crate::header::{Header, IntoHeaderValue};
use crate::http::header::{self, HeaderName, HeaderValue}; use crate::http::header::{self, HeaderName, HeaderValue};
use crate::http::{Error as HttpError, HeaderMap, HttpTryFrom, StatusCode}; use crate::http::{Error as HttpError, HeaderMap, StatusCode};
use crate::message::{BoxedResponseHead, ConnectionType, ResponseHead}; use crate::message::{BoxedResponseHead, ConnectionType, ResponseHead};
/// An HTTP Response /// An HTTP Response
@ -384,7 +384,8 @@ impl ResponseBuilder {
/// ``` /// ```
pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
if let Some(parts) = parts(&mut self.head, &self.err) { if let Some(parts) = parts(&mut self.head, &self.err) {
@ -416,7 +417,8 @@ impl ResponseBuilder {
/// ``` /// ```
pub fn set_header<K, V>(&mut self, key: K, value: V) -> &mut Self pub fn set_header<K, V>(&mut self, key: K, value: V) -> &mut Self
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
if let Some(parts) = parts(&mut self.head, &self.err) { if let Some(parts) = parts(&mut self.head, &self.err) {
@ -485,7 +487,8 @@ impl ResponseBuilder {
#[inline] #[inline]
pub fn content_type<V>(&mut self, value: V) -> &mut Self pub fn content_type<V>(&mut self, value: V) -> &mut Self
where where
HeaderValue: HttpTryFrom<V>, HeaderValue: TryFrom<V>,
<HeaderValue as TryFrom<V>>::Error: Into<HttpError>,
{ {
if let Some(parts) = parts(&mut self.head, &self.err) { if let Some(parts) = parts(&mut self.head, &self.err) {
match HeaderValue::try_from(value) { match HeaderValue::try_from(value) {
@ -501,9 +504,7 @@ impl ResponseBuilder {
/// Set content length /// Set content length
#[inline] #[inline]
pub fn content_length(&mut self, len: u64) -> &mut Self { pub fn content_length(&mut self, len: u64) -> &mut Self {
let mut wrt = BytesMut::new().writer(); self.header(header::CONTENT_LENGTH, len)
let _ = write!(wrt, "{}", len);
self.header(header::CONTENT_LENGTH, wrt.get_mut().take().freeze())
} }
/// Set a cookie /// Set a cookie

View File

@ -1,15 +1,13 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::{fmt, io, net, rc}; use std::{fmt, net, rc};
use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_server_config::{ use actix_rt::net::TcpStream;
Io as ServerIo, IoStream, Protocol, ServerConfig as SrvConfig, use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory};
}; use bytes::Bytes;
use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use futures::{future::ok, ready, Future};
use bytes::{BufMut, Bytes, BytesMut};
use futures::{ready, Future};
use h2::server::{self, Handshake}; use h2::server::{self, Handshake};
use pin_project::{pin_project, project}; use pin_project::{pin_project, project};
@ -21,21 +19,21 @@ use crate::error::{DispatchError, Error};
use crate::helpers::DataFactory; use crate::helpers::DataFactory;
use crate::request::Request; use crate::request::Request;
use crate::response::Response; use crate::response::Response;
use crate::{h1, h2::Dispatcher}; use crate::{h1, h2::Dispatcher, Protocol};
/// `ServiceFactory` HTTP1.1/HTTP2 transport implementation /// `ServiceFactory` HTTP1.1/HTTP2 transport implementation
pub struct HttpService<T, P, S, B, X = h1::ExpectHandler, U = h1::UpgradeHandler<T>> { pub struct HttpService<T, S, B, X = h1::ExpectHandler, U = h1::UpgradeHandler<T>> {
srv: S, srv: S,
cfg: ServiceConfig, cfg: ServiceConfig,
expect: X, expect: X,
upgrade: Option<U>, upgrade: Option<U>,
on_connect: Option<rc::Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>, on_connect: Option<rc::Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>,
_t: PhantomData<(T, P, B)>, _t: PhantomData<(T, B)>,
} }
impl<T, S, B> HttpService<T, (), S, B> impl<T, S, B> HttpService<T, S, B>
where where
S: ServiceFactory<Config = SrvConfig, Request = Request>, S: ServiceFactory<Config = (), Request = Request>,
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,
@ -48,9 +46,9 @@ where
} }
} }
impl<T, P, S, B> HttpService<T, P, S, B> impl<T, S, B> HttpService<T, S, B>
where where
S: ServiceFactory<Config = SrvConfig, Request = Request>, S: ServiceFactory<Config = (), Request = Request>,
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,
@ -59,7 +57,7 @@ where
{ {
/// Create new `HttpService` instance. /// Create new `HttpService` instance.
pub fn new<F: IntoServiceFactory<S>>(service: F) -> Self { pub fn new<F: IntoServiceFactory<S>>(service: F) -> Self {
let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0); let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0, false, None);
HttpService { HttpService {
cfg, cfg,
@ -87,9 +85,9 @@ where
} }
} }
impl<T, P, S, B, X, U> HttpService<T, P, S, B, X, U> impl<T, S, B, X, U> HttpService<T, S, B, X, U>
where where
S: ServiceFactory<Config = SrvConfig, Request = Request>, S: ServiceFactory<Config = (), Request = Request>,
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,
@ -101,9 +99,9 @@ where
/// Service get called with request that contains `EXPECT` header. /// Service get called with request that contains `EXPECT` header.
/// Service must return request in case of success, in that case /// Service must return request in case of success, in that case
/// request will be forwarded to main service. /// request will be forwarded to main service.
pub fn expect<X1>(self, expect: X1) -> HttpService<T, P, S, B, X1, U> pub fn expect<X1>(self, expect: X1) -> HttpService<T, S, B, X1, U>
where where
X1: ServiceFactory<Config = SrvConfig, Request = Request, Response = Request>, X1: ServiceFactory<Config = (), Request = Request, Response = Request>,
X1::Error: Into<Error>, X1::Error: Into<Error>,
X1::InitError: fmt::Debug, X1::InitError: fmt::Debug,
<X1::Service as Service>::Future: 'static, <X1::Service as Service>::Future: 'static,
@ -122,10 +120,10 @@ where
/// ///
/// If service is provided then normal requests handling get halted /// If service is provided then normal requests handling get halted
/// and this service get called with original request and framed object. /// and this service get called with original request and framed object.
pub fn upgrade<U1>(self, upgrade: Option<U1>) -> HttpService<T, P, S, B, X, U1> pub fn upgrade<U1>(self, upgrade: Option<U1>) -> HttpService<T, S, B, X, U1>
where where
U1: ServiceFactory< U1: ServiceFactory<
Config = SrvConfig, Config = (),
Request = (Request, Framed<T, h1::Codec>), Request = (Request, Framed<T, h1::Codec>),
Response = (), Response = (),
>, >,
@ -153,21 +151,186 @@ where
} }
} }
impl<T, P, S, B, X, U> ServiceFactory for HttpService<T, P, S, B, X, U> impl<S, B, X, U> HttpService<TcpStream, S, B, X, U>
where where
T: IoStream, S: ServiceFactory<Config = (), Request = Request>,
S: ServiceFactory<Config = SrvConfig, Request = Request>,
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>::Future: 'static, <S::Service as Service>::Future: 'static,
B: MessageBody + 'static, B: MessageBody + 'static,
X: ServiceFactory<Config = SrvConfig, Request = Request, Response = Request>, X: ServiceFactory<Config = (), Request = Request, Response = Request>,
X::Error: Into<Error>, X::Error: Into<Error>,
X::InitError: fmt::Debug, X::InitError: fmt::Debug,
<X::Service as Service>::Future: 'static, <X::Service as Service>::Future: 'static,
U: ServiceFactory< U: ServiceFactory<
Config = SrvConfig, Config = (),
Request = (Request, Framed<TcpStream, h1::Codec>),
Response = (),
>,
U::Error: fmt::Display,
U::InitError: fmt::Debug,
<U::Service as Service>::Future: 'static,
{
/// Create simple tcp stream service
pub fn tcp(
self,
) -> impl ServiceFactory<
Config = (),
Request = TcpStream,
Response = (),
Error = DispatchError,
InitError = (),
> {
pipeline_factory(|io: TcpStream| {
let peer_addr = io.peer_addr().ok();
ok((io, Protocol::Http1, peer_addr))
})
.and_then(self)
}
}
#[cfg(feature = "openssl")]
mod openssl {
use super::*;
use actix_tls::openssl::{Acceptor, SslAcceptor, SslStream};
use actix_tls::{openssl::HandshakeError, SslError};
impl<S, B, X, U> HttpService<SslStream<TcpStream>, S, B, X, U>
where
S: ServiceFactory<Config = (), Request = Request>,
S::Error: Into<Error> + 'static,
S::InitError: fmt::Debug,
S::Response: Into<Response<B>> + 'static,
<S::Service as Service>::Future: 'static,
B: MessageBody + 'static,
X: ServiceFactory<Config = (), Request = Request, Response = Request>,
X::Error: Into<Error>,
X::InitError: fmt::Debug,
<X::Service as Service>::Future: 'static,
U: ServiceFactory<
Config = (),
Request = (Request, Framed<SslStream<TcpStream>, h1::Codec>),
Response = (),
>,
U::Error: fmt::Display,
U::InitError: fmt::Debug,
<U::Service as Service>::Future: 'static,
{
/// Create openssl based service
pub fn openssl(
self,
acceptor: SslAcceptor,
) -> impl ServiceFactory<
Config = (),
Request = TcpStream,
Response = (),
Error = SslError<HandshakeError<TcpStream>, DispatchError>,
InitError = (),
> {
pipeline_factory(
Acceptor::new(acceptor)
.map_err(SslError::Ssl)
.map_init_err(|_| panic!()),
)
.and_then(|io: SslStream<TcpStream>| {
let proto = if let Some(protos) = io.ssl().selected_alpn_protocol() {
if protos.windows(2).any(|window| window == b"h2") {
Protocol::Http2
} else {
Protocol::Http1
}
} else {
Protocol::Http1
};
let peer_addr = io.get_ref().peer_addr().ok();
ok((io, proto, peer_addr))
})
.and_then(self.map_err(SslError::Service))
}
}
}
#[cfg(feature = "rustls")]
mod rustls {
use super::*;
use actix_tls::rustls::{Acceptor, ServerConfig, Session, TlsStream};
use actix_tls::SslError;
use std::io;
impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>
where
S: ServiceFactory<Config = (), Request = Request>,
S::Error: Into<Error> + 'static,
S::InitError: fmt::Debug,
S::Response: Into<Response<B>> + 'static,
<S::Service as Service>::Future: 'static,
B: MessageBody + 'static,
X: ServiceFactory<Config = (), Request = Request, Response = Request>,
X::Error: Into<Error>,
X::InitError: fmt::Debug,
<X::Service as Service>::Future: 'static,
U: ServiceFactory<
Config = (),
Request = (Request, Framed<TlsStream<TcpStream>, h1::Codec>),
Response = (),
>,
U::Error: fmt::Display,
U::InitError: fmt::Debug,
<U::Service as Service>::Future: 'static,
{
/// Create openssl based service
pub fn rustls(
self,
mut config: ServerConfig,
) -> impl ServiceFactory<
Config = (),
Request = TcpStream,
Response = (),
Error = SslError<io::Error, DispatchError>,
InitError = (),
> {
let protos = vec!["h2".to_string().into(), "http/1.1".to_string().into()];
config.set_protocols(&protos);
pipeline_factory(
Acceptor::new(config)
.map_err(SslError::Ssl)
.map_init_err(|_| panic!()),
)
.and_then(|io: TlsStream<TcpStream>| {
let proto = if let Some(protos) = io.get_ref().1.get_alpn_protocol() {
if protos.windows(2).any(|window| window == b"h2") {
Protocol::Http2
} else {
Protocol::Http1
}
} else {
Protocol::Http1
};
let peer_addr = io.get_ref().0.peer_addr().ok();
ok((io, proto, peer_addr))
})
.and_then(self.map_err(SslError::Service))
}
}
}
impl<T, S, B, X, U> ServiceFactory for HttpService<T, S, B, X, U>
where
T: AsyncRead + AsyncWrite + Unpin,
S: ServiceFactory<Config = (), Request = Request>,
S::Error: Into<Error> + 'static,
S::InitError: fmt::Debug,
S::Response: Into<Response<B>> + 'static,
<S::Service as Service>::Future: 'static,
B: MessageBody + 'static,
X: ServiceFactory<Config = (), Request = Request, Response = Request>,
X::Error: Into<Error>,
X::InitError: fmt::Debug,
<X::Service as Service>::Future: 'static,
U: ServiceFactory<
Config = (),
Request = (Request, Framed<T, h1::Codec>), Request = (Request, Framed<T, h1::Codec>),
Response = (), Response = (),
>, >,
@ -175,23 +338,23 @@ where
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
<U::Service as Service>::Future: 'static, <U::Service as Service>::Future: 'static,
{ {
type Config = SrvConfig; type Config = ();
type Request = ServerIo<T, P>; type Request = (T, Protocol, Option<net::SocketAddr>);
type Response = (); type Response = ();
type Error = DispatchError; type Error = DispatchError;
type InitError = (); type InitError = ();
type Service = HttpServiceHandler<T, P, S::Service, B, X::Service, U::Service>; type Service = HttpServiceHandler<T, S::Service, B, X::Service, U::Service>;
type Future = HttpServiceResponse<T, P, S, B, X, U>; type Future = HttpServiceResponse<T, S, B, X, U>;
fn new_service(&self, cfg: &SrvConfig) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
HttpServiceResponse { HttpServiceResponse {
fut: self.srv.new_service(cfg), fut: self.srv.new_service(()),
fut_ex: Some(self.expect.new_service(cfg)), fut_ex: Some(self.expect.new_service(())),
fut_upg: self.upgrade.as_ref().map(|f| f.new_service(cfg)), fut_upg: self.upgrade.as_ref().map(|f| f.new_service(())),
expect: None, expect: None,
upgrade: None, upgrade: None,
on_connect: self.on_connect.clone(), on_connect: self.on_connect.clone(),
cfg: Some(self.cfg.clone()), cfg: self.cfg.clone(),
_t: PhantomData, _t: PhantomData,
} }
} }
@ -201,7 +364,6 @@ where
#[pin_project] #[pin_project]
pub struct HttpServiceResponse< pub struct HttpServiceResponse<
T, T,
P,
S: ServiceFactory, S: ServiceFactory,
B, B,
X: ServiceFactory, X: ServiceFactory,
@ -216,13 +378,13 @@ pub struct HttpServiceResponse<
expect: Option<X::Service>, expect: Option<X::Service>,
upgrade: Option<U::Service>, upgrade: Option<U::Service>,
on_connect: Option<rc::Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>, on_connect: Option<rc::Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>,
cfg: Option<ServiceConfig>, cfg: ServiceConfig,
_t: PhantomData<(T, P, B)>, _t: PhantomData<(T, B)>,
} }
impl<T, P, S, B, X, U> Future for HttpServiceResponse<T, P, S, B, X, U> impl<T, S, B, X, U> Future for HttpServiceResponse<T, S, B, X, U>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
S: ServiceFactory<Request = Request>, S: ServiceFactory<Request = Request>,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
@ -239,7 +401,7 @@ where
<U::Service as Service>::Future: 'static, <U::Service as Service>::Future: 'static,
{ {
type Output = type Output =
Result<HttpServiceHandler<T, P, S::Service, B, X::Service, U::Service>, ()>; Result<HttpServiceHandler<T, S::Service, B, X::Service, U::Service>, ()>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let mut this = self.as_mut().project(); let mut this = self.as_mut().project();
@ -269,7 +431,7 @@ where
Poll::Ready(result.map(|service| { Poll::Ready(result.map(|service| {
let this = self.as_mut().project(); let this = self.as_mut().project();
HttpServiceHandler::new( HttpServiceHandler::new(
this.cfg.take().unwrap(), this.cfg.clone(),
service, service,
this.expect.take().unwrap(), this.expect.take().unwrap(),
this.upgrade.take(), this.upgrade.take(),
@ -280,16 +442,16 @@ where
} }
/// `Service` implementation for http transport /// `Service` implementation for http transport
pub struct HttpServiceHandler<T, P, S, B, X, U> { pub struct HttpServiceHandler<T, S, B, X, U> {
srv: CloneableService<S>, srv: CloneableService<S>,
expect: CloneableService<X>, expect: CloneableService<X>,
upgrade: Option<CloneableService<U>>, upgrade: Option<CloneableService<U>>,
cfg: ServiceConfig, cfg: ServiceConfig,
on_connect: Option<rc::Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>, on_connect: Option<rc::Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>,
_t: PhantomData<(T, P, B, X)>, _t: PhantomData<(T, B, X)>,
} }
impl<T, P, S, B, X, U> HttpServiceHandler<T, P, S, B, X, U> impl<T, S, B, X, U> HttpServiceHandler<T, S, B, X, U>
where where
S: Service<Request = Request>, S: Service<Request = Request>,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
@ -307,7 +469,7 @@ where
expect: X, expect: X,
upgrade: Option<U>, upgrade: Option<U>,
on_connect: Option<rc::Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>, on_connect: Option<rc::Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>,
) -> HttpServiceHandler<T, P, S, B, X, U> { ) -> HttpServiceHandler<T, S, B, X, U> {
HttpServiceHandler { HttpServiceHandler {
cfg, cfg,
on_connect, on_connect,
@ -319,9 +481,9 @@ where
} }
} }
impl<T, P, S, B, X, U> Service for HttpServiceHandler<T, P, S, B, X, U> impl<T, S, B, X, U> Service for HttpServiceHandler<T, S, B, X, U>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
S: Service<Request = Request>, S: Service<Request = Request>,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::Future: 'static, S::Future: 'static,
@ -332,7 +494,7 @@ where
U: Service<Request = (Request, Framed<T, h1::Codec>), Response = ()>, U: Service<Request = (Request, Framed<T, h1::Codec>), Response = ()>,
U::Error: fmt::Display, U::Error: fmt::Display,
{ {
type Request = ServerIo<T, P>; type Request = (T, Protocol, Option<net::SocketAddr>);
type Response = (); type Response = ();
type Error = DispatchError; type Error = DispatchError;
type Future = HttpServiceHandlerResponse<T, S, B, X, U>; type Future = HttpServiceHandlerResponse<T, S, B, X, U>;
@ -366,9 +528,7 @@ where
} }
} }
fn call(&mut self, req: Self::Request) -> Self::Future { fn call(&mut self, (io, proto, peer_addr): Self::Request) -> Self::Future {
let (io, _, proto) = req.into_parts();
let on_connect = if let Some(ref on_connect) = self.on_connect { let on_connect = if let Some(ref on_connect) = self.on_connect {
Some(on_connect(&io)) Some(on_connect(&io))
} else { } else {
@ -376,23 +536,16 @@ where
}; };
match proto { match proto {
Protocol::Http2 => { Protocol::Http2 => HttpServiceHandlerResponse {
let peer_addr = io.peer_addr(); state: State::H2Handshake(Some((
let io = Io { server::handshake(io),
inner: io, self.cfg.clone(),
unread: None, self.srv.clone(),
}; on_connect,
HttpServiceHandlerResponse { peer_addr,
state: State::Handshake(Some(( ))),
server::handshake(io), },
self.cfg.clone(), Protocol::Http1 => HttpServiceHandlerResponse {
self.srv.clone(),
peer_addr,
on_connect,
))),
}
}
Protocol::Http10 | Protocol::Http11 => HttpServiceHandlerResponse {
state: State::H1(h1::Dispatcher::new( state: State::H1(h1::Dispatcher::new(
io, io,
self.cfg.clone(), self.cfg.clone(),
@ -400,19 +553,9 @@ where
self.expect.clone(), self.expect.clone(),
self.upgrade.clone(), self.upgrade.clone(),
on_connect, on_connect,
peer_addr,
)), )),
}, },
_ => HttpServiceHandlerResponse {
state: State::Unknown(Some((
io,
BytesMut::with_capacity(14),
self.cfg.clone(),
self.srv.clone(),
self.expect.clone(),
self.upgrade.clone(),
on_connect,
))),
},
} }
} }
} }
@ -423,7 +566,7 @@ where
S: Service<Request = Request>, S: Service<Request = Request>,
S::Future: 'static, S::Future: 'static,
S::Error: Into<Error>, S::Error: Into<Error>,
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
B: MessageBody, B: MessageBody,
X: Service<Request = Request, Response = Request>, X: Service<Request = Request, Response = Request>,
X::Error: Into<Error>, X::Error: Into<Error>,
@ -431,25 +574,14 @@ where
U::Error: fmt::Display, U::Error: fmt::Display,
{ {
H1(#[pin] h1::Dispatcher<T, S, B, X, U>), H1(#[pin] h1::Dispatcher<T, S, B, X, U>),
H2(#[pin] Dispatcher<Io<T>, S, B>), H2(#[pin] Dispatcher<T, S, B>),
Unknown( H2Handshake(
Option<( Option<(
T, Handshake<T, Bytes>,
BytesMut,
ServiceConfig, ServiceConfig,
CloneableService<S>, CloneableService<S>,
CloneableService<X>,
Option<CloneableService<U>>,
Option<Box<dyn DataFactory>>, Option<Box<dyn DataFactory>>,
)>,
),
Handshake(
Option<(
Handshake<Io<T>, Bytes>,
ServiceConfig,
CloneableService<S>,
Option<net::SocketAddr>, Option<net::SocketAddr>,
Option<Box<dyn DataFactory>>,
)>, )>,
), ),
} }
@ -457,7 +589,7 @@ where
#[pin_project] #[pin_project]
pub struct HttpServiceHandlerResponse<T, S, B, X, U> pub struct HttpServiceHandlerResponse<T, S, B, X, U>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
S: Service<Request = Request>, S: Service<Request = Request>,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::Future: 'static, S::Future: 'static,
@ -472,11 +604,9 @@ where
state: State<T, S, B, X, U>, state: State<T, S, B, X, U>,
} }
const HTTP2_PREFACE: [u8; 14] = *b"PRI * HTTP/2.0";
impl<T, S, B, X, U> Future for HttpServiceHandlerResponse<T, S, B, X, U> impl<T, S, B, X, U> Future for HttpServiceHandlerResponse<T, S, B, X, U>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
S: Service<Request = Request>, S: Service<Request = Request>,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::Future: 'static, S::Future: 'static,
@ -496,7 +626,7 @@ where
impl<T, S, B, X, U> State<T, S, B, X, U> impl<T, S, B, X, U> State<T, S, B, X, U>
where where
T: IoStream, T: AsyncRead + AsyncWrite + Unpin,
S: Service<Request = Request>, S: Service<Request = Request>,
S::Error: Into<Error> + 'static, S::Error: Into<Error> + 'static,
S::Response: Into<Response<B>> + 'static, S::Response: Into<Response<B>> + 'static,
@ -515,57 +645,7 @@ where
match self.as_mut().project() { match self.as_mut().project() {
State::H1(disp) => disp.poll(cx), State::H1(disp) => disp.poll(cx),
State::H2(disp) => disp.poll(cx), State::H2(disp) => disp.poll(cx),
State::Unknown(ref mut data) => { State::H2Handshake(ref mut data) => {
if let Some(ref mut item) = data {
loop {
// Safety - we only write to the returned slice.
let b = unsafe { item.1.bytes_mut() };
let n = ready!(Pin::new(&mut item.0).poll_read(cx, b))?;
if n == 0 {
return Poll::Ready(Ok(()));
}
// Safety - we know that 'n' bytes have
// been initialized via the contract of
// 'poll_read'
unsafe { item.1.advance_mut(n) };
if item.1.len() >= HTTP2_PREFACE.len() {
break;
}
}
} else {
panic!()
}
let (io, buf, cfg, srv, expect, upgrade, on_connect) =
data.take().unwrap();
if buf[..14] == HTTP2_PREFACE[..] {
let peer_addr = io.peer_addr();
let io = Io {
inner: io,
unread: Some(buf),
};
self.set(State::Handshake(Some((
server::handshake(io),
cfg,
srv,
peer_addr,
on_connect,
))));
} else {
self.set(State::H1(h1::Dispatcher::with_timeout(
io,
h1::Codec::new(cfg.clone()),
cfg,
buf,
None,
srv,
expect,
upgrade,
on_connect,
)))
}
self.poll(cx)
}
State::Handshake(ref mut data) => {
let conn = if let Some(ref mut item) = data { let conn = if let Some(ref mut item) = data {
match Pin::new(&mut item.0).poll(cx) { match Pin::new(&mut item.0).poll(cx) {
Poll::Ready(Ok(conn)) => conn, Poll::Ready(Ok(conn)) => conn,
@ -578,7 +658,7 @@ where
} else { } else {
panic!() panic!()
}; };
let (_, cfg, srv, peer_addr, on_connect) = data.take().unwrap(); let (_, cfg, srv, on_connect, peer_addr) = data.take().unwrap();
self.set(State::H2(Dispatcher::new( self.set(State::H2(Dispatcher::new(
srv, conn, on_connect, cfg, None, peer_addr, srv, conn, on_connect, cfg, None, peer_addr,
))); )));
@ -587,117 +667,3 @@ where
} }
} }
} }
/// Wrapper for `AsyncRead + AsyncWrite` types
#[pin_project::pin_project]
struct Io<T> {
unread: Option<BytesMut>,
#[pin]
inner: T,
}
impl<T: io::Read> io::Read for Io<T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if let Some(mut bytes) = self.unread.take() {
let size = std::cmp::min(buf.len(), bytes.len());
buf[..size].copy_from_slice(&bytes[..size]);
if bytes.len() > size {
bytes.split_to(size);
self.unread = Some(bytes);
}
Ok(size)
} else {
self.inner.read(buf)
}
}
}
impl<T: io::Write> io::Write for Io<T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
impl<T: AsyncRead> AsyncRead for Io<T> {
// unsafe fn initializer(&self) -> io::Initializer {
// self.get_mut().inner.initializer()
// }
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
self.inner.prepare_uninitialized_buffer(buf)
}
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
let this = self.project();
if let Some(mut bytes) = this.unread.take() {
let size = std::cmp::min(buf.len(), bytes.len());
buf[..size].copy_from_slice(&bytes[..size]);
if bytes.len() > size {
bytes.split_to(size);
*this.unread = Some(bytes);
}
Poll::Ready(Ok(size))
} else {
this.inner.poll_read(cx, buf)
}
}
// fn poll_read_vectored(
// self: Pin<&mut Self>,
// cx: &mut Context<'_>,
// bufs: &mut [io::IoSliceMut<'_>],
// ) -> Poll<io::Result<usize>> {
// self.get_mut().inner.poll_read_vectored(cx, bufs)
// }
}
impl<T: AsyncWrite> actix_codec::AsyncWrite for Io<T> {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
self.project().inner.poll_write(cx, buf)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.project().inner.poll_flush(cx)
}
fn poll_shutdown(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<io::Result<()>> {
self.project().inner.poll_shutdown(cx)
}
}
impl<T: IoStream> actix_server_config::IoStream for Io<T> {
#[inline]
fn peer_addr(&self) -> Option<net::SocketAddr> {
self.inner.peer_addr()
}
#[inline]
fn set_nodelay(&mut self, nodelay: bool) -> io::Result<()> {
self.inner.set_nodelay(nodelay)
}
#[inline]
fn set_linger(&mut self, dur: Option<std::time::Duration>) -> io::Result<()> {
self.inner.set_linger(dur)
}
#[inline]
fn set_keepalive(&mut self, dur: Option<std::time::Duration>) -> io::Result<()> {
self.inner.set_keepalive(dur)
}
}

View File

@ -1,4 +1,5 @@
//! Test Various helpers for Actix applications to use during testing. //! Test Various helpers for Actix applications to use during testing.
use std::convert::TryFrom;
use std::fmt::Write as FmtWrite; use std::fmt::Write as FmtWrite;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use std::pin::Pin; use std::pin::Pin;
@ -6,10 +7,9 @@ use std::str::FromStr;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use actix_codec::{AsyncRead, AsyncWrite}; use actix_codec::{AsyncRead, AsyncWrite};
use actix_server_config::IoStream;
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use http::header::{self, HeaderName, HeaderValue}; use http::header::{self, HeaderName, HeaderValue};
use http::{HttpTryFrom, Method, Uri, Version}; use http::{Error as HttpError, Method, Uri, Version};
use percent_encoding::percent_encode; use percent_encoding::percent_encode;
use crate::cookie::{Cookie, CookieJar, USERINFO}; use crate::cookie::{Cookie, CookieJar, USERINFO};
@ -83,7 +83,8 @@ impl TestRequest {
/// Create TestRequest and set header /// Create TestRequest and set header
pub fn with_header<K, V>(key: K, value: V) -> TestRequest pub fn with_header<K, V>(key: K, value: V) -> TestRequest
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
TestRequest::default().header(key, value).take() TestRequest::default().header(key, value).take()
@ -119,7 +120,8 @@ impl TestRequest {
/// Set a header /// Set a header
pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
if let Ok(key) = HeaderName::try_from(key) { if let Ok(key) = HeaderName::try_from(key) {
@ -272,17 +274,3 @@ impl AsyncWrite for TestBuffer {
Poll::Ready(Ok(())) Poll::Ready(Ok(()))
} }
} }
impl IoStream for TestBuffer {
fn set_nodelay(&mut self, _nodelay: bool) -> io::Result<()> {
Ok(())
}
fn set_linger(&mut self, _dur: Option<std::time::Duration>) -> io::Result<()> {
Ok(())
}
fn set_keepalive(&mut self, _dur: Option<std::time::Duration>) -> io::Result<()> {
Ok(())
}
}

View File

@ -180,11 +180,11 @@ impl Parser {
} else if payload_len <= 65_535 { } else if payload_len <= 65_535 {
dst.reserve(p_len + 4 + if mask { 4 } else { 0 }); dst.reserve(p_len + 4 + if mask { 4 } else { 0 });
dst.put_slice(&[one, two | 126]); dst.put_slice(&[one, two | 126]);
dst.put_u16_be(payload_len as u16); dst.put_u16(payload_len as u16);
} else { } else {
dst.reserve(p_len + 10 + if mask { 4 } else { 0 }); dst.reserve(p_len + 10 + if mask { 4 } else { 0 });
dst.put_slice(&[one, two | 127]); dst.put_slice(&[one, two | 127]);
dst.put_u64_be(payload_len as u64); dst.put_u64(payload_len as u64);
}; };
if mask { if mask {

View File

@ -30,7 +30,9 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
#[actix_rt::test] #[actix_rt::test]
async fn test_h1_v2() { async fn test_h1_v2() {
let srv = TestServer::start(move || { let srv = TestServer::start(move || {
HttpService::build().finish(|_| future::ok::<_, ()>(Response::Ok().body(STR))) HttpService::build()
.finish(|_| future::ok::<_, ()>(Response::Ok().body(STR)))
.tcp()
}); });
let response = srv.get("/").send().await.unwrap(); let response = srv.get("/").send().await.unwrap();
@ -57,6 +59,7 @@ async fn test_connection_close() {
let srv = TestServer::start(move || { let srv = TestServer::start(move || {
HttpService::build() HttpService::build()
.finish(|_| ok::<_, ()>(Response::Ok().body(STR))) .finish(|_| ok::<_, ()>(Response::Ok().body(STR)))
.tcp()
.map(|_| ()) .map(|_| ())
}); });
@ -75,6 +78,7 @@ async fn test_with_query_parameter() {
ok::<_, ()>(Response::BadRequest().finish()) ok::<_, ()>(Response::BadRequest().finish())
} }
}) })
.tcp()
.map(|_| ()) .map(|_| ())
}); });

View File

@ -1,11 +1,8 @@
#![cfg(feature = "openssl")] #![cfg(feature = "openssl")]
use std::io; use std::io;
use actix_codec::{AsyncRead, AsyncWrite};
use actix_http_test::TestServer; use actix_http_test::TestServer;
use actix_server::ssl::OpensslAcceptor; use actix_service::{service_fn, ServiceFactory};
use actix_server_config::ServerConfig;
use actix_service::{factory_fn_cfg, pipeline_factory, service_fn2, ServiceFactory};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures::future::{err, ok, ready}; use futures::future::{err, ok, ready};
@ -36,7 +33,7 @@ where
Ok(body) Ok(body)
} }
fn ssl_acceptor<T: AsyncRead + AsyncWrite>() -> io::Result<OpensslAcceptor<T, ()>> { fn ssl_acceptor() -> SslAcceptor {
// load ssl keys // load ssl keys
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
builder builder
@ -47,30 +44,29 @@ fn ssl_acceptor<T: AsyncRead + AsyncWrite>() -> io::Result<OpensslAcceptor<T, ()
.unwrap(); .unwrap();
builder.set_alpn_select_callback(|_, protos| { builder.set_alpn_select_callback(|_, protos| {
const H2: &[u8] = b"\x02h2"; const H2: &[u8] = b"\x02h2";
const H11: &[u8] = b"\x08http/1.1";
if protos.windows(3).any(|window| window == H2) { if protos.windows(3).any(|window| window == H2) {
Ok(b"h2") Ok(b"h2")
} else if protos.windows(9).any(|window| window == H11) {
Ok(b"http/1.1")
} else { } else {
Err(AlpnError::NOACK) Err(AlpnError::NOACK)
} }
}); });
builder.set_alpn_protos(b"\x02h2")?; builder
Ok(OpensslAcceptor::new(builder.build())) .set_alpn_protos(b"\x08http/1.1\x02h2")
.expect("Can not contrust SslAcceptor");
builder.build()
} }
#[actix_rt::test] #[actix_rt::test]
async fn test_h2() -> io::Result<()> { async fn test_h2() -> io::Result<()> {
let openssl = ssl_acceptor()?;
let srv = TestServer::start(move || { let srv = TestServer::start(move || {
pipeline_factory( HttpService::build()
openssl .h2(|_| ok::<_, Error>(Response::Ok().finish()))
.clone() .openssl(ssl_acceptor())
.map_err(|e| println!("Openssl error: {}", e)), .map_err(|_| ())
)
.and_then(
HttpService::build()
.h2(|_| ok::<_, Error>(Response::Ok().finish()))
.map_err(|_| ()),
)
}); });
let response = srv.sget("/").send().await.unwrap(); let response = srv.sget("/").send().await.unwrap();
@ -80,22 +76,15 @@ async fn test_h2() -> io::Result<()> {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_1() -> io::Result<()> { async fn test_h2_1() -> io::Result<()> {
let openssl = ssl_acceptor()?;
let srv = TestServer::start(move || { let srv = TestServer::start(move || {
pipeline_factory( HttpService::build()
openssl .finish(|req: Request| {
.clone() assert!(req.peer_addr().is_some());
.map_err(|e| println!("Openssl error: {}", e)), assert_eq!(req.version(), Version::HTTP_2);
) ok::<_, Error>(Response::Ok().finish())
.and_then( })
HttpService::build() .openssl(ssl_acceptor())
.finish(|req: Request| { .map_err(|_| ())
assert!(req.peer_addr().is_some());
assert_eq!(req.version(), Version::HTTP_2);
ok::<_, Error>(Response::Ok().finish())
})
.map_err(|_| ()),
)
}); });
let response = srv.sget("/").send().await.unwrap(); let response = srv.sget("/").send().await.unwrap();
@ -106,23 +95,16 @@ async fn test_h2_1() -> io::Result<()> {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_body() -> io::Result<()> { async fn test_h2_body() -> io::Result<()> {
let data = "HELLOWORLD".to_owned().repeat(64 * 1024); let data = "HELLOWORLD".to_owned().repeat(64 * 1024);
let openssl = ssl_acceptor()?;
let mut srv = TestServer::start(move || { let mut srv = TestServer::start(move || {
pipeline_factory( HttpService::build()
openssl .h2(|mut req: Request<_>| {
.clone() async move {
.map_err(|e| println!("Openssl error: {}", e)), let body = load_body(req.take_payload()).await?;
) Ok::<_, Error>(Response::Ok().body(body))
.and_then( }
HttpService::build() })
.h2(|mut req: Request<_>| { .openssl(ssl_acceptor())
async move { .map_err(|_| ())
let body = load_body(req.take_payload()).await?;
Ok::<_, Error>(Response::Ok().body(body))
}
})
.map_err(|_| ()),
)
}); });
let response = srv.sget("/").send_body(data.clone()).await.unwrap(); let response = srv.sget("/").send_body(data.clone()).await.unwrap();
@ -135,30 +117,22 @@ async fn test_h2_body() -> io::Result<()> {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_content_length() { async fn test_h2_content_length() {
let openssl = ssl_acceptor().unwrap();
let srv = TestServer::start(move || { let srv = TestServer::start(move || {
pipeline_factory( HttpService::build()
openssl .h2(|req: Request| {
.clone() let indx: usize = req.uri().path()[1..].parse().unwrap();
.map_err(|e| println!("Openssl error: {}", e)), let statuses = [
) StatusCode::NO_CONTENT,
.and_then( StatusCode::CONTINUE,
HttpService::build() StatusCode::SWITCHING_PROTOCOLS,
.h2(|req: Request| { StatusCode::PROCESSING,
let indx: usize = req.uri().path()[1..].parse().unwrap(); StatusCode::OK,
let statuses = [ StatusCode::NOT_FOUND,
StatusCode::NO_CONTENT, ];
StatusCode::CONTINUE, ok::<_, ()>(Response::new(statuses[indx]))
StatusCode::SWITCHING_PROTOCOLS, })
StatusCode::PROCESSING, .openssl(ssl_acceptor())
StatusCode::OK, .map_err(|_| ())
StatusCode::NOT_FOUND,
];
ok::<_, ()>(Response::new(statuses[indx]))
})
.map_err(|_| ()),
)
}); });
let header = HeaderName::from_static("content-length"); let header = HeaderName::from_static("content-length");
@ -193,14 +167,9 @@ async fn test_h2_content_length() {
async fn test_h2_headers() { async fn test_h2_headers() {
let data = STR.repeat(10); let data = STR.repeat(10);
let data2 = data.clone(); let data2 = data.clone();
let openssl = ssl_acceptor().unwrap();
let mut srv = TestServer::start(move || { let mut srv = TestServer::start(move || {
let data = data.clone(); let data = data.clone();
pipeline_factory(openssl
.clone()
.map_err(|e| println!("Openssl error: {}", e)))
.and_then(
HttpService::build().h2(move |_| { HttpService::build().h2(move |_| {
let mut builder = Response::Ok(); let mut builder = Response::Ok();
for idx in 0..90 { for idx in 0..90 {
@ -222,7 +191,9 @@ async fn test_h2_headers() {
); );
} }
ok::<_, ()>(builder.body(data.clone())) ok::<_, ()>(builder.body(data.clone()))
}).map_err(|_| ())) })
.openssl(ssl_acceptor())
.map_err(|_| ())
}); });
let response = srv.sget("/").send().await.unwrap(); let response = srv.sget("/").send().await.unwrap();
@ -257,18 +228,11 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_body2() { async fn test_h2_body2() {
let openssl = ssl_acceptor().unwrap();
let mut srv = TestServer::start(move || { let mut srv = TestServer::start(move || {
pipeline_factory( HttpService::build()
openssl .h2(|_| ok::<_, ()>(Response::Ok().body(STR)))
.clone() .openssl(ssl_acceptor())
.map_err(|e| println!("Openssl error: {}", e)), .map_err(|_| ())
)
.and_then(
HttpService::build()
.h2(|_| ok::<_, ()>(Response::Ok().body(STR)))
.map_err(|_| ()),
)
}); });
let response = srv.sget("/").send().await.unwrap(); let response = srv.sget("/").send().await.unwrap();
@ -281,18 +245,11 @@ async fn test_h2_body2() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_head_empty() { async fn test_h2_head_empty() {
let openssl = ssl_acceptor().unwrap();
let mut srv = TestServer::start(move || { let mut srv = TestServer::start(move || {
pipeline_factory( HttpService::build()
openssl .finish(|_| ok::<_, ()>(Response::Ok().body(STR)))
.clone() .openssl(ssl_acceptor())
.map_err(|e| println!("Openssl error: {}", e)), .map_err(|_| ())
)
.and_then(
HttpService::build()
.finish(|_| ok::<_, ()>(Response::Ok().body(STR)))
.map_err(|_| ()),
)
}); });
let response = srv.shead("/").send().await.unwrap(); let response = srv.shead("/").send().await.unwrap();
@ -311,22 +268,13 @@ async fn test_h2_head_empty() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_head_binary() { async fn test_h2_head_binary() {
let openssl = ssl_acceptor().unwrap();
let mut srv = TestServer::start(move || { let mut srv = TestServer::start(move || {
pipeline_factory( HttpService::build()
openssl .h2(|_| {
.clone() ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR))
.map_err(|e| println!("Openssl error: {}", e)), })
) .openssl(ssl_acceptor())
.and_then( .map_err(|_| ())
HttpService::build()
.h2(|_| {
ok::<_, ()>(
Response::Ok().content_length(STR.len() as u64).body(STR),
)
})
.map_err(|_| ()),
)
}); });
let response = srv.shead("/").send().await.unwrap(); let response = srv.shead("/").send().await.unwrap();
@ -344,18 +292,11 @@ async fn test_h2_head_binary() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_head_binary2() { async fn test_h2_head_binary2() {
let openssl = ssl_acceptor().unwrap();
let srv = TestServer::start(move || { let srv = TestServer::start(move || {
pipeline_factory( HttpService::build()
openssl .h2(|_| ok::<_, ()>(Response::Ok().body(STR)))
.clone() .openssl(ssl_acceptor())
.map_err(|e| println!("Openssl error: {}", e)), .map_err(|_| ())
)
.and_then(
HttpService::build()
.h2(|_| ok::<_, ()>(Response::Ok().body(STR)))
.map_err(|_| ()),
)
}); });
let response = srv.shead("/").send().await.unwrap(); let response = srv.shead("/").send().await.unwrap();
@ -369,24 +310,16 @@ async fn test_h2_head_binary2() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_body_length() { async fn test_h2_body_length() {
let openssl = ssl_acceptor().unwrap();
let mut srv = TestServer::start(move || { let mut srv = TestServer::start(move || {
pipeline_factory( HttpService::build()
openssl .h2(|_| {
.clone() let body = once(ok(Bytes::from_static(STR.as_ref())));
.map_err(|e| println!("Openssl error: {}", e)), ok::<_, ()>(
) Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)),
.and_then( )
HttpService::build() })
.h2(|_| { .openssl(ssl_acceptor())
let body = once(ok(Bytes::from_static(STR.as_ref()))); .map_err(|_| ())
ok::<_, ()>(
Response::Ok()
.body(body::SizedStream::new(STR.len() as u64, body)),
)
})
.map_err(|_| ()),
)
}); });
let response = srv.sget("/").send().await.unwrap(); let response = srv.sget("/").send().await.unwrap();
@ -399,25 +332,18 @@ async fn test_h2_body_length() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_body_chunked_explicit() { async fn test_h2_body_chunked_explicit() {
let openssl = ssl_acceptor().unwrap();
let mut srv = TestServer::start(move || { let mut srv = TestServer::start(move || {
pipeline_factory( HttpService::build()
openssl .h2(|_| {
.clone() let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));
.map_err(|e| println!("Openssl error: {}", e)), ok::<_, ()>(
) Response::Ok()
.and_then( .header(header::TRANSFER_ENCODING, "chunked")
HttpService::build() .streaming(body),
.h2(|_| { )
let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); })
ok::<_, ()>( .openssl(ssl_acceptor())
Response::Ok() .map_err(|_| ())
.header(header::TRANSFER_ENCODING, "chunked")
.streaming(body),
)
})
.map_err(|_| ()),
)
}); });
let response = srv.sget("/").send().await.unwrap(); let response = srv.sget("/").send().await.unwrap();
@ -433,28 +359,18 @@ async fn test_h2_body_chunked_explicit() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_response_http_error_handling() { async fn test_h2_response_http_error_handling() {
let openssl = ssl_acceptor().unwrap();
let mut srv = TestServer::start(move || { let mut srv = TestServer::start(move || {
pipeline_factory( HttpService::build()
openssl .h2(service_fn(|_| {
.clone() let broken_header = Bytes::from_static(b"\0\0\0");
.map_err(|e| println!("Openssl error: {}", e)), ok::<_, ()>(
) Response::Ok()
.and_then( .header(header::CONTENT_TYPE, broken_header)
HttpService::build() .body(STR),
.h2(factory_fn_cfg(|_: &ServerConfig| { )
ok::<_, ()>(service_fn2(|_| { }))
let broken_header = Bytes::from_static(b"\0\0\0"); .openssl(ssl_acceptor())
ok::<_, ()>( .map_err(|_| ())
Response::Ok()
.header(header::CONTENT_TYPE, broken_header)
.body(STR),
)
}))
}))
.map_err(|_| ()),
)
}); });
let response = srv.sget("/").send().await.unwrap(); let response = srv.sget("/").send().await.unwrap();
@ -467,19 +383,11 @@ async fn test_h2_response_http_error_handling() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_service_error() { async fn test_h2_service_error() {
let openssl = ssl_acceptor().unwrap();
let mut srv = TestServer::start(move || { let mut srv = TestServer::start(move || {
pipeline_factory( HttpService::build()
openssl .h2(|_| err::<Response, Error>(ErrorBadRequest("error")))
.clone() .openssl(ssl_acceptor())
.map_err(|e| println!("Openssl error: {}", e)), .map_err(|_| ())
)
.and_then(
HttpService::build()
.h2(|_| err::<Response, Error>(ErrorBadRequest("error")))
.map_err(|_| ()),
)
}); });
let response = srv.sget("/").send().await.unwrap(); let response = srv.sget("/").send().await.unwrap();
@ -492,23 +400,15 @@ async fn test_h2_service_error() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_on_connect() { async fn test_h2_on_connect() {
let openssl = ssl_acceptor().unwrap();
let srv = TestServer::start(move || { let srv = TestServer::start(move || {
pipeline_factory( HttpService::build()
openssl .on_connect(|_| 10usize)
.clone() .h2(|req: Request| {
.map_err(|e| println!("Openssl error: {}", e)), assert!(req.extensions().contains::<usize>());
) ok::<_, ()>(Response::Ok().finish())
.and_then( })
HttpService::build() .openssl(ssl_acceptor())
.on_connect(|_| 10usize) .map_err(|_| ())
.h2(|req: Request| {
assert!(req.extensions().contains::<usize>());
ok::<_, ()>(Response::Ok().finish())
})
.map_err(|_| ()),
)
}); });
let response = srv.sget("/").send().await.unwrap(); let response = srv.sget("/").send().await.unwrap();

View File

@ -1,13 +1,10 @@
#![cfg(feature = "rustls")] #![cfg(feature = "rustls")]
use actix_codec::{AsyncRead, AsyncWrite};
use actix_http::error::PayloadError; use actix_http::error::PayloadError;
use actix_http::http::header::{self, HeaderName, HeaderValue}; use actix_http::http::header::{self, HeaderName, HeaderValue};
use actix_http::http::{Method, StatusCode, Version}; 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::TestServer; use actix_http_test::TestServer;
use actix_server::ssl::RustlsAcceptor; use actix_service::{factory_fn_cfg, service_fn2};
use actix_server_config::ServerConfig;
use actix_service::{factory_fn_cfg, pipeline_factory, service_fn2, ServiceFactory};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures::future::{self, err, ok}; use futures::future::{self, err, ok};
@ -31,7 +28,7 @@ where
Ok(body) Ok(body)
} }
fn ssl_acceptor<T: AsyncRead + AsyncWrite>() -> io::Result<RustlsAcceptor<T, ()>> { fn ssl_acceptor() -> RustlsServerConfig {
// load ssl keys // load ssl keys
let mut config = RustlsServerConfig::new(NoClientAuth::new()); let mut config = RustlsServerConfig::new(NoClientAuth::new());
let cert_file = &mut BufReader::new(File::open("../tests/cert.pem").unwrap()); let cert_file = &mut BufReader::new(File::open("../tests/cert.pem").unwrap());
@ -39,22 +36,45 @@ fn ssl_acceptor<T: AsyncRead + AsyncWrite>() -> io::Result<RustlsAcceptor<T, ()>
let cert_chain = certs(cert_file).unwrap(); let cert_chain = certs(cert_file).unwrap();
let mut keys = pkcs8_private_keys(key_file).unwrap(); let mut keys = pkcs8_private_keys(key_file).unwrap();
config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); config.set_single_cert(cert_chain, keys.remove(0)).unwrap();
config
}
let protos = vec![b"h2".to_vec()]; #[actix_rt::test]
config.set_protocols(&protos); async fn test_h1() -> io::Result<()> {
Ok(RustlsAcceptor::new(config)) let srv = TestServer::start(move || {
HttpService::build()
.h1(|_| future::ok::<_, Error>(Response::Ok().finish()))
.rustls(ssl_acceptor())
});
let response = srv.sget("/").send().await.unwrap();
assert!(response.status().is_success());
Ok(())
} }
#[actix_rt::test] #[actix_rt::test]
async fn test_h2() -> io::Result<()> { async fn test_h2() -> io::Result<()> {
let rustls = ssl_acceptor()?;
let srv = TestServer::start(move || { let srv = TestServer::start(move || {
pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) HttpService::build()
.and_then( .h2(|_| future::ok::<_, Error>(Response::Ok().finish()))
HttpService::build() .rustls(ssl_acceptor())
.h2(|_| future::ok::<_, Error>(Response::Ok().finish())) });
.map_err(|_| ()),
) let response = srv.sget("/").send().await.unwrap();
assert!(response.status().is_success());
Ok(())
}
#[actix_rt::test]
async fn test_h1_1() -> io::Result<()> {
let srv = TestServer::start(move || {
HttpService::build()
.h1(|req: Request| {
assert!(req.peer_addr().is_some());
assert_eq!(req.version(), Version::HTTP_11);
future::ok::<_, Error>(Response::Ok().finish())
})
.rustls(ssl_acceptor())
}); });
let response = srv.sget("/").send().await.unwrap(); let response = srv.sget("/").send().await.unwrap();
@ -64,18 +84,14 @@ async fn test_h2() -> io::Result<()> {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_1() -> io::Result<()> { async fn test_h2_1() -> io::Result<()> {
let rustls = ssl_acceptor()?;
let srv = TestServer::start(move || { let srv = TestServer::start(move || {
pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) HttpService::build()
.and_then( .finish(|req: Request| {
HttpService::build() assert!(req.peer_addr().is_some());
.finish(|req: Request| { assert_eq!(req.version(), Version::HTTP_2);
assert!(req.peer_addr().is_some()); future::ok::<_, Error>(Response::Ok().finish())
assert_eq!(req.version(), Version::HTTP_2); })
future::ok::<_, Error>(Response::Ok().finish()) .rustls(ssl_acceptor())
})
.map_err(|_| ()),
)
}); });
let response = srv.sget("/").send().await.unwrap(); let response = srv.sget("/").send().await.unwrap();
@ -86,19 +102,15 @@ async fn test_h2_1() -> io::Result<()> {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_body1() -> io::Result<()> { async fn test_h2_body1() -> io::Result<()> {
let data = "HELLOWORLD".to_owned().repeat(64 * 1024); let data = "HELLOWORLD".to_owned().repeat(64 * 1024);
let rustls = ssl_acceptor()?;
let mut srv = TestServer::start(move || { let mut srv = TestServer::start(move || {
pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) HttpService::build()
.and_then( .h2(|mut req: Request<_>| {
HttpService::build() async move {
.h2(|mut req: Request<_>| { let body = load_body(req.take_payload()).await?;
async move { Ok::<_, Error>(Response::Ok().body(body))
let body = load_body(req.take_payload()).await?; }
Ok::<_, Error>(Response::Ok().body(body)) })
} .rustls(ssl_acceptor())
})
.map_err(|_| ()),
)
}); });
let response = srv.sget("/").send_body(data.clone()).await.unwrap(); let response = srv.sget("/").send_body(data.clone()).await.unwrap();
@ -111,31 +123,25 @@ async fn test_h2_body1() -> io::Result<()> {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_content_length() { async fn test_h2_content_length() {
let rustls = ssl_acceptor().unwrap();
let srv = TestServer::start(move || { let srv = TestServer::start(move || {
pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) HttpService::build()
.and_then( .h2(|req: Request| {
HttpService::build() let indx: usize = req.uri().path()[1..].parse().unwrap();
.h2(|req: Request| { let statuses = [
let indx: usize = req.uri().path()[1..].parse().unwrap(); StatusCode::NO_CONTENT,
let statuses = [ StatusCode::CONTINUE,
StatusCode::NO_CONTENT, StatusCode::SWITCHING_PROTOCOLS,
StatusCode::CONTINUE, StatusCode::PROCESSING,
StatusCode::SWITCHING_PROTOCOLS, StatusCode::OK,
StatusCode::PROCESSING, StatusCode::NOT_FOUND,
StatusCode::OK, ];
StatusCode::NOT_FOUND, future::ok::<_, ()>(Response::new(statuses[indx]))
]; })
future::ok::<_, ()>(Response::new(statuses[indx])) .rustls(ssl_acceptor())
})
.map_err(|_| ()),
)
}); });
let header = HeaderName::from_static("content-length"); let header = HeaderName::from_static("content-length");
let value = HeaderValue::from_static("0"); let value = HeaderValue::from_static("0");
{ {
for i in 0..4 { for i in 0..4 {
let req = srv let req = srv
@ -165,14 +171,9 @@ async fn test_h2_content_length() {
async fn test_h2_headers() { async fn test_h2_headers() {
let data = STR.repeat(10); let data = STR.repeat(10);
let data2 = data.clone(); let data2 = data.clone();
let rustls = ssl_acceptor().unwrap();
let mut srv = TestServer::start(move || { let mut srv = TestServer::start(move || {
let data = data.clone(); let data = data.clone();
pipeline_factory(rustls
.clone()
.map_err(|e| println!("Rustls error: {}", e)))
.and_then(
HttpService::build().h2(move |_| { HttpService::build().h2(move |_| {
let mut config = Response::Ok(); let mut config = Response::Ok();
for idx in 0..90 { for idx in 0..90 {
@ -194,7 +195,8 @@ async fn test_h2_headers() {
); );
} }
future::ok::<_, ()>(config.body(data.clone())) future::ok::<_, ()>(config.body(data.clone()))
}).map_err(|_| ())) })
.rustls(ssl_acceptor())
}); });
let response = srv.sget("/").send().await.unwrap(); let response = srv.sget("/").send().await.unwrap();
@ -229,14 +231,10 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_body2() { async fn test_h2_body2() {
let rustls = ssl_acceptor().unwrap();
let mut srv = TestServer::start(move || { let mut srv = TestServer::start(move || {
pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) HttpService::build()
.and_then( .h2(|_| future::ok::<_, ()>(Response::Ok().body(STR)))
HttpService::build() .rustls(ssl_acceptor())
.h2(|_| future::ok::<_, ()>(Response::Ok().body(STR)))
.map_err(|_| ()),
)
}); });
let response = srv.sget("/").send().await.unwrap(); let response = srv.sget("/").send().await.unwrap();
@ -249,14 +247,10 @@ async fn test_h2_body2() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_head_empty() { async fn test_h2_head_empty() {
let rustls = ssl_acceptor().unwrap();
let mut srv = TestServer::start(move || { let mut srv = TestServer::start(move || {
pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) HttpService::build()
.and_then( .finish(|_| ok::<_, ()>(Response::Ok().body(STR)))
HttpService::build() .rustls(ssl_acceptor())
.finish(|_| ok::<_, ()>(Response::Ok().body(STR)))
.map_err(|_| ()),
)
}); });
let response = srv.shead("/").send().await.unwrap(); let response = srv.shead("/").send().await.unwrap();
@ -278,18 +272,12 @@ async fn test_h2_head_empty() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_head_binary() { async fn test_h2_head_binary() {
let rustls = ssl_acceptor().unwrap();
let mut srv = TestServer::start(move || { let mut srv = TestServer::start(move || {
pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) HttpService::build()
.and_then( .h2(|_| {
HttpService::build() ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR))
.h2(|_| { })
ok::<_, ()>( .rustls(ssl_acceptor())
Response::Ok().content_length(STR.len() as u64).body(STR),
)
})
.map_err(|_| ()),
)
}); });
let response = srv.shead("/").send().await.unwrap(); let response = srv.shead("/").send().await.unwrap();
@ -310,14 +298,10 @@ async fn test_h2_head_binary() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_head_binary2() { async fn test_h2_head_binary2() {
let rustls = ssl_acceptor().unwrap();
let srv = TestServer::start(move || { let srv = TestServer::start(move || {
pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) HttpService::build()
.and_then( .h2(|_| ok::<_, ()>(Response::Ok().body(STR)))
HttpService::build() .rustls(ssl_acceptor())
.h2(|_| ok::<_, ()>(Response::Ok().body(STR)))
.map_err(|_| ()),
)
}); });
let response = srv.shead("/").send().await.unwrap(); let response = srv.shead("/").send().await.unwrap();
@ -334,20 +318,15 @@ async fn test_h2_head_binary2() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_body_length() { async fn test_h2_body_length() {
let rustls = ssl_acceptor().unwrap();
let mut srv = TestServer::start(move || { let mut srv = TestServer::start(move || {
pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) HttpService::build()
.and_then( .h2(|_| {
HttpService::build() let body = once(ok(Bytes::from_static(STR.as_ref())));
.h2(|_| { ok::<_, ()>(
let body = once(ok(Bytes::from_static(STR.as_ref()))); Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)),
ok::<_, ()>( )
Response::Ok() })
.body(body::SizedStream::new(STR.len() as u64, body)), .rustls(ssl_acceptor())
)
})
.map_err(|_| ()),
)
}); });
let response = srv.sget("/").send().await.unwrap(); let response = srv.sget("/").send().await.unwrap();
@ -360,22 +339,17 @@ async fn test_h2_body_length() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_body_chunked_explicit() { async fn test_h2_body_chunked_explicit() {
let rustls = ssl_acceptor().unwrap();
let mut srv = TestServer::start(move || { let mut srv = TestServer::start(move || {
pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) HttpService::build()
.and_then( .h2(|_| {
HttpService::build() let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));
.h2(|_| { ok::<_, ()>(
let body = Response::Ok()
once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); .header(header::TRANSFER_ENCODING, "chunked")
ok::<_, ()>( .streaming(body),
Response::Ok() )
.header(header::TRANSFER_ENCODING, "chunked") })
.streaming(body), .rustls(ssl_acceptor())
)
})
.map_err(|_| ()),
)
}); });
let response = srv.sget("/").send().await.unwrap(); let response = srv.sget("/").send().await.unwrap();
@ -391,24 +365,19 @@ async fn test_h2_body_chunked_explicit() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_response_http_error_handling() { async fn test_h2_response_http_error_handling() {
let rustls = ssl_acceptor().unwrap();
let mut srv = TestServer::start(move || { let mut srv = TestServer::start(move || {
pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) HttpService::build()
.and_then( .h2(factory_fn_cfg(|_: ()| {
HttpService::build() ok::<_, ()>(service_fn2(|_| {
.h2(factory_fn_cfg(|_: &ServerConfig| { let broken_header = Bytes::from_static(b"\0\0\0");
ok::<_, ()>(service_fn2(|_| { ok::<_, ()>(
let broken_header = Bytes::from_static(b"\0\0\0"); Response::Ok()
ok::<_, ()>( .header(http::header::CONTENT_TYPE, broken_header)
Response::Ok() .body(STR),
.header(http::header::CONTENT_TYPE, broken_header) )
.body(STR), }))
) }))
})) .rustls(ssl_acceptor())
}))
.map_err(|_| ()),
)
}); });
let response = srv.sget("/").send().await.unwrap(); let response = srv.sget("/").send().await.unwrap();
@ -421,15 +390,26 @@ async fn test_h2_response_http_error_handling() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h2_service_error() { async fn test_h2_service_error() {
let rustls = ssl_acceptor().unwrap();
let mut srv = TestServer::start(move || { let mut srv = TestServer::start(move || {
pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) HttpService::build()
.and_then( .h2(|_| err::<Response, Error>(error::ErrorBadRequest("error")))
HttpService::build() .rustls(ssl_acceptor())
.h2(|_| err::<Response, Error>(error::ErrorBadRequest("error"))) });
.map_err(|_| ()),
) let response = srv.sget("/").send().await.unwrap();
assert_eq!(response.status(), http::StatusCode::BAD_REQUEST);
// read response
let bytes = srv.load_body(response).await.unwrap();
assert_eq!(bytes, Bytes::from_static(b"error"));
}
#[actix_rt::test]
async fn test_h1_service_error() {
let mut srv = TestServer::start(move || {
HttpService::build()
.h1(|_| err::<Response, Error>(error::ErrorBadRequest("error")))
.rustls(ssl_acceptor())
}); });
let response = srv.sget("/").send().await.unwrap(); let response = srv.sget("/").send().await.unwrap();

View File

@ -4,8 +4,7 @@ use std::{net, thread};
use actix_http_test::TestServer; use actix_http_test::TestServer;
use actix_rt::time::delay_for; use actix_rt::time::delay_for;
use actix_server_config::ServerConfig; use actix_service::service_fn;
use actix_service::{factory_fn_cfg, pipeline, service_fn, ServiceFactory};
use bytes::Bytes; use bytes::Bytes;
use futures::future::{self, err, ok, ready, FutureExt}; use futures::future::{self, err, ok, ready, FutureExt};
use futures::stream::{once, StreamExt}; use futures::stream::{once, StreamExt};
@ -27,6 +26,7 @@ async fn test_h1() {
assert!(req.peer_addr().is_some()); assert!(req.peer_addr().is_some());
future::ok::<_, ()>(Response::Ok().finish()) future::ok::<_, ()>(Response::Ok().finish())
}) })
.tcp()
}); });
let response = srv.get("/").send().await.unwrap(); let response = srv.get("/").send().await.unwrap();
@ -45,7 +45,7 @@ async fn test_h1_2() {
assert_eq!(req.version(), http::Version::HTTP_11); assert_eq!(req.version(), http::Version::HTTP_11);
future::ok::<_, ()>(Response::Ok().finish()) future::ok::<_, ()>(Response::Ok().finish())
}) })
.map(|_| ()) .tcp()
}); });
let response = srv.get("/").send().await.unwrap(); let response = srv.get("/").send().await.unwrap();
@ -64,6 +64,7 @@ async fn test_expect_continue() {
} }
})) }))
.finish(|_| future::ok::<_, ()>(Response::Ok().finish())) .finish(|_| future::ok::<_, ()>(Response::Ok().finish()))
.tcp()
}); });
let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
@ -92,7 +93,8 @@ async fn test_expect_continue_h1() {
} }
}) })
})) }))
.h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .h1(service_fn(|_| future::ok::<_, ()>(Response::Ok().finish())))
.tcp()
}); });
let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
@ -114,18 +116,20 @@ async fn test_chunked_payload() {
let total_size: usize = chunk_sizes.iter().sum(); let total_size: usize = chunk_sizes.iter().sum();
let srv = TestServer::start(|| { let srv = TestServer::start(|| {
HttpService::build().h1(service_fn(|mut request: Request| { HttpService::build()
request .h1(service_fn(|mut request: Request| {
.take_payload() request
.map(|res| match res { .take_payload()
Ok(pl) => pl, .map(|res| match res {
Err(e) => panic!(format!("Error reading payload: {}", e)), Ok(pl) => pl,
}) Err(e) => panic!(format!("Error reading payload: {}", e)),
.fold(0usize, |acc, chunk| ready(acc + chunk.len())) })
.map(|req_size| { .fold(0usize, |acc, chunk| ready(acc + chunk.len()))
Ok::<_, Error>(Response::Ok().body(format!("size={}", req_size))) .map(|req_size| {
}) Ok::<_, Error>(Response::Ok().body(format!("size={}", req_size)))
})) })
}))
.tcp()
}); });
let returned_size = { let returned_size = {
@ -167,6 +171,7 @@ async fn test_slow_request() {
HttpService::build() HttpService::build()
.client_timeout(100) .client_timeout(100)
.finish(|_| future::ok::<_, ()>(Response::Ok().finish())) .finish(|_| future::ok::<_, ()>(Response::Ok().finish()))
.tcp()
}); });
let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
@ -179,7 +184,9 @@ async fn test_slow_request() {
#[actix_rt::test] #[actix_rt::test]
async fn test_http1_malformed_request() { async fn test_http1_malformed_request() {
let srv = TestServer::start(|| { let srv = TestServer::start(|| {
HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) HttpService::build()
.h1(|_| future::ok::<_, ()>(Response::Ok().finish()))
.tcp()
}); });
let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
@ -192,7 +199,9 @@ async fn test_http1_malformed_request() {
#[actix_rt::test] #[actix_rt::test]
async fn test_http1_keepalive() { async fn test_http1_keepalive() {
let srv = TestServer::start(|| { let srv = TestServer::start(|| {
HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) HttpService::build()
.h1(|_| future::ok::<_, ()>(Response::Ok().finish()))
.tcp()
}); });
let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
@ -213,6 +222,7 @@ async fn test_http1_keepalive_timeout() {
HttpService::build() HttpService::build()
.keep_alive(1) .keep_alive(1)
.h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .h1(|_| future::ok::<_, ()>(Response::Ok().finish()))
.tcp()
}); });
let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
@ -230,7 +240,9 @@ async fn test_http1_keepalive_timeout() {
#[actix_rt::test] #[actix_rt::test]
async fn test_http1_keepalive_close() { async fn test_http1_keepalive_close() {
let srv = TestServer::start(|| { let srv = TestServer::start(|| {
HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) HttpService::build()
.h1(|_| future::ok::<_, ()>(Response::Ok().finish()))
.tcp()
}); });
let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
@ -248,7 +260,9 @@ async fn test_http1_keepalive_close() {
#[actix_rt::test] #[actix_rt::test]
async fn test_http10_keepalive_default_close() { async fn test_http10_keepalive_default_close() {
let srv = TestServer::start(|| { let srv = TestServer::start(|| {
HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) HttpService::build()
.h1(|_| future::ok::<_, ()>(Response::Ok().finish()))
.tcp()
}); });
let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
@ -265,7 +279,9 @@ async fn test_http10_keepalive_default_close() {
#[actix_rt::test] #[actix_rt::test]
async fn test_http10_keepalive() { async fn test_http10_keepalive() {
let srv = TestServer::start(|| { let srv = TestServer::start(|| {
HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) HttpService::build()
.h1(|_| future::ok::<_, ()>(Response::Ok().finish()))
.tcp()
}); });
let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
@ -292,6 +308,7 @@ async fn test_http1_keepalive_disabled() {
HttpService::build() HttpService::build()
.keep_alive(KeepAlive::Disabled) .keep_alive(KeepAlive::Disabled)
.h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .h1(|_| future::ok::<_, ()>(Response::Ok().finish()))
.tcp()
}); });
let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
@ -313,18 +330,20 @@ async fn test_content_length() {
}; };
let srv = TestServer::start(|| { let srv = TestServer::start(|| {
HttpService::build().h1(|req: Request| { HttpService::build()
let indx: usize = req.uri().path()[1..].parse().unwrap(); .h1(|req: Request| {
let statuses = [ let indx: usize = req.uri().path()[1..].parse().unwrap();
StatusCode::NO_CONTENT, let statuses = [
StatusCode::CONTINUE, StatusCode::NO_CONTENT,
StatusCode::SWITCHING_PROTOCOLS, StatusCode::CONTINUE,
StatusCode::PROCESSING, StatusCode::SWITCHING_PROTOCOLS,
StatusCode::OK, StatusCode::PROCESSING,
StatusCode::NOT_FOUND, StatusCode::OK,
]; StatusCode::NOT_FOUND,
future::ok::<_, ()>(Response::new(statuses[indx])) ];
}) future::ok::<_, ()>(Response::new(statuses[indx]))
})
.tcp()
}); });
let header = HeaderName::from_static("content-length"); let header = HeaderName::from_static("content-length");
@ -377,7 +396,7 @@ async fn test_h1_headers() {
); );
} }
future::ok::<_, ()>(builder.body(data.clone())) future::ok::<_, ()>(builder.body(data.clone()))
}) }).tcp()
}); });
let response = srv.get("/").send().await.unwrap(); let response = srv.get("/").send().await.unwrap();
@ -413,7 +432,9 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
#[actix_rt::test] #[actix_rt::test]
async fn test_h1_body() { async fn test_h1_body() {
let mut srv = TestServer::start(|| { let mut srv = TestServer::start(|| {
HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) HttpService::build()
.h1(|_| ok::<_, ()>(Response::Ok().body(STR)))
.tcp()
}); });
let response = srv.get("/").send().await.unwrap(); let response = srv.get("/").send().await.unwrap();
@ -427,7 +448,9 @@ async fn test_h1_body() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h1_head_empty() { async fn test_h1_head_empty() {
let mut srv = TestServer::start(|| { let mut srv = TestServer::start(|| {
HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) HttpService::build()
.h1(|_| ok::<_, ()>(Response::Ok().body(STR)))
.tcp()
}); });
let response = srv.head("/").send().await.unwrap(); let response = srv.head("/").send().await.unwrap();
@ -449,9 +472,11 @@ async fn test_h1_head_empty() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h1_head_binary() { async fn test_h1_head_binary() {
let mut srv = TestServer::start(|| { let mut srv = TestServer::start(|| {
HttpService::build().h1(|_| { HttpService::build()
ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) .h1(|_| {
}) ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR))
})
.tcp()
}); });
let response = srv.head("/").send().await.unwrap(); let response = srv.head("/").send().await.unwrap();
@ -473,7 +498,9 @@ async fn test_h1_head_binary() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h1_head_binary2() { async fn test_h1_head_binary2() {
let srv = TestServer::start(|| { let srv = TestServer::start(|| {
HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) HttpService::build()
.h1(|_| ok::<_, ()>(Response::Ok().body(STR)))
.tcp()
}); });
let response = srv.head("/").send().await.unwrap(); let response = srv.head("/").send().await.unwrap();
@ -491,12 +518,14 @@ async fn test_h1_head_binary2() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h1_body_length() { async fn test_h1_body_length() {
let mut srv = TestServer::start(|| { let mut srv = TestServer::start(|| {
HttpService::build().h1(|_| { HttpService::build()
let body = once(ok(Bytes::from_static(STR.as_ref()))); .h1(|_| {
ok::<_, ()>( let body = once(ok(Bytes::from_static(STR.as_ref())));
Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)), ok::<_, ()>(
) Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)),
}) )
})
.tcp()
}); });
let response = srv.get("/").send().await.unwrap(); let response = srv.get("/").send().await.unwrap();
@ -510,14 +539,16 @@ async fn test_h1_body_length() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h1_body_chunked_explicit() { async fn test_h1_body_chunked_explicit() {
let mut srv = TestServer::start(|| { let mut srv = TestServer::start(|| {
HttpService::build().h1(|_| { HttpService::build()
let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); .h1(|_| {
ok::<_, ()>( let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));
Response::Ok() ok::<_, ()>(
.header(header::TRANSFER_ENCODING, "chunked") Response::Ok()
.streaming(body), .header(header::TRANSFER_ENCODING, "chunked")
) .streaming(body),
}) )
})
.tcp()
}); });
let response = srv.get("/").send().await.unwrap(); let response = srv.get("/").send().await.unwrap();
@ -542,10 +573,12 @@ async fn test_h1_body_chunked_explicit() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h1_body_chunked_implicit() { async fn test_h1_body_chunked_implicit() {
let mut srv = TestServer::start(|| { let mut srv = TestServer::start(|| {
HttpService::build().h1(|_| { HttpService::build()
let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); .h1(|_| {
ok::<_, ()>(Response::Ok().streaming(body)) let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));
}) ok::<_, ()>(Response::Ok().streaming(body))
})
.tcp()
}); });
let response = srv.get("/").send().await.unwrap(); let response = srv.get("/").send().await.unwrap();
@ -568,8 +601,8 @@ async fn test_h1_body_chunked_implicit() {
#[actix_rt::test] #[actix_rt::test]
async fn test_h1_response_http_error_handling() { async fn test_h1_response_http_error_handling() {
let mut srv = TestServer::start(|| { let mut srv = TestServer::start(|| {
HttpService::build().h1(factory_fn_cfg(|_: &ServerConfig| { HttpService::build()
ok::<_, ()>(pipeline(|_| { .h1(service_fn(|_| {
let broken_header = Bytes::from_static(b"\0\0\0"); let broken_header = Bytes::from_static(b"\0\0\0");
ok::<_, ()>( ok::<_, ()>(
Response::Ok() Response::Ok()
@ -577,7 +610,7 @@ async fn test_h1_response_http_error_handling() {
.body(STR), .body(STR),
) )
})) }))
})) .tcp()
}); });
let response = srv.get("/").send().await.unwrap(); let response = srv.get("/").send().await.unwrap();
@ -593,6 +626,7 @@ async fn test_h1_service_error() {
let mut srv = TestServer::start(|| { let mut srv = TestServer::start(|| {
HttpService::build() HttpService::build()
.h1(|_| future::err::<Response, Error>(error::ErrorBadRequest("error"))) .h1(|_| future::err::<Response, Error>(error::ErrorBadRequest("error")))
.tcp()
}); });
let response = srv.get("/").send().await.unwrap(); let response = srv.get("/").send().await.unwrap();
@ -612,6 +646,7 @@ async fn test_h1_on_connect() {
assert!(req.extensions().contains::<usize>()); assert!(req.extensions().contains::<usize>());
future::ok::<_, ()>(Response::Ok().finish()) future::ok::<_, ()>(Response::Ok().finish())
}) })
.tcp()
}); });
let response = srv.get("/").send().await.unwrap(); let response = srv.get("/").send().await.unwrap();

View File

@ -2,7 +2,7 @@ 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::TestServer; use actix_http_test::TestServer;
use actix_utils::framed::FramedTransport; use actix_utils::framed::FramedTransport;
use bytes::{Bytes, BytesMut}; use bytes::BytesMut;
use futures::future; use futures::future;
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
@ -40,6 +40,7 @@ async fn test_simple() {
HttpService::build() HttpService::build()
.upgrade(actix_service::service_fn(ws_service)) .upgrade(actix_service::service_fn(ws_service))
.finish(|_| future::ok::<_, ()>(Response::NotFound())) .finish(|_| future::ok::<_, ()>(Response::NotFound()))
.tcp()
}); });
// client service // client service
@ -61,7 +62,7 @@ async fn test_simple() {
let (item, mut framed) = framed.into_future().await; let (item, mut framed) = framed.into_future().await;
assert_eq!( assert_eq!(
item.unwrap().unwrap(), item.unwrap().unwrap(),
ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) ws::Frame::Binary(Some(BytesMut::from(&b"text"[..]).into()))
); );
framed.send(ws::Message::Ping("text".into())).await.unwrap(); framed.send(ws::Message::Ping("text".into())).await.unwrap();

View File

@ -17,14 +17,14 @@ name = "actix_identity"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
actix-web = { version = "2.0.0-alpha.1", default-features = false, features = ["secure-cookies"] } actix-web = { version = "2.0.0-alpha.2", default-features = false, features = ["secure-cookies"] }
actix-service = "1.0.0-alpha.1" actix-service = "1.0.0-alpha.2"
futures = "0.3.1" futures = "0.3.1"
serde = "1.0" serde = "1.0"
serde_json = "1.0" serde_json = "1.0"
time = "0.1.42" time = "0.1.42"
[dev-dependencies] [dev-dependencies]
actix-rt = "1.0.0-alpha.1" actix-rt = "1.0.0-alpha.2"
actix-http = "0.3.0-alpha.1" actix-http = "0.3.0-alpha.2"
bytes = "0.4" bytes = "0.5.2"

View File

@ -1,5 +1,9 @@
# Changes # Changes
## [0.2.0-alpha.2] - 2019-12-03
* Migrate to `std::future`
## [0.1.4] - 2019-09-12 ## [0.1.4] - 2019-09-12
* Multipart handling now parses requests which do not end in CRLF #1038 * Multipart handling now parses requests which do not end in CRLF #1038

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-multipart" name = "actix-multipart"
version = "0.2.0-alpha.1" version = "0.2.0-alpha.2"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Multipart support for actix web framework." description = "Multipart support for actix web framework."
readme = "README.md" readme = "README.md"
@ -18,10 +18,10 @@ name = "actix_multipart"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
actix-web = { version = "2.0.0-alpha.1", default-features = false } actix-web = { version = "2.0.0-alpha.2", default-features = false }
actix-service = "1.0.0-alpha.1" actix-service = "1.0.0-alpha.2"
actix-utils = "0.5.0-alpha.1" actix-utils = "1.0.0-alpha.2"
bytes = "0.4" bytes = "0.5.2"
derive_more = "0.99.2" derive_more = "0.99.2"
httparse = "1.3" httparse = "1.3"
futures = "0.3.1" futures = "0.3.1"
@ -31,5 +31,5 @@ time = "0.1"
twoway = "0.2" twoway = "0.2"
[dev-dependencies] [dev-dependencies]
actix-rt = "1.0.0-alpha.1" actix-rt = "1.0.0-alpha.2"
actix-http = "0.3.0-alpha.1" actix-http = "0.3.0-alpha.2"

View File

@ -5,4 +5,4 @@
* [API Documentation](https://docs.rs/actix-multipart/) * [API Documentation](https://docs.rs/actix-multipart/)
* [Chat on gitter](https://gitter.im/actix/actix) * [Chat on gitter](https://gitter.im/actix/actix)
* Cargo package: [actix-multipart](https://crates.io/crates/actix-multipart) * Cargo package: [actix-multipart](https://crates.io/crates/actix-multipart)
* Minimum supported Rust version: 1.33 or later * Minimum supported Rust version: 1.39 or later

View File

@ -1,5 +1,6 @@
//! Multipart payload support //! Multipart payload support
use std::cell::{Cell, RefCell, RefMut}; use std::cell::{Cell, RefCell, RefMut};
use std::convert::TryFrom;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
@ -16,7 +17,6 @@ use actix_web::error::{ParseError, PayloadError};
use actix_web::http::header::{ use actix_web::http::header::{
self, ContentDisposition, HeaderMap, HeaderName, HeaderValue, self, ContentDisposition, HeaderMap, HeaderName, HeaderValue,
}; };
use actix_web::http::HttpTryFrom;
use crate::error::MultipartError; use crate::error::MultipartError;
@ -582,7 +582,7 @@ impl InnerField {
} }
} }
} else { } else {
Poll::Ready(Some(Ok(payload.buf.take().freeze()))) Poll::Ready(Some(Ok(payload.buf.split().freeze())))
}; };
} }
} }
@ -792,7 +792,7 @@ impl PayloadBuffer {
pub fn readline_or_eof(&mut self) -> Result<Option<Bytes>, MultipartError> { pub fn readline_or_eof(&mut self) -> Result<Option<Bytes>, MultipartError> {
match self.readline() { match self.readline() {
Err(MultipartError::Incomplete) if self.eof => { Err(MultipartError::Incomplete) if self.eof => {
Ok(Some(self.buf.take().freeze())) Ok(Some(self.buf.split().freeze()))
} }
line => line, line => line,
} }
@ -800,7 +800,7 @@ impl PayloadBuffer {
/// Put unprocessed data back to the buffer /// Put unprocessed data back to the buffer
pub fn unprocessed(&mut self, data: Bytes) { pub fn unprocessed(&mut self, data: Bytes) {
let buf = BytesMut::from(data); let buf = BytesMut::from(data.as_ref());
let buf = std::mem::replace(&mut self.buf, buf); let buf = std::mem::replace(&mut self.buf, buf);
self.buf.extend_from_slice(&buf); self.buf.extend_from_slice(&buf);
} }
@ -893,8 +893,8 @@ mod tests {
#[actix_rt::test] #[actix_rt::test]
async fn test_multipart_no_end_crlf() { async fn test_multipart_no_end_crlf() {
let (sender, payload) = create_stream(); let (sender, payload) = create_stream();
let (bytes, headers) = create_simple_request_with_header(); let (mut bytes, headers) = create_simple_request_with_header();
let bytes_stripped = bytes.slice_to(bytes.len()); // strip crlf let bytes_stripped = bytes.split_to(bytes.len()); // strip crlf
sender.send(Ok(bytes_stripped)).unwrap(); sender.send(Ok(bytes_stripped)).unwrap();
drop(sender); // eof drop(sender); // eof

View File

@ -1,11 +1,19 @@
# Changes # Changes
## [0.3.0-alpha.3] - 2019-12-xx
* Add access to the session from RequestHead for use of session from guard methods
* Migrate to `std::future`
* Migrate to `actix-web` 2.0
## [0.2.0] - 2019-07-08 ## [0.2.0] - 2019-07-08
* Enhanced ``actix-session`` to facilitate state changes. Use ``Session.renew()`` * Enhanced ``actix-session`` to facilitate state changes. Use ``Session.renew()``
at successful login to cycle a session (new key/cookie but keeps state). at successful login to cycle a session (new key/cookie but keeps state).
Use ``Session.purge()`` at logout to invalid a session cookie (and remove Use ``Session.purge()`` at logout to invalid a session cookie (and remove
from redis cache, if applicable). from redis cache, if applicable).
## [0.1.1] - 2019-06-03 ## [0.1.1] - 2019-06-03

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-session" name = "actix-session"
version = "0.3.0-alpha.1" version = "0.3.0-alpha.3"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Session for actix web framework." description = "Session for actix web framework."
readme = "README.md" readme = "README.md"
@ -24,15 +24,14 @@ default = ["cookie-session"]
cookie-session = ["actix-web/secure-cookies"] cookie-session = ["actix-web/secure-cookies"]
[dependencies] [dependencies]
actix-web = "2.0.0-alpha.1" actix-web = "2.0.0-alpha.3"
actix-service = "1.0.0-alpha.1" actix-service = "1.0.0-alpha.3"
bytes = "0.4" bytes = "0.5.2"
derive_more = "0.99.2" derive_more = "0.99.2"
futures = "0.3.1" futures = "0.3.1"
hashbrown = "0.6.3"
serde = "1.0" serde = "1.0"
serde_json = "1.0" serde_json = "1.0"
time = "0.1.42" time = "0.1.42"
[dev-dependencies] [dev-dependencies]
actix-rt = "1.0.0-alpha.1" actix-rt = "1.0.0-alpha.3"

View File

@ -43,12 +43,14 @@
//! } //! }
//! ``` //! ```
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
use actix_web::dev::{Extensions, Payload, ServiceRequest, ServiceResponse}; use actix_web::dev::{
Extensions, Payload, RequestHead, ServiceRequest, ServiceResponse,
};
use actix_web::{Error, FromRequest, HttpMessage, HttpRequest}; use actix_web::{Error, FromRequest, HttpMessage, HttpRequest};
use futures::future::{ok, Ready}; use futures::future::{ok, Ready};
use hashbrown::HashMap;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize; use serde::Serialize;
use serde_json; use serde_json;
@ -99,6 +101,12 @@ impl UserSession for ServiceRequest {
} }
} }
impl UserSession for RequestHead {
fn get_session(&mut self) -> Session {
Session::get_session(&mut *self.extensions_mut())
}
}
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
pub enum SessionStatus { pub enum SessionStatus {
Changed, Changed,
@ -281,6 +289,20 @@ mod tests {
assert_eq!(res, Some("value".to_string())); assert_eq!(res, Some("value".to_string()));
} }
#[test]
fn get_session_from_request_head() {
let mut req = test::TestRequest::default().to_srv_request();
Session::set_session(
vec![("key".to_string(), "\"value\"".to_string())].into_iter(),
&mut req,
);
let session = req.head_mut().get_session();
let res = session.get::<String>("key").unwrap();
assert_eq!(res, Some("value".to_string()));
}
#[test] #[test]
fn purge_session() { fn purge_session() {
let req = test::TestRequest::default().to_srv_request(); let req = test::TestRequest::default().to_srv_request();

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-web-codegen" name = "actix-web-codegen"
version = "0.2.0-alpha.1" version = "0.2.0-alpha.2"
description = "Actix web proc macros" description = "Actix web proc macros"
readme = "README.md" readme = "README.md"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
@ -17,8 +17,8 @@ syn = { version = "^1", features = ["full", "parsing"] }
proc-macro2 = "^1" proc-macro2 = "^1"
[dev-dependencies] [dev-dependencies]
actix-rt = { version = "1.0.0-alpha.1" } actix-rt = { version = "1.0.0-alpha.2" }
actix-web = { version = "2.0.0-alpha.1" } actix-web = { version = "2.0.0-alpha.2" }
actix-http = { version = "0.3.0-alpha.1", features=["openssl"] } actix-http = { version = "0.3.0-alpha.2", features=["openssl"] }
actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } actix-http-test = { version = "0.3.0-alpha.2", features=["openssl"] }
futures = { version = "0.3.1" } futures = { version = "0.3.1" }

View File

@ -78,6 +78,7 @@ async fn test_params() {
.service(put_param_test) .service(put_param_test)
.service(delete_param_test), .service(delete_param_test),
) )
.tcp()
}); });
let request = srv.request(http::Method::GET, srv.url("/test/it")); let request = srv.request(http::Method::GET, srv.url("/test/it"));
@ -107,6 +108,7 @@ async fn test_body() {
.service(patch_test) .service(patch_test)
.service(test), .service(test),
) )
.tcp()
}); });
let request = srv.request(http::Method::GET, srv.url("/test")); let request = srv.request(http::Method::GET, srv.url("/test"));
let response = request.send().await.unwrap(); let response = request.send().await.unwrap();
@ -149,7 +151,8 @@ async fn test_body() {
#[actix_rt::test] #[actix_rt::test]
async fn test_auto_async() { async fn test_auto_async() {
let srv = TestServer::start(|| HttpService::new(App::new().service(auto_async))); let srv =
TestServer::start(|| HttpService::new(App::new().service(auto_async)).tcp());
let request = srv.request(http::Method::GET, srv.url("/test")); let request = srv.request(http::Method::GET, srv.url("/test"));
let response = request.send().await.unwrap(); let response = request.send().await.unwrap();

View File

@ -1,6 +1,6 @@
[package] [package]
name = "awc" name = "awc"
version = "0.3.0-alpha.1" version = "0.3.0-alpha.3"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix http client." description = "Actix http client."
readme = "README.md" readme = "README.md"
@ -21,7 +21,7 @@ name = "awc"
path = "src/lib.rs" path = "src/lib.rs"
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = ["openssl", "brotli", "flate2-zlib"] features = ["openssl", "rustls", "brotli", "flate2-zlib"]
[features] [features]
default = ["brotli", "flate2-zlib"] default = ["brotli", "flate2-zlib"]
@ -30,7 +30,7 @@ default = ["brotli", "flate2-zlib"]
openssl = ["open-ssl", "actix-http/openssl"] openssl = ["open-ssl", "actix-http/openssl"]
# rustls # rustls
# rustls = ["rust-tls", "actix-http/rustls"] rustls = ["rust-tls", "actix-http/rustls"]
# brotli encoding, requires c compiler # brotli encoding, requires c compiler
brotli = ["actix-http/brotli"] brotli = ["actix-http/brotli"]
@ -42,13 +42,13 @@ flate2-zlib = ["actix-http/flate2-zlib"]
flate2-rust = ["actix-http/flate2-rust"] flate2-rust = ["actix-http/flate2-rust"]
[dependencies] [dependencies]
actix-codec = "0.2.0-alpha.1" actix-codec = "0.2.0-alpha.3"
actix-service = "1.0.0-alpha.1" actix-service = "1.0.0-alpha.3"
actix-http = "0.3.0-alpha.1" actix-http = "0.3.0-alpha.3"
actix-rt = "1.0.0-alpha.1" actix-rt = "1.0.0-alpha.3"
base64 = "0.10.1" base64 = "0.11"
bytes = "0.4" bytes = "0.5.2"
derive_more = "0.99.2" derive_more = "0.99.2"
futures = "0.3.1" futures = "0.3.1"
log =" 0.4" log =" 0.4"
@ -59,17 +59,17 @@ serde = "1.0"
serde_json = "1.0" serde_json = "1.0"
serde_urlencoded = "0.6.1" serde_urlencoded = "0.6.1"
open-ssl = { version="0.10", package="openssl", optional = true } open-ssl = { version="0.10", package="openssl", optional = true }
# rust-tls = { version = "0.16.0", package="rustls", optional = true, features = ["dangerous_configuration"] } rust-tls = { version = "0.16.0", package="rustls", optional = true, features = ["dangerous_configuration"] }
[dev-dependencies] [dev-dependencies]
actix-connect = { version = "1.0.0-alpha.1", features=["openssl"] } actix-connect = { version = "1.0.0-alpha.3", features=["openssl"] }
actix-web = { version = "2.0.0-alpha.1", features=["openssl"] } actix-web = { version = "2.0.0-alpha.3", features=["openssl"] }
actix-http = { version = "0.3.0-alpha.1", features=["openssl"] } actix-http = { version = "0.3.0-alpha.3", features=["openssl"] }
actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } actix-http-test = { version = "0.3.0-alpha.3", features=["openssl"] }
actix-utils = "0.5.0-alpha.1" actix-utils = "1.0.0-alpha.3"
actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } actix-server = { version = "1.0.0-alpha.3" }
actix-tls = { version = "1.0.0-alpha.3", features=["openssl", "rustls"] }
brotli2 = { version="0.3.2" } brotli2 = { version="0.3.2" }
flate2 = { version="1.0.2" } flate2 = { version="1.0.2" }
env_logger = "0.6" env_logger = "0.6"
rand = "0.7"
webpki = { version = "0.21" } webpki = { version = "0.21" }

View File

@ -1,10 +1,11 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::convert::TryFrom;
use std::fmt; use std::fmt;
use std::rc::Rc; use std::rc::Rc;
use std::time::Duration; use std::time::Duration;
use actix_http::client::{Connect, ConnectError, Connection, Connector}; use actix_http::client::{Connect, ConnectError, Connection, Connector};
use actix_http::http::{header, HeaderMap, HeaderName, HttpTryFrom}; use actix_http::http::{header, Error as HttpError, HeaderMap, HeaderName};
use actix_service::Service; use actix_service::Service;
use crate::connect::ConnectorWrapper; use crate::connect::ConnectorWrapper;
@ -97,8 +98,8 @@ impl ClientBuilder {
/// get added to every request. /// get added to every request.
pub fn header<K, V>(mut self, key: K, value: V) -> Self pub fn header<K, V>(mut self, key: K, value: V) -> Self
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as HttpTryFrom<K>>::Error: fmt::Debug, <HeaderName as TryFrom<K>>::Error: fmt::Debug + Into<HttpError>,
V: header::IntoHeaderValue, V: header::IntoHeaderValue,
V::Error: fmt::Debug, V::Error: fmt::Debug,
{ {

View File

@ -1,7 +1,7 @@
use std::pin::Pin; use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::{fmt, io, net}; use std::{fmt, io, mem, net};
use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_http::body::Body; use actix_http::body::Body;
@ -201,7 +201,10 @@ impl fmt::Debug for BoxedSocket {
} }
impl AsyncRead for BoxedSocket { impl AsyncRead for BoxedSocket {
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { unsafe fn prepare_uninitialized_buffer(
&self,
buf: &mut [mem::MaybeUninit<u8>],
) -> bool {
self.0.as_read().prepare_uninitialized_buffer(buf) self.0.as_read().prepare_uninitialized_buffer(buf)
} }

View File

@ -3,13 +3,14 @@ pub use actix_http::client::{
ConnectError, FreezeRequestError, InvalidUrl, SendRequestError, ConnectError, FreezeRequestError, InvalidUrl, SendRequestError,
}; };
pub use actix_http::error::PayloadError; pub use actix_http::error::PayloadError;
pub use actix_http::http::Error as HttpError;
pub use actix_http::ws::HandshakeError as WsHandshakeError; pub use actix_http::ws::HandshakeError as WsHandshakeError;
pub use actix_http::ws::ProtocolError as WsProtocolError; pub use actix_http::ws::ProtocolError as WsProtocolError;
use actix_http::ResponseError; use actix_http::ResponseError;
use serde_json::error::Error as JsonError; use serde_json::error::Error as JsonError;
use actix_http::http::{header::HeaderValue, Error as HttpError, StatusCode}; use actix_http::http::{header::HeaderValue, StatusCode};
use derive_more::{Display, From}; use derive_more::{Display, From};
/// Websocket client error /// Websocket client error

View File

@ -1,3 +1,4 @@
use std::convert::TryFrom;
use std::net; use std::net;
use std::rc::Rc; use std::rc::Rc;
use std::time::Duration; use std::time::Duration;
@ -8,9 +9,7 @@ use serde::Serialize;
use actix_http::body::Body; use actix_http::body::Body;
use actix_http::http::header::IntoHeaderValue; use actix_http::http::header::IntoHeaderValue;
use actix_http::http::{ use actix_http::http::{Error as HttpError, HeaderMap, HeaderName, Method, Uri};
Error as HttpError, HeaderMap, HeaderName, HttpTryFrom, Method, Uri,
};
use actix_http::{Error, RequestHead}; use actix_http::{Error, RequestHead};
use crate::sender::{RequestSender, SendClientRequest}; use crate::sender::{RequestSender, SendClientRequest};
@ -112,7 +111,8 @@ impl FrozenClientRequest {
/// Create a `FrozenSendBuilder` with an extra header /// Create a `FrozenSendBuilder` with an extra header
pub fn extra_header<K, V>(&self, key: K, value: V) -> FrozenSendBuilder pub fn extra_header<K, V>(&self, key: K, value: V) -> FrozenSendBuilder
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
self.extra_headers(HeaderMap::new()) self.extra_headers(HeaderMap::new())
@ -139,7 +139,8 @@ impl FrozenSendBuilder {
/// Insert a header, it overrides existing header in `FrozenClientRequest`. /// Insert a header, it overrides existing header in `FrozenClientRequest`.
pub fn extra_header<K, V>(mut self, key: K, value: V) -> Self pub fn extra_header<K, V>(mut self, key: K, value: V) -> Self
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
match HeaderName::try_from(key) { match HeaderName::try_from(key) {

View File

@ -19,12 +19,13 @@
//! } //! }
//! ``` //! ```
use std::cell::RefCell; use std::cell::RefCell;
use std::convert::TryFrom;
use std::rc::Rc; use std::rc::Rc;
use std::time::Duration; use std::time::Duration;
pub use actix_http::{client::Connector, cookie, http}; pub use actix_http::{client::Connector, cookie, http};
use actix_http::http::{HeaderMap, HttpTryFrom, Method, Uri}; use actix_http::http::{Error as HttpError, HeaderMap, Method, Uri};
use actix_http::RequestHead; use actix_http::RequestHead;
mod builder; mod builder;
@ -102,7 +103,8 @@ impl Client {
/// Construct HTTP request. /// Construct HTTP request.
pub fn request<U>(&self, method: Method, url: U) -> ClientRequest pub fn request<U>(&self, method: Method, url: U) -> ClientRequest
where where
Uri: HttpTryFrom<U>, Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<HttpError>,
{ {
let mut req = ClientRequest::new(method, url, self.0.clone()); let mut req = ClientRequest::new(method, url, self.0.clone());
@ -118,7 +120,8 @@ impl Client {
/// copies all headers and the method. /// copies all headers and the method.
pub fn request_from<U>(&self, url: U, head: &RequestHead) -> ClientRequest pub fn request_from<U>(&self, url: U, head: &RequestHead) -> ClientRequest
where where
Uri: HttpTryFrom<U>, Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<HttpError>,
{ {
let mut req = self.request(head.method.clone(), url); let mut req = self.request(head.method.clone(), url);
for (key, value) in head.headers.iter() { for (key, value) in head.headers.iter() {
@ -130,7 +133,8 @@ impl Client {
/// Construct HTTP *GET* request. /// Construct HTTP *GET* request.
pub fn get<U>(&self, url: U) -> ClientRequest pub fn get<U>(&self, url: U) -> ClientRequest
where where
Uri: HttpTryFrom<U>, Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<HttpError>,
{ {
self.request(Method::GET, url) self.request(Method::GET, url)
} }
@ -138,7 +142,8 @@ impl Client {
/// Construct HTTP *HEAD* request. /// Construct HTTP *HEAD* request.
pub fn head<U>(&self, url: U) -> ClientRequest pub fn head<U>(&self, url: U) -> ClientRequest
where where
Uri: HttpTryFrom<U>, Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<HttpError>,
{ {
self.request(Method::HEAD, url) self.request(Method::HEAD, url)
} }
@ -146,7 +151,8 @@ impl Client {
/// Construct HTTP *PUT* request. /// Construct HTTP *PUT* request.
pub fn put<U>(&self, url: U) -> ClientRequest pub fn put<U>(&self, url: U) -> ClientRequest
where where
Uri: HttpTryFrom<U>, Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<HttpError>,
{ {
self.request(Method::PUT, url) self.request(Method::PUT, url)
} }
@ -154,7 +160,8 @@ impl Client {
/// Construct HTTP *POST* request. /// Construct HTTP *POST* request.
pub fn post<U>(&self, url: U) -> ClientRequest pub fn post<U>(&self, url: U) -> ClientRequest
where where
Uri: HttpTryFrom<U>, Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<HttpError>,
{ {
self.request(Method::POST, url) self.request(Method::POST, url)
} }
@ -162,7 +169,8 @@ impl Client {
/// Construct HTTP *PATCH* request. /// Construct HTTP *PATCH* request.
pub fn patch<U>(&self, url: U) -> ClientRequest pub fn patch<U>(&self, url: U) -> ClientRequest
where where
Uri: HttpTryFrom<U>, Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<HttpError>,
{ {
self.request(Method::PATCH, url) self.request(Method::PATCH, url)
} }
@ -170,7 +178,8 @@ impl Client {
/// Construct HTTP *DELETE* request. /// Construct HTTP *DELETE* request.
pub fn delete<U>(&self, url: U) -> ClientRequest pub fn delete<U>(&self, url: U) -> ClientRequest
where where
Uri: HttpTryFrom<U>, Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<HttpError>,
{ {
self.request(Method::DELETE, url) self.request(Method::DELETE, url)
} }
@ -178,7 +187,8 @@ impl Client {
/// Construct HTTP *OPTIONS* request. /// Construct HTTP *OPTIONS* request.
pub fn options<U>(&self, url: U) -> ClientRequest pub fn options<U>(&self, url: U) -> ClientRequest
where where
Uri: HttpTryFrom<U>, Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<HttpError>,
{ {
self.request(Method::OPTIONS, url) self.request(Method::OPTIONS, url)
} }
@ -186,7 +196,8 @@ impl Client {
/// Construct WebSockets request. /// Construct WebSockets request.
pub fn ws<U>(&self, url: U) -> ws::WebsocketsRequest pub fn ws<U>(&self, url: U) -> ws::WebsocketsRequest
where where
Uri: HttpTryFrom<U>, Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<HttpError>,
{ {
let mut req = ws::WebsocketsRequest::new(url, self.0.clone()); let mut req = ws::WebsocketsRequest::new(url, self.0.clone());
for (key, value) in self.0.headers.iter() { for (key, value) in self.0.headers.iter() {

View File

@ -1,10 +1,10 @@
use std::convert::TryFrom;
use std::fmt::Write as FmtWrite; use std::fmt::Write as FmtWrite;
use std::io::Write;
use std::rc::Rc; use std::rc::Rc;
use std::time::Duration; use std::time::Duration;
use std::{fmt, net}; use std::{fmt, net};
use bytes::{BufMut, Bytes, BytesMut}; use bytes::Bytes;
use futures::Stream; use futures::Stream;
use percent_encoding::percent_encode; use percent_encoding::percent_encode;
use serde::Serialize; use serde::Serialize;
@ -13,8 +13,8 @@ use actix_http::body::Body;
use actix_http::cookie::{Cookie, CookieJar, USERINFO}; use actix_http::cookie::{Cookie, CookieJar, USERINFO};
use actix_http::http::header::{self, Header, IntoHeaderValue}; use actix_http::http::header::{self, Header, IntoHeaderValue};
use actix_http::http::{ use actix_http::http::{
uri, ConnectionType, Error as HttpError, HeaderMap, HeaderName, HeaderValue, uri, ConnectionType, Error as HttpError, HeaderMap, HeaderName, HeaderValue, Method,
HttpTryFrom, Method, Uri, Version, Uri, Version,
}; };
use actix_http::{Error, RequestHead}; use actix_http::{Error, RequestHead};
@ -67,7 +67,8 @@ impl ClientRequest {
/// Create new client request builder. /// Create new client request builder.
pub(crate) fn new<U>(method: Method, uri: U, config: Rc<ClientConfig>) -> Self pub(crate) fn new<U>(method: Method, uri: U, config: Rc<ClientConfig>) -> Self
where where
Uri: HttpTryFrom<U>, Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<HttpError>,
{ {
ClientRequest { ClientRequest {
config, config,
@ -86,7 +87,8 @@ impl ClientRequest {
#[inline] #[inline]
pub fn uri<U>(mut self, uri: U) -> Self pub fn uri<U>(mut self, uri: U) -> Self
where where
Uri: HttpTryFrom<U>, Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<HttpError>,
{ {
match Uri::try_from(uri) { match Uri::try_from(uri) {
Ok(uri) => self.head.uri = uri, Ok(uri) => self.head.uri = uri,
@ -196,7 +198,8 @@ impl ClientRequest {
/// ``` /// ```
pub fn header<K, V>(mut self, key: K, value: V) -> Self pub fn header<K, V>(mut self, key: K, value: V) -> Self
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
match HeaderName::try_from(key) { match HeaderName::try_from(key) {
@ -212,7 +215,8 @@ impl ClientRequest {
/// Insert a header, replaces existing header. /// Insert a header, replaces existing header.
pub fn set_header<K, V>(mut self, key: K, value: V) -> Self pub fn set_header<K, V>(mut self, key: K, value: V) -> Self
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
match HeaderName::try_from(key) { match HeaderName::try_from(key) {
@ -228,7 +232,8 @@ impl ClientRequest {
/// Insert a header only if it is not yet set. /// Insert a header only if it is not yet set.
pub fn set_header_if_none<K, V>(mut self, key: K, value: V) -> Self pub fn set_header_if_none<K, V>(mut self, key: K, value: V) -> Self
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
match HeaderName::try_from(key) { match HeaderName::try_from(key) {
@ -264,7 +269,8 @@ impl ClientRequest {
#[inline] #[inline]
pub fn content_type<V>(mut self, value: V) -> Self pub fn content_type<V>(mut self, value: V) -> Self
where where
HeaderValue: HttpTryFrom<V>, HeaderValue: TryFrom<V>,
<HeaderValue as TryFrom<V>>::Error: Into<HttpError>,
{ {
match HeaderValue::try_from(value) { match HeaderValue::try_from(value) {
Ok(value) => self.head.headers.insert(header::CONTENT_TYPE, value), Ok(value) => self.head.headers.insert(header::CONTENT_TYPE, value),
@ -276,9 +282,7 @@ impl ClientRequest {
/// Set content length /// Set content length
#[inline] #[inline]
pub fn content_length(self, len: u64) -> Self { pub fn content_length(self, len: u64) -> Self {
let mut wrt = BytesMut::new().writer(); self.header(header::CONTENT_LENGTH, len)
let _ = write!(wrt, "{}", len);
self.header(header::CONTENT_LENGTH, wrt.get_mut().take().freeze())
} }
/// Set HTTP basic authorization header /// Set HTTP basic authorization header
@ -513,9 +517,9 @@ impl ClientRequest {
let uri = &self.head.uri; let uri = &self.head.uri;
if uri.host().is_none() { if uri.host().is_none() {
return Err(InvalidUrl::MissingHost.into()); return Err(InvalidUrl::MissingHost.into());
} else if uri.scheme_part().is_none() { } else if uri.scheme().is_none() {
return Err(InvalidUrl::MissingScheme.into()); return Err(InvalidUrl::MissingScheme.into());
} else if let Some(scheme) = uri.scheme_part() { } else if let Some(scheme) = uri.scheme() {
match scheme.as_str() { match scheme.as_str() {
"http" | "ws" | "https" | "wss" => (), "http" | "ws" | "https" | "wss" => (),
_ => return Err(InvalidUrl::UnknownScheme.into()), _ => return Err(InvalidUrl::UnknownScheme.into()),
@ -551,7 +555,7 @@ impl ClientRequest {
let https = slf let https = slf
.head .head
.uri .uri
.scheme_part() .scheme()
.map(|s| s == &uri::Scheme::HTTPS) .map(|s| s == &uri::Scheme::HTTPS)
.unwrap_or(true); .unwrap_or(true);

View File

@ -348,7 +348,7 @@ where
continue; continue;
} }
} }
Poll::Ready(None) => Poll::Ready(Ok(this.buf.take().freeze())), Poll::Ready(None) => Poll::Ready(Ok(this.buf.split().freeze())),
Poll::Pending => Poll::Pending, Poll::Pending => Poll::Pending,
}; };
} }

View File

@ -1,9 +1,10 @@
//! 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 std::fmt::Write as FmtWrite; use std::fmt::Write as FmtWrite;
use actix_http::cookie::{Cookie, CookieJar, USERINFO}; use actix_http::cookie::{Cookie, CookieJar, USERINFO};
use actix_http::http::header::{self, Header, HeaderValue, IntoHeaderValue}; use actix_http::http::header::{self, Header, HeaderValue, IntoHeaderValue};
use actix_http::http::{HeaderName, HttpTryFrom, StatusCode, Version}; use actix_http::http::{Error as HttpError, HeaderName, StatusCode, Version};
use actix_http::{h1, Payload, ResponseHead}; use actix_http::{h1, Payload, ResponseHead};
use bytes::Bytes; use bytes::Bytes;
use percent_encoding::percent_encode; use percent_encoding::percent_encode;
@ -31,7 +32,8 @@ 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<K, V>(key: K, value: V) -> Self
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
Self::default().header(key, value) Self::default().header(key, value)
@ -55,7 +57,8 @@ impl TestResponse {
/// Append a header /// Append a header
pub fn header<K, V>(mut self, key: K, value: V) -> Self pub fn header<K, V>(mut self, key: K, value: V) -> Self
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
if let Ok(key) = HeaderName::try_from(key) { if let Ok(key) = HeaderName::try_from(key) {

View File

@ -1,4 +1,5 @@
//! Websockets client //! Websockets client
use std::convert::TryFrom;
use std::fmt::Write as FmtWrite; use std::fmt::Write as FmtWrite;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::rc::Rc; use std::rc::Rc;
@ -7,7 +8,7 @@ use std::{fmt, str};
use actix_codec::Framed; use actix_codec::Framed;
use actix_http::cookie::{Cookie, CookieJar}; use actix_http::cookie::{Cookie, CookieJar};
use actix_http::{ws, Payload, RequestHead}; use actix_http::{ws, Payload, RequestHead};
use actix_rt::time::Timeout; use actix_rt::time::timeout;
use percent_encoding::percent_encode; use percent_encoding::percent_encode;
use actix_http::cookie::USERINFO; use actix_http::cookie::USERINFO;
@ -19,7 +20,7 @@ use crate::http::header::{
self, HeaderName, HeaderValue, IntoHeaderValue, AUTHORIZATION, self, HeaderName, HeaderValue, IntoHeaderValue, AUTHORIZATION,
}; };
use crate::http::{ use crate::http::{
ConnectionType, Error as HttpError, HttpTryFrom, Method, StatusCode, Uri, Version, ConnectionType, Error as HttpError, Method, StatusCode, Uri, Version,
}; };
use crate::response::ClientResponse; use crate::response::ClientResponse;
use crate::ClientConfig; use crate::ClientConfig;
@ -41,7 +42,8 @@ impl WebsocketsRequest {
/// Create new websocket connection /// Create new websocket connection
pub(crate) fn new<U>(uri: U, config: Rc<ClientConfig>) -> Self pub(crate) fn new<U>(uri: U, config: Rc<ClientConfig>) -> Self
where where
Uri: HttpTryFrom<U>, Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<HttpError>,
{ {
let mut err = None; let mut err = None;
let mut head = RequestHead::default(); let mut head = RequestHead::default();
@ -102,9 +104,10 @@ impl WebsocketsRequest {
} }
/// Set request Origin /// Set request Origin
pub fn origin<V>(mut self, origin: V) -> Self pub fn origin<V, E>(mut self, origin: V) -> Self
where where
HeaderValue: HttpTryFrom<V>, HeaderValue: TryFrom<V, Error = E>,
HttpError: From<E>,
{ {
match HeaderValue::try_from(origin) { match HeaderValue::try_from(origin) {
Ok(value) => self.origin = Some(value), Ok(value) => self.origin = Some(value),
@ -133,7 +136,8 @@ impl WebsocketsRequest {
/// To override header use `set_header()` method. /// To override header use `set_header()` method.
pub fn header<K, V>(mut self, key: K, value: V) -> Self pub fn header<K, V>(mut self, key: K, value: V) -> Self
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
match HeaderName::try_from(key) { match HeaderName::try_from(key) {
@ -151,7 +155,8 @@ impl WebsocketsRequest {
/// Insert a header, replaces existing header. /// Insert a header, replaces existing header.
pub fn set_header<K, V>(mut self, key: K, value: V) -> Self pub fn set_header<K, V>(mut self, key: K, value: V) -> Self
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
match HeaderName::try_from(key) { match HeaderName::try_from(key) {
@ -169,7 +174,8 @@ impl WebsocketsRequest {
/// Insert a header only if it is not yet set. /// Insert a header only if it is not yet set.
pub fn set_header_if_none<K, V>(mut self, key: K, value: V) -> Self pub fn set_header_if_none<K, V>(mut self, key: K, value: V) -> Self
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
match HeaderName::try_from(key) { match HeaderName::try_from(key) {
@ -220,9 +226,9 @@ impl WebsocketsRequest {
let uri = &self.head.uri; let uri = &self.head.uri;
if uri.host().is_none() { if uri.host().is_none() {
return Err(InvalidUrl::MissingHost.into()); return Err(InvalidUrl::MissingHost.into());
} else if uri.scheme_part().is_none() { } else if uri.scheme().is_none() {
return Err(InvalidUrl::MissingScheme.into()); return Err(InvalidUrl::MissingScheme.into());
} else if let Some(scheme) = uri.scheme_part() { } else if let Some(scheme) = uri.scheme() {
match scheme.as_str() { match scheme.as_str() {
"http" | "ws" | "https" | "wss" => (), "http" | "ws" | "https" | "wss" => (),
_ => return Err(InvalidUrl::UnknownScheme.into()), _ => return Err(InvalidUrl::UnknownScheme.into()),
@ -295,8 +301,8 @@ impl WebsocketsRequest {
.open_tunnel(head, self.addr); .open_tunnel(head, self.addr);
// set request timeout // set request timeout
let (head, framed) = if let Some(timeout) = self.config.timeout { let (head, framed) = if let Some(to) = self.config.timeout {
Timeout::new(fut, timeout) timeout(to, fut)
.await .await
.map_err(|_| SendRequestError::Timeout.into()) .map_err(|_| SendRequestError::Timeout.into())
.and_then(|res| res)? .and_then(|res| res)?

View File

@ -49,6 +49,7 @@ async fn test_simple() {
HttpService::new(App::new().service( HttpService::new(App::new().service(
web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))),
)) ))
.tcp()
}); });
let request = srv.get("/").header("x-test", "111").send(); let request = srv.get("/").header("x-test", "111").send();
@ -77,6 +78,7 @@ async fn test_json() {
HttpService::new(App::new().service( HttpService::new(App::new().service(
web::resource("/").route(web::to(|_: web::Json<String>| HttpResponse::Ok())), web::resource("/").route(web::to(|_: web::Json<String>| HttpResponse::Ok())),
)) ))
.tcp()
}); });
let request = srv let request = srv
@ -93,6 +95,7 @@ async fn test_form() {
HttpService::new(App::new().service(web::resource("/").route(web::to( HttpService::new(App::new().service(web::resource("/").route(web::to(
|_: web::Form<HashMap<String, String>>| HttpResponse::Ok(), |_: web::Form<HashMap<String, String>>| HttpResponse::Ok(),
)))) ))))
.tcp()
}); });
let mut data = HashMap::new(); let mut data = HashMap::new();
@ -112,6 +115,7 @@ async fn test_timeout() {
Ok::<_, Error>(HttpResponse::Ok().body(STR)) Ok::<_, Error>(HttpResponse::Ok().body(STR))
} }
})))) }))))
.tcp()
}); });
let connector = awc::Connector::new() let connector = awc::Connector::new()
@ -142,6 +146,7 @@ async fn test_timeout_override() {
Ok::<_, Error>(HttpResponse::Ok().body(STR)) Ok::<_, Error>(HttpResponse::Ok().body(STR))
} }
})))) }))))
.tcp()
}); });
let client = awc::Client::build() let client = awc::Client::build()
@ -168,9 +173,13 @@ async fn test_connection_reuse() {
num2.fetch_add(1, Ordering::Relaxed); num2.fetch_add(1, Ordering::Relaxed);
ok(io) ok(io)
}) })
.and_then(HttpService::new( .and_then(
App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), HttpService::new(
)) App::new()
.service(web::resource("/").route(web::to(|| HttpResponse::Ok()))),
)
.tcp(),
)
}); });
let client = awc::Client::default(); let client = awc::Client::default();
@ -200,9 +209,13 @@ async fn test_connection_force_close() {
num2.fetch_add(1, Ordering::Relaxed); num2.fetch_add(1, Ordering::Relaxed);
ok(io) ok(io)
}) })
.and_then(HttpService::new( .and_then(
App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), HttpService::new(
)) App::new()
.service(web::resource("/").route(web::to(|| HttpResponse::Ok()))),
)
.tcp(),
)
}); });
let client = awc::Client::default(); let client = awc::Client::default();
@ -232,12 +245,15 @@ async fn test_connection_server_close() {
num2.fetch_add(1, Ordering::Relaxed); num2.fetch_add(1, Ordering::Relaxed);
ok(io) ok(io)
}) })
.and_then(HttpService::new( .and_then(
App::new().service( HttpService::new(
web::resource("/") App::new().service(
.route(web::to(|| HttpResponse::Ok().force_close().finish())), web::resource("/")
), .route(web::to(|| HttpResponse::Ok().force_close().finish())),
)) ),
)
.tcp(),
)
}); });
let client = awc::Client::default(); let client = awc::Client::default();
@ -267,9 +283,12 @@ async fn test_connection_wait_queue() {
num2.fetch_add(1, Ordering::Relaxed); num2.fetch_add(1, Ordering::Relaxed);
ok(io) ok(io)
}) })
.and_then(HttpService::new(App::new().service( .and_then(
web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), HttpService::new(App::new().service(
))) web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))),
))
.tcp(),
)
}); });
let client = awc::Client::build() let client = awc::Client::build()
@ -308,12 +327,15 @@ async fn test_connection_wait_queue_force_close() {
num2.fetch_add(1, Ordering::Relaxed); num2.fetch_add(1, Ordering::Relaxed);
ok(io) ok(io)
}) })
.and_then(HttpService::new( .and_then(
App::new().service( HttpService::new(
web::resource("/") App::new().service(
.route(web::to(|| HttpResponse::Ok().force_close().body(STR))), web::resource("/")
), .route(web::to(|| HttpResponse::Ok().force_close().body(STR))),
)) ),
)
.tcp(),
)
}); });
let client = awc::Client::build() let client = awc::Client::build()
@ -353,6 +375,7 @@ async fn test_with_query_parameter() {
} }
}, },
))) )))
.tcp()
}); });
let res = awc::Client::new() let res = awc::Client::new()
@ -373,6 +396,7 @@ async fn test_no_decompress() {
res res
})), })),
)) ))
.tcp()
}); });
let mut res = awc::Client::new() let mut res = awc::Client::new()
@ -419,6 +443,7 @@ async fn test_client_gzip_encoding() {
.header("content-encoding", "gzip") .header("content-encoding", "gzip")
.body(data) .body(data)
})))) }))))
.tcp()
}); });
// client request // client request
@ -442,6 +467,7 @@ async fn test_client_gzip_encoding_large() {
.header("content-encoding", "gzip") .header("content-encoding", "gzip")
.body(data) .body(data)
})))) }))))
.tcp()
}); });
// client request // client request
@ -471,6 +497,7 @@ async fn test_client_gzip_encoding_large_random() {
.body(data) .body(data)
}, },
)))) ))))
.tcp()
}); });
// client request // client request
@ -495,6 +522,7 @@ async fn test_client_brotli_encoding() {
.body(data) .body(data)
}, },
)))) ))))
.tcp()
}); });
// client request // client request
@ -707,6 +735,7 @@ async fn test_client_cookie_handling() {
} }
}), }),
)) ))
.tcp()
}); });
let request = srv.get("/").cookie(cookie1.clone()).cookie(cookie2.clone()); let request = srv.get("/").cookie(cookie1.clone()).cookie(cookie2.clone());
@ -768,6 +797,7 @@ async fn client_basic_auth() {
} }
}), }),
)) ))
.tcp()
}); });
// set authorization header to Basic <base64 encoded username:password> // set authorization header to Basic <base64 encoded username:password>
@ -796,6 +826,7 @@ async fn client_bearer_auth() {
} }
}), }),
)) ))
.tcp()
}); });
// set authorization header to Bearer <token> // set authorization header to Bearer <token>

View File

@ -1,21 +1,17 @@
#![cfg(feature = "rustls")] #![cfg(feature = "rustls")]
use rust_tls::ClientConfig;
use std::io::Result;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use actix_codec::{AsyncRead, AsyncWrite};
use actix_http::HttpService; use actix_http::HttpService;
use actix_http_test::TestServer; use actix_http_test::TestServer;
use actix_server::ssl::OpensslAcceptor;
use actix_service::{pipeline_factory, ServiceFactory}; use actix_service::{pipeline_factory, ServiceFactory};
use actix_web::http::Version; use actix_web::http::Version;
use actix_web::{web, App, HttpResponse}; use actix_web::{web, App, HttpResponse};
use futures::future::ok; use futures::future::ok;
use open_ssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslVerifyMode}; use open_ssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslVerifyMode};
use rust_tls::ClientConfig;
fn ssl_acceptor<T: AsyncRead + AsyncWrite>() -> Result<OpensslAcceptor<T, ()>> { fn ssl_acceptor() -> SslAcceptor {
// load ssl keys // load ssl keys
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
builder.set_verify_callback(SslVerifyMode::NONE, |_, _| true); builder.set_verify_callback(SslVerifyMode::NONE, |_, _| true);
@ -33,8 +29,8 @@ fn ssl_acceptor<T: AsyncRead + AsyncWrite>() -> Result<OpensslAcceptor<T, ()>> {
Err(open_ssl::ssl::AlpnError::NOACK) Err(open_ssl::ssl::AlpnError::NOACK)
} }
}); });
builder.set_alpn_protos(b"\x02h2")?; builder.set_alpn_protos(b"\x02h2").unwrap();
Ok(actix_server::ssl::OpensslAcceptor::new(builder.build())) builder.build()
} }
mod danger { mod danger {
@ -55,7 +51,6 @@ mod danger {
// #[actix_rt::test] // #[actix_rt::test]
async fn _test_connection_reuse_h2() { async fn _test_connection_reuse_h2() {
let openssl = ssl_acceptor().unwrap();
let num = Arc::new(AtomicUsize::new(0)); let num = Arc::new(AtomicUsize::new(0));
let num2 = num.clone(); let num2 = num.clone();
@ -65,15 +60,11 @@ async fn _test_connection_reuse_h2() {
num2.fetch_add(1, Ordering::Relaxed); num2.fetch_add(1, Ordering::Relaxed);
ok(io) ok(io)
}) })
.and_then(
openssl
.clone()
.map_err(|e| println!("Openssl error: {}", e)),
)
.and_then( .and_then(
HttpService::build() HttpService::build()
.h2(App::new() .h2(App::new()
.service(web::resource("/").route(web::to(|| HttpResponse::Ok())))) .service(web::resource("/").route(web::to(|| HttpResponse::Ok()))))
.openssl(ssl_acceptor())
.map_err(|_| ()), .map_err(|_| ()),
) )
}); });

View File

@ -1,20 +1,16 @@
#![cfg(feature = "openssl")] #![cfg(feature = "openssl")]
use open_ssl::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode};
use std::io::Result;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use actix_codec::{AsyncRead, AsyncWrite};
use actix_http::HttpService; use actix_http::HttpService;
use actix_http_test::TestServer; use actix_http_test::TestServer;
use actix_server::ssl::OpensslAcceptor;
use actix_service::{pipeline_factory, ServiceFactory}; use actix_service::{pipeline_factory, ServiceFactory};
use actix_web::http::Version; use actix_web::http::Version;
use actix_web::{web, App, HttpResponse}; use actix_web::{web, App, HttpResponse};
use futures::future::ok; use futures::future::ok;
use open_ssl::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode};
fn ssl_acceptor<T: AsyncRead + AsyncWrite>() -> Result<OpensslAcceptor<T, ()>> { fn ssl_acceptor() -> SslAcceptor {
// load ssl keys // load ssl keys
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
builder builder
@ -31,13 +27,12 @@ fn ssl_acceptor<T: AsyncRead + AsyncWrite>() -> Result<OpensslAcceptor<T, ()>> {
Err(open_ssl::ssl::AlpnError::NOACK) Err(open_ssl::ssl::AlpnError::NOACK)
} }
}); });
builder.set_alpn_protos(b"\x02h2")?; builder.set_alpn_protos(b"\x02h2").unwrap();
Ok(actix_server::ssl::OpensslAcceptor::new(builder.build())) builder.build()
} }
#[actix_rt::test] #[actix_rt::test]
async fn test_connection_reuse_h2() { async fn test_connection_reuse_h2() {
let openssl = ssl_acceptor().unwrap();
let num = Arc::new(AtomicUsize::new(0)); let num = Arc::new(AtomicUsize::new(0));
let num2 = num.clone(); let num2 = num.clone();
@ -47,15 +42,11 @@ async fn test_connection_reuse_h2() {
num2.fetch_add(1, Ordering::Relaxed); num2.fetch_add(1, Ordering::Relaxed);
ok(io) ok(io)
}) })
.and_then(
openssl
.clone()
.map_err(|e| println!("Openssl error: {}", e)),
)
.and_then( .and_then(
HttpService::build() HttpService::build()
.h2(App::new() .h2(App::new()
.service(web::resource("/").route(web::to(|| HttpResponse::Ok())))) .service(web::resource("/").route(web::to(|| HttpResponse::Ok()))))
.openssl(ssl_acceptor())
.map_err(|_| ()), .map_err(|_| ()),
) )
}); });

View File

@ -46,6 +46,7 @@ async fn test_simple() {
} }
}) })
.finish(|_| ok::<_, Error>(Response::NotFound())) .finish(|_| ok::<_, Error>(Response::NotFound()))
.tcp()
}); });
// client service // client service
@ -62,10 +63,7 @@ async fn test_simple() {
.await .await
.unwrap(); .unwrap();
let item = framed.next().await.unwrap().unwrap(); let item = framed.next().await.unwrap().unwrap();
assert_eq!( assert_eq!(item, ws::Frame::Binary(Some(BytesMut::from(&b"text"[..]))));
item,
ws::Frame::Binary(Some(Bytes::from_static(b"text").into()))
);
framed.send(ws::Message::Ping("text".into())).await.unwrap(); framed.send(ws::Message::Ping("text".into())).await.unwrap();
let item = framed.next().await.unwrap().unwrap(); let item = framed.next().await.unwrap().unwrap();

View File

@ -7,7 +7,6 @@ use std::task::{Context, Poll};
use actix_http::{Extensions, Request, Response}; use actix_http::{Extensions, Request, Response};
use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url}; use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url};
use actix_server_config::ServerConfig;
use actix_service::boxed::{self, BoxService, BoxServiceFactory}; use actix_service::boxed::{self, BoxService, BoxServiceFactory};
use actix_service::{service_fn, Service, ServiceFactory}; use actix_service::{service_fn, Service, ServiceFactory};
use futures::future::{ok, FutureExt, LocalBoxFuture}; use futures::future::{ok, FutureExt, LocalBoxFuture};
@ -59,7 +58,7 @@ where
InitError = (), InitError = (),
>, >,
{ {
type Config = ServerConfig; type Config = ();
type Request = Request; type Request = Request;
type Response = ServiceResponse<B>; type Response = ServiceResponse<B>;
type Error = T::Error; type Error = T::Error;
@ -67,7 +66,7 @@ where
type Service = AppInitService<T::Service, B>; type Service = AppInitService<T::Service, B>;
type Future = AppInitResult<T, B>; type Future = AppInitResult<T, B>;
fn new_service(&self, cfg: &ServerConfig) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
// update resource default service // update resource default service
let default = self.default.clone().unwrap_or_else(|| { let default = self.default.clone().unwrap_or_else(|| {
Rc::new(boxed::factory(service_fn(|req: ServiceRequest| { Rc::new(boxed::factory(service_fn(|req: ServiceRequest| {
@ -76,13 +75,6 @@ where
}); });
// App config // App config
{
let mut c = self.config.borrow_mut();
let loc_cfg = Rc::get_mut(&mut c.0).unwrap();
loc_cfg.secure = cfg.secure();
loc_cfg.addr = cfg.local_addr();
}
let mut config = AppService::new( let mut config = AppService::new(
self.config.borrow().clone(), self.config.borrow().clone(),
default.clone(), default.clone(),
@ -123,7 +115,7 @@ where
AppInitResult { AppInitResult {
endpoint: None, endpoint: None,
endpoint_fut: self.endpoint.new_service(&()), endpoint_fut: self.endpoint.new_service(()),
data: self.data.clone(), data: self.data.clone(),
data_factories: Vec::new(), data_factories: Vec::new(),
data_factories_fut: self.data_factories.iter().map(|f| f()).collect(), data_factories_fut: self.data_factories.iter().map(|f| f()).collect(),
@ -281,7 +273,7 @@ impl ServiceFactory for AppRoutingFactory {
type Service = AppRouting; type Service = AppRouting;
type Future = AppRoutingFactoryResponse; type Future = AppRoutingFactoryResponse;
fn new_service(&self, _: &()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
AppRoutingFactoryResponse { AppRoutingFactoryResponse {
fut: self fut: self
.services .services
@ -290,12 +282,12 @@ impl ServiceFactory for AppRoutingFactory {
CreateAppRoutingItem::Future( CreateAppRoutingItem::Future(
Some(path.clone()), Some(path.clone()),
guards.borrow_mut().take(), guards.borrow_mut().take(),
service.new_service(&()).boxed_local(), service.new_service(()).boxed_local(),
) )
}) })
.collect(), .collect(),
default: None, default: None,
default_fut: Some(self.default.new_service(&())), default_fut: Some(self.default.new_service(())),
} }
} }
} }
@ -440,8 +432,8 @@ impl ServiceFactory for AppEntry {
type Service = AppRouting; type Service = AppRouting;
type Future = AppRoutingFactoryResponse; type Future = AppRoutingFactoryResponse;
fn new_service(&self, _: &()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
self.factory.borrow_mut().as_mut().unwrap().new_service(&()) self.factory.borrow_mut().as_mut().unwrap().new_service(())
} }
} }

View File

@ -24,9 +24,10 @@
//! ); //! );
//! } //! }
//! ``` //! ```
#![allow(non_snake_case)] #![allow(non_snake_case)]
use actix_http::http::{self, header, uri::Uri, HttpTryFrom}; use std::convert::TryFrom;
use actix_http::http::{self, header, uri::Uri};
use actix_http::RequestHead; use actix_http::RequestHead;
/// Trait defines resource guards. Guards are used for route selection. /// Trait defines resource guards. Guards are used for route selection.

View File

@ -177,7 +177,7 @@ where
type Service = ExtractService<T, S>; type Service = ExtractService<T, S>;
type Future = Ready<Result<Self::Service, ()>>; type Future = Ready<Result<Self::Service, ()>>;
fn new_service(&self, _: &()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
ok(ExtractService { ok(ExtractService {
_t: PhantomData, _t: PhantomData,
service: self.service.clone(), service: self.service.clone(),

View File

@ -76,7 +76,7 @@ impl ConnectionInfo {
} }
} }
if scheme.is_none() { if scheme.is_none() {
scheme = req.uri.scheme_part().map(|a| a.as_str()); scheme = req.uri.scheme().map(|a| a.as_str());
if scheme.is_none() && cfg.secure() { if scheme.is_none() && cfg.secure() {
scheme = Some("https") scheme = Some("https")
} }
@ -98,7 +98,7 @@ impl ConnectionInfo {
host = h.to_str().ok(); host = h.to_str().ok();
} }
if host.is_none() { if host.is_none() {
host = req.uri.authority_part().map(|a| a.as_str()); host = req.uri.authority().map(|a| a.as_str());
if host.is_none() { if host.is_none() {
host = Some(cfg.host()); host = Some(cfg.host());
} }

View File

@ -1,4 +1,5 @@
//! Middleware for setting default response headers //! Middleware for setting default response headers
use std::convert::TryFrom;
use std::rc::Rc; use std::rc::Rc;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
@ -6,7 +7,7 @@ use actix_service::{Service, Transform};
use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; use futures::future::{ok, FutureExt, LocalBoxFuture, Ready};
use crate::http::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use crate::http::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use crate::http::{HeaderMap, HttpTryFrom}; use crate::http::{Error as HttpError, HeaderMap};
use crate::service::{ServiceRequest, ServiceResponse}; use crate::service::{ServiceRequest, ServiceResponse};
use crate::Error; use crate::Error;
@ -58,8 +59,10 @@ impl DefaultHeaders {
#[inline] #[inline]
pub fn header<K, V>(mut self, key: K, value: V) -> Self pub fn header<K, V>(mut self, key: K, value: V) -> Self
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
HeaderValue: HttpTryFrom<V>, <HeaderName as TryFrom<K>>::Error: Into<HttpError>,
HeaderValue: TryFrom<V>,
<HeaderValue as TryFrom<V>>::Error: Into<HttpError>,
{ {
#[allow(clippy::match_wild_err_arm)] #[allow(clippy::match_wild_err_arm)]
match HeaderName::try_from(key) { match HeaderName::try_from(key) {

View File

@ -4,7 +4,7 @@ use std::task::{Context, Poll};
use actix_service::{Service, Transform}; use actix_service::{Service, Transform};
use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; use futures::future::{ok, FutureExt, LocalBoxFuture, Ready};
use hashbrown::HashMap; use fxhash::FxHashMap;
use crate::dev::{ServiceRequest, ServiceResponse}; use crate::dev::{ServiceRequest, ServiceResponse};
use crate::error::{Error, Result}; use crate::error::{Error, Result};
@ -52,13 +52,13 @@ type ErrorHandler<B> = dyn Fn(ServiceResponse<B>) -> Result<ErrorHandlerResponse
/// # } /// # }
/// ``` /// ```
pub struct ErrorHandlers<B> { pub struct ErrorHandlers<B> {
handlers: Rc<HashMap<StatusCode, Box<ErrorHandler<B>>>>, handlers: Rc<FxHashMap<StatusCode, Box<ErrorHandler<B>>>>,
} }
impl<B> Default for ErrorHandlers<B> { impl<B> Default for ErrorHandlers<B> {
fn default() -> Self { fn default() -> Self {
ErrorHandlers { ErrorHandlers {
handlers: Rc::new(HashMap::new()), handlers: Rc::new(FxHashMap::default()),
} }
} }
} }
@ -105,7 +105,7 @@ where
#[doc(hidden)] #[doc(hidden)]
pub struct ErrorHandlersMiddleware<S, B> { pub struct ErrorHandlersMiddleware<S, B> {
service: S, service: S,
handlers: Rc<HashMap<StatusCode, Box<ErrorHandler<B>>>>, handlers: Rc<FxHashMap<StatusCode, Box<ErrorHandler<B>>>>,
} }
impl<S, B> Service for ErrorHandlersMiddleware<S, B> impl<S, B> Service for ErrorHandlersMiddleware<S, B>

View File

@ -1,5 +1,6 @@
//! Request logging middleware //! Request logging middleware
use std::collections::HashSet; use std::collections::HashSet;
use std::convert::TryFrom;
use std::env; use std::env;
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use std::future::Future; use std::future::Future;
@ -17,7 +18,7 @@ use time;
use crate::dev::{BodySize, MessageBody, ResponseBody}; use crate::dev::{BodySize, MessageBody, ResponseBody};
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::http::{HeaderName, HttpTryFrom, StatusCode}; use crate::http::{HeaderName, StatusCode};
use crate::service::{ServiceRequest, ServiceResponse}; use crate::service::{ServiceRequest, ServiceResponse};
use crate::HttpResponse; use crate::HttpResponse;

View File

@ -1,7 +1,7 @@
//! `Middleware` to normalize request's URI //! `Middleware` to normalize request's URI
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use actix_http::http::{HttpTryFrom, PathAndQuery, Uri}; use actix_http::http::{PathAndQuery, Uri};
use actix_service::{Service, Transform}; use actix_service::{Service, Transform};
use bytes::Bytes; use bytes::Bytes;
use futures::future::{ok, Ready}; use futures::future::{ok, Ready};
@ -74,7 +74,6 @@ where
fn call(&mut self, mut req: ServiceRequest) -> Self::Future { fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
let head = req.head_mut(); let head = req.head_mut();
let path = head.uri.path(); let path = head.uri.path();
let original_len = path.len(); let original_len = path.len();
let path = self.merge_slash.replace_all(path, "/"); let path = self.merge_slash.replace_all(path, "/");
@ -86,9 +85,10 @@ where
let path = if let Some(q) = pq.query() { let path = if let Some(q) = pq.query() {
Bytes::from(format!("{}?{}", path, q)) Bytes::from(format!("{}?{}", path, q))
} else { } else {
Bytes::from(path.as_ref()) Bytes::copy_from_slice(path.as_bytes())
}; };
parts.path_and_query = Some(PathAndQuery::try_from(path).unwrap()); parts.path_and_query = Some(PathAndQuery::from_maybe_shared(path).unwrap());
drop(head);
let uri = Uri::from_parts(parts).unwrap(); let uri = Uri::from_parts(parts).unwrap();
req.match_info_mut().get_mut().update(&uri); req.match_info_mut().get_mut().update(&uri);

View File

@ -435,9 +435,9 @@ impl ServiceFactory for ResourceFactory {
type Service = ResourceService; type Service = ResourceService;
type Future = CreateResourceService; type Future = CreateResourceService;
fn new_service(&self, _: &()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
let default_fut = if let Some(ref default) = *self.default.borrow() { let default_fut = if let Some(ref default) = *self.default.borrow() {
Some(default.new_service(&())) Some(default.new_service(()))
} else { } else {
None None
}; };
@ -446,7 +446,7 @@ impl ServiceFactory for ResourceFactory {
fut: self fut: self
.routes .routes
.iter() .iter()
.map(|route| CreateRouteServiceItem::Future(route.new_service(&()))) .map(|route| CreateRouteServiceItem::Future(route.new_service(())))
.collect(), .collect(),
data: self.data.clone(), data: self.data.clone(),
default: None, default: None,
@ -575,8 +575,8 @@ impl ServiceFactory for ResourceEndpoint {
type Service = ResourceService; type Service = ResourceService;
type Future = CreateResourceService; type Future = CreateResourceService;
fn new_service(&self, _: &()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
self.factory.borrow_mut().as_mut().unwrap().new_service(&()) self.factory.borrow_mut().as_mut().unwrap().new_service(())
} }
} }

View File

@ -1,3 +1,4 @@
use std::convert::TryFrom;
use std::future::Future; use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
@ -5,8 +6,7 @@ use std::task::{Context, Poll};
use actix_http::error::InternalError; use actix_http::error::InternalError;
use actix_http::http::{ use actix_http::http::{
header::IntoHeaderValue, Error as HttpError, HeaderMap, HeaderName, HttpTryFrom, header::IntoHeaderValue, Error as HttpError, HeaderMap, HeaderName, StatusCode,
StatusCode,
}; };
use actix_http::{Error, Response, ResponseBuilder}; use actix_http::{Error, Response, ResponseBuilder};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
@ -68,7 +68,8 @@ pub trait Responder {
fn with_header<K, V>(self, key: K, value: V) -> CustomResponder<Self> fn with_header<K, V>(self, key: K, value: V) -> CustomResponder<Self>
where where
Self: Sized, Self: Sized,
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
CustomResponder::new(self).with_header(key, value) CustomResponder::new(self).with_header(key, value)
@ -267,7 +268,8 @@ impl<T: Responder> CustomResponder<T> {
/// ``` /// ```
pub fn with_header<K, V>(mut self, key: K, value: V) -> Self pub fn with_header<K, V>(mut self, key: K, value: V) -> Self
where where
HeaderName: HttpTryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
if self.headers.is_none() { if self.headers.is_none() {

View File

@ -2,7 +2,7 @@ use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use actix_router::ResourceDef; use actix_router::ResourceDef;
use hashbrown::HashMap; use fxhash::FxHashMap;
use url::Url; use url::Url;
use crate::error::UrlGenerationError; use crate::error::UrlGenerationError;
@ -12,7 +12,7 @@ use crate::request::HttpRequest;
pub struct ResourceMap { pub struct ResourceMap {
root: ResourceDef, root: ResourceDef,
parent: RefCell<Option<Rc<ResourceMap>>>, parent: RefCell<Option<Rc<ResourceMap>>>,
named: HashMap<String, ResourceDef>, named: FxHashMap<String, ResourceDef>,
patterns: Vec<(ResourceDef, Option<Rc<ResourceMap>>)>, patterns: Vec<(ResourceDef, Option<Rc<ResourceMap>>)>,
} }
@ -21,7 +21,7 @@ impl ResourceMap {
ResourceMap { ResourceMap {
root, root,
parent: RefCell::new(None), parent: RefCell::new(None),
named: HashMap::new(), named: FxHashMap::default(),
patterns: Vec::new(), patterns: Vec::new(),
} }
} }

View File

@ -69,9 +69,9 @@ impl ServiceFactory for Route {
type Service = RouteService; type Service = RouteService;
type Future = CreateRouteService; type Future = CreateRouteService;
fn new_service(&self, _: &()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
CreateRouteService { CreateRouteService {
fut: self.service.new_service(&()), fut: self.service.new_service(()),
guards: self.guards.clone(), guards: self.guards.clone(),
} }
} }
@ -280,9 +280,9 @@ where
type Service = BoxedRouteService<ServiceRequest, Self::Response>; type Service = BoxedRouteService<ServiceRequest, Self::Response>;
type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>; type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: &()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
self.service self.service
.new_service(&()) .new_service(())
.map(|result| match result { .map(|result| match result {
Ok(service) => { Ok(service) => {
let service: BoxedRouteService<_, _> = let service: BoxedRouteService<_, _> =

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