diff --git a/CHANGES.md b/CHANGES.md index a7569862..600dc8e1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,12 @@ # Changes +## [2.0.0-alpha.3] - 2019-12-xx + +### Changed + +* Migrate to tokio 0.2 + + ## [2.0.0-alpha.1] - 2019-11-22 ### Changed diff --git a/Cargo.toml b/Cargo.toml index 689f7b14..ee3158a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "2.0.0-alpha.1" +version = "2.0.0-alpha.3" authors = ["Nikolay Kim "] description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." readme = "README.md" @@ -63,35 +63,36 @@ secure-cookies = ["actix-http/secure-cookies"] fail = ["actix-http/fail"] # openssl -openssl = ["open-ssl", "actix-server/openssl", "awc/openssl"] +openssl = ["open-ssl", "actix-tls/openssl", "awc/openssl"] # rustls -# rustls = ["rust-tls", "actix-server/rustls", "awc/rustls"] +rustls = ["rust-tls", "actix-tls/rustls", "awc/rustls"] [dependencies] -actix-codec = "0.2.0-alpha.1" -actix-service = "1.0.0-alpha.1" -actix-utils = "0.5.0-alpha.1" -actix-router = "0.1.5" -actix-rt = "1.0.0-alpha.1" -actix-web-codegen = "0.2.0-alpha.1" -actix-http = "0.3.0-alpha.1" -actix-server = "0.8.0-alpha.1" -actix-server-config = "0.3.0-alpha.1" -actix-testing = "0.3.0-alpha.1" -actix-threadpool = "0.2.0-alpha.1" -awc = { version = "0.3.0-alpha.1", optional = true } +actix-codec = "0.2.0-alpha.3" +actix-service = "1.0.0-alpha.3" +actix-utils = "1.0.0-alpha.3" +actix-router = "0.2.0" +actix-rt = "1.0.0-alpha.3" +actix-server = "1.0.0-alpha.3" +actix-testing = "1.0.0-alpha.3" +actix-threadpool = "0.3.0" +actix-tls = { version = "1.0.0-alpha.3" } -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" encoding_rs = "0.8" futures = "0.3.1" -hashbrown = "0.6.3" +fxhash = "0.2.1" log = "0.4" mime = "0.3" net2 = "0.2.33" parking_lot = "0.9" -pin-project = "0.4.5" +pin-project = "0.4.6" regex = "1.0" serde = { version = "1.0", features=["derive"] } serde_json = "1.0" @@ -101,12 +102,12 @@ url = "2.1" # ssl support 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] # actix = "0.8.3" -actix-connect = "0.3.0-alpha.1" -actix-http-test = "0.3.0-alpha.1" +actix-connect = "1.0.0-alpha.2" +actix-http-test = "0.3.0-alpha.2" rand = "0.7" env_logger = "0.6" serde_derive = "1.0" @@ -123,7 +124,6 @@ actix-web = { path = "." } actix-http = { path = "actix-http" } actix-http-test = { path = "test-server" } actix-web-codegen = { path = "actix-web-codegen" } -# actix-web-actors = { path = "actix-web-actors" } actix-cors = { path = "actix-cors" } actix-identity = { path = "actix-identity" } actix-session = { path = "actix-session" } @@ -134,9 +134,9 @@ awc = { path = "awc" } actix-codec = { 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-macros = { 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-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" } diff --git a/actix-cors/CHANGES.md b/actix-cors/CHANGES.md index 10e408ed..92e3b697 100644 --- a/actix-cors/CHANGES.md +++ b/actix-cors/CHANGES.md @@ -1,8 +1,10 @@ # 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 diff --git a/actix-cors/Cargo.toml b/actix-cors/Cargo.toml index ddb5f307..976d0be7 100644 --- a/actix-cors/Cargo.toml +++ b/actix-cors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-cors" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.3" authors = ["Nikolay Kim "] description = "Cross-origin resource sharing (CORS) for Actix applications." readme = "README.md" @@ -18,9 +18,9 @@ path = "src/lib.rs" [dependencies] 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" futures = "0.3.1" [dev-dependencies] -actix-rt = "1.0.0-alpha.1" +actix-rt = "1.0.0-alpha.2" diff --git a/actix-cors/src/lib.rs b/actix-cors/src/lib.rs index d3607aa8..71d98f89 100644 --- a/actix-cors/src/lib.rs +++ b/actix-cors/src/lib.rs @@ -40,6 +40,7 @@ //! //! Cors middleware automatically handle *OPTIONS* preflight request. use std::collections::HashSet; +use std::convert::TryFrom; use std::iter::FromIterator; use std::rc::Rc; use std::task::{Context, Poll}; @@ -48,7 +49,7 @@ use actix_service::{Service, Transform}; use actix_web::dev::{RequestHead, ServiceRequest, ServiceResponse}; use actix_web::error::{Error, ResponseError, Result}; use actix_web::http::header::{self, HeaderName, HeaderValue}; -use actix_web::http::{self, HttpTryFrom, Method, StatusCode, Uri}; +use actix_web::http::{self, Error as HttpError, Method, StatusCode, Uri}; use actix_web::HttpResponse; use derive_more::Display; use futures::future::{ok, Either, FutureExt, LocalBoxFuture, Ready}; @@ -274,7 +275,8 @@ impl Cors { pub fn allowed_methods(mut self, methods: U) -> Cors where U: IntoIterator, - Method: HttpTryFrom, + Method: TryFrom, + >::Error: Into, { self.methods = true; if let Some(cors) = cors(&mut self.cors, &self.error) { @@ -296,7 +298,8 @@ impl Cors { /// Set an allowed header pub fn allowed_header(mut self, header: H) -> Cors where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, { if let Some(cors) = cors(&mut self.cors, &self.error) { match HeaderName::try_from(header) { @@ -328,7 +331,8 @@ impl Cors { pub fn allowed_headers(mut self, headers: U) -> Cors where U: IntoIterator, - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, { if let Some(cors) = cors(&mut self.cors, &self.error) { for h in headers { @@ -362,7 +366,8 @@ impl Cors { pub fn expose_headers(mut self, headers: U) -> Cors where U: IntoIterator, - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, { for h in headers { match HeaderName::try_from(h) { diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index 5ec56593..29e774e0 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [0.2.0-alpha.2] - 2019-12-03 + +* Migrate to `std::future` + ## [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) diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 19366b90..261bf14e 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.3" authors = ["Nikolay Kim "] description = "Static files support for actix web." readme = "README.md" @@ -18,11 +18,11 @@ name = "actix_files" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.1", default-features = false } -actix-http = "0.3.0-alpha.1" -actix-service = "1.0.0-alpha.1" +actix-web = { version = "2.0.0-alpha.2", default-features = false } +actix-http = "0.3.0-alpha.2" +actix-service = "1.0.0-alpha.3" bitflags = "1" -bytes = "0.4" +bytes = "0.5.2" futures = "0.3.1" derive_more = "0.99.2" log = "0.4" @@ -32,5 +32,5 @@ percent-encoding = "2.1" v_htmlescape = "0.4" [dev-dependencies] -actix-rt = "1.0.0-alpha.1" -actix-web = { version = "2.0.0-alpha.1", features=["openssl"] } +actix-rt = "1.0.0-alpha.2" +actix-web = { version = "2.0.0-alpha.2", features=["openssl"] } diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index ed8b6c3b..c33367d7 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -18,7 +18,7 @@ use actix_web::dev::{ AppService, HttpServiceFactory, Payload, ResourceDef, ServiceRequest, ServiceResponse, }; -use actix_web::error::{Canceled, Error, ErrorInternalServerError}; +use actix_web::error::{BlockingError, Error, ErrorInternalServerError}; use actix_web::guard::Guard; use actix_web::http::header::{self, DispositionType}; 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() } +fn handle_error(err: BlockingError) -> Error { + match err { + BlockingError::Error(err) => err.into(), + BlockingError::Canceled => ErrorInternalServerError("Unexpected error"), + } +} #[doc(hidden)] /// A helper created from a `std::fs::File` which reads the file /// chunk-by-chunk on a `ThreadPool`. @@ -57,9 +63,8 @@ pub struct ChunkedReadFile { size: u64, offset: u64, file: Option, - fut: Option< - LocalBoxFuture<'static, Result, Canceled>>, - >, + fut: + Option>>>, counter: u64, } @@ -72,18 +77,14 @@ impl Stream for ChunkedReadFile { ) -> Poll> { if let Some(ref mut fut) = self.fut { return match Pin::new(fut).poll(cx) { - Poll::Ready(Err(_)) => Poll::Ready(Some(Err(ErrorInternalServerError( - "Unexpected error", - ) - .into()))), - Poll::Ready(Ok(Ok((file, bytes)))) => { + Poll::Ready(Ok((file, bytes))) => { self.fut.take(); self.file = Some(file); self.offset += bytes.len() as u64; self.counter += bytes.len() as u64; 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, }; } @@ -415,7 +416,7 @@ impl ServiceFactory for Files { type InitError = (); type Future = LocalBoxFuture<'static, Result>; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { let mut srv = FilesService { directory: self.directory.clone(), index: self.index.clone(), @@ -430,7 +431,7 @@ impl ServiceFactory for Files { if let Some(ref default) = *self.default.borrow() { default - .new_service(&()) + .new_service(()) .map(move |result| match result { Ok(default) => { srv.default = Some(default); @@ -1262,7 +1263,7 @@ mod tests { .default_handler(|req: ServiceRequest| { ok(req.into_response(HttpResponse::Ok().body("default content"))) }) - .new_service(&()) + .new_service(()) .await .unwrap(); let req = TestRequest::with_uri("/missing").to_srv_request(); diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index 4783daef..0b80266a 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -20,20 +20,19 @@ name = "actix_framed" path = "src/lib.rs" [dependencies] -actix-codec = "0.2.0-alpha.1" -actix-service = "1.0.0-alpha.1" -actix-router = "0.1.2" -actix-rt = "1.0.0-alpha.1" -actix-http = "0.3.0-alpha.1" -actix-server-config = "0.3.0-alpha.1" +actix-codec = "0.2.0-alpha.3" +actix-service = "1.0.0-alpha.3" +actix-router = "0.2.0" +actix-rt = "1.0.0-alpha.3" +actix-http = "0.3.0-alpha.3" -bytes = "0.4" +bytes = "0.5.2" futures = "0.3.1" pin-project = "0.4.6" log = "0.4" [dev-dependencies] -actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } -actix-connect = { version = "0.3.0-alpha.1", features=["openssl"] } -actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } -actix-utils = "0.5.0-alpha.1" +actix-server = { version = "1.0.0-alpha.3" } +actix-connect = { version = "1.0.0-alpha.3", features=["openssl"] } +actix-http-test = { version = "0.3.0-alpha.3", features=["openssl"] } +actix-utils = "1.0.0-alpha.3" diff --git a/actix-framed/src/app.rs b/actix-framed/src/app.rs index f3e746e9..e4b91e6c 100644 --- a/actix-framed/src/app.rs +++ b/actix-framed/src/app.rs @@ -7,7 +7,6 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_http::h1::{Codec, SendResponse}; use actix_http::{Error, Request, Response}; use actix_router::{Path, Router, Url}; -use actix_server_config::ServerConfig; use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use futures::future::{ok, FutureExt, LocalBoxFuture}; @@ -97,7 +96,7 @@ where T: AsyncRead + AsyncWrite + Unpin + 'static, S: 'static, { - type Config = ServerConfig; + type Config = (); type Request = (Request, Framed); type Response = (); type Error = Error; @@ -105,7 +104,7 @@ where type Service = FramedAppService; type Future = CreateService; - fn new_service(&self, _: &ServerConfig) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { CreateService { fut: self .services @@ -113,7 +112,7 @@ where .map(|(path, service)| { CreateServiceItem::Future( Some(path.clone()), - service.new_service(&()), + service.new_service(()), ) }) .collect(), diff --git a/actix-framed/src/helpers.rs b/actix-framed/src/helpers.rs index b654f9cd..d08ca25a 100644 --- a/actix-framed/src/helpers.rs +++ b/actix-framed/src/helpers.rs @@ -56,8 +56,8 @@ where type Service = BoxedHttpService; type Future = LocalBoxFuture<'static, Result>; - fn new_service(&self, _: &()) -> Self::Future { - let fut = self.0.new_service(&()); + fn new_service(&self, _: ()) -> Self::Future { + let fut = self.0.new_service(()); async move { fut.await.map_err(|_| ()).map(|service| { diff --git a/actix-framed/src/request.rs b/actix-framed/src/request.rs index bdcdd702..1872dcc2 100644 --- a/actix-framed/src/request.rs +++ b/actix-framed/src/request.rs @@ -123,7 +123,9 @@ impl FramedRequest { #[cfg(test)] 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 super::*; diff --git a/actix-framed/src/route.rs b/actix-framed/src/route.rs index 78303968..03e48e4d 100644 --- a/actix-framed/src/route.rs +++ b/actix-framed/src/route.rs @@ -113,7 +113,7 @@ where type Service = FramedRouteService; type Future = Ready>; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { ok(FramedRouteService { handler: self.handler.clone(), methods: self.methods.clone(), diff --git a/actix-framed/src/service.rs b/actix-framed/src/service.rs index ed3a75ff..92393ca7 100644 --- a/actix-framed/src/service.rs +++ b/actix-framed/src/service.rs @@ -33,7 +33,7 @@ impl ServiceFactory for VerifyWebSockets { type Service = VerifyWebSockets; type Future = Ready>; - fn new_service(&self, _: &C) -> Self::Future { + fn new_service(&self, _: C) -> Self::Future { ok(VerifyWebSockets { _t: PhantomData }) } } @@ -83,7 +83,7 @@ where type Service = SendError; type Future = Ready>; - fn new_service(&self, _: &C) -> Self::Future { + fn new_service(&self, _: C) -> Self::Future { ok(SendError(PhantomData)) } } diff --git a/actix-framed/src/test.rs b/actix-framed/src/test.rs index 7969d51f..b8029531 100644 --- a/actix-framed/src/test.rs +++ b/actix-framed/src/test.rs @@ -1,10 +1,11 @@ //! Various helpers for Actix applications to use during testing. +use std::convert::TryFrom; use std::future::Future; use actix_codec::Framed; use actix_http::h1::Codec; use actix_http::http::header::{Header, HeaderName, IntoHeaderValue}; -use actix_http::http::{HttpTryFrom, Method, Uri, Version}; +use actix_http::http::{Error as HttpError, Method, Uri, Version}; use actix_http::test::{TestBuffer, TestRequest as HttpTestRequest}; use actix_router::{Path, Url}; @@ -41,7 +42,8 @@ impl TestRequest<()> { /// Create TestRequest and set header pub fn with_header(key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { Self::default().header(key, value) @@ -96,7 +98,8 @@ impl TestRequest { /// Set a header pub fn header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { self.req.header(key, value); diff --git a/actix-framed/tests/test_server.rs b/actix-framed/tests/test_server.rs index 4d1028d3..40e698c7 100644 --- a/actix-framed/tests/test_server.rs +++ b/actix-framed/tests/test_server.rs @@ -3,7 +3,7 @@ use actix_http::{body, http::StatusCode, ws, Error, HttpService, Response}; use actix_http_test::TestServer; use actix_service::{pipeline_factory, IntoServiceFactory, ServiceFactory}; use actix_utils::framed::FramedTransport; -use bytes::{Bytes, BytesMut}; +use bytes::BytesMut; use futures::{future, SinkExt, StreamExt}; 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)), ) .finish(|_| future::ok::<_, Error>(Response::NotFound())) + .tcp() }); assert!(srv.ws_at("/test").await.is_err()); @@ -69,7 +70,7 @@ async fn test_simple() { let (item, mut framed) = framed.into_future().await; assert_eq!( 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(); @@ -135,7 +136,7 @@ async fn test_service() { let (item, mut framed) = framed.into_future().await; assert_eq!( 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(); diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 9a14abef..aadef9e3 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "0.3.0-alpha.1" +version = "0.3.0-alpha.3" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" @@ -16,7 +16,7 @@ edition = "2018" workspace = ".." [package.metadata.docs.rs] -features = ["openssl", "fail", "brotli", "flate2-zlib", "secure-cookies"] +features = ["openssl", "rustls", "fail", "brotli", "flate2-zlib", "secure-cookies"] [lib] name = "actix_http" @@ -26,10 +26,10 @@ path = "src/lib.rs" default = [] # openssl -openssl = ["open-ssl", "actix-connect/openssl", "tokio-openssl"] +openssl = ["actix-tls/openssl", "actix-connect/openssl"] # rustls support -# rustls = ["rust-tls", "webpki-roots", "actix-connect/rustls"] +rustls = ["actix-tls/rustls", "actix-connect/rustls"] # brotli encoding, requires c compiler brotli = ["brotli2"] @@ -47,26 +47,26 @@ fail = ["failure"] secure-cookies = ["ring"] [dependencies] -actix-service = "1.0.0-alpha.1" -actix-codec = "0.2.0-alpha.1" -actix-connect = "1.0.0-alpha.1" -actix-utils = "0.5.0-alpha.1" -actix-server-config = "0.3.0-alpha.1" -actix-rt = "1.0.0-alpha.1" -actix-threadpool = "0.2.0-alpha.1" +actix-service = "1.0.0-alpha.3" +actix-codec = "0.2.0-alpha.3" +actix-connect = "1.0.0-alpha.3" +actix-utils = "1.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" +actix-threadpool = "0.3.0" +actix-tls = { version = "1.0.0-alpha.3", optional = true } -base64 = "0.10" +base64 = "0.11" bitflags = "1.0" -bytes = "0.4" +bytes = "0.5.2" copyless = "0.1.4" chrono = "0.4.6" derive_more = "0.99.2" either = "1.5.2" encoding_rs = "0.8" futures = "0.3.1" -hashbrown = "0.6.3" -h2 = "0.2.0-alpha.3" -http = "0.1.17" +fxhash = "0.2.1" +h2 = "0.2.0" +http = "0.2.0" httparse = "1.3" indexmap = "1.2" lazy_static = "1.0" @@ -74,9 +74,9 @@ language-tags = "0.2" log = "0.4" mime = "0.3" percent-encoding = "2.1" -pin-project = "0.4.5" +pin-project = "0.4.6" rand = "0.7" -regex = "1.0" +regex = "1.3" serde = "1.0" serde_json = "1.0" sha1 = "0.6" @@ -84,9 +84,6 @@ slab = "0.4" serde_urlencoded = "0.6.1" 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 ring = { version = "0.16.9", optional = true } @@ -96,17 +93,13 @@ flate2 = { version="1.0.7", optional = true, default-features = false } # optional deps 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] -#actix-server = { version = "0.8.0-alpha.1", features=["openssl", "rustls"] } -actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } -actix-connect = { version = "1.0.0-alpha.1", features=["openssl"] } -actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } +actix-server = { version = "1.0.0-alpha.3" } +actix-connect = { version = "1.0.0-alpha.3", features=["openssl"] } +actix-http-test = { version = "0.3.0-alpha.3", features=["openssl"] } +actix-tls = { version = "1.0.0-alpha.3", features=["openssl"] } env_logger = "0.6" 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" } diff --git a/actix-http/examples/echo.rs b/actix-http/examples/echo.rs index ba81020c..5b2894f8 100644 --- a/actix-http/examples/echo.rs +++ b/actix-http/examples/echo.rs @@ -34,6 +34,7 @@ fn main() -> io::Result<()> { ) } }) + .tcp() })? .run() } diff --git a/actix-http/examples/echo2.rs b/actix-http/examples/echo2.rs index 3776c7d5..07d18127 100644 --- a/actix-http/examples/echo2.rs +++ b/actix-http/examples/echo2.rs @@ -25,7 +25,7 @@ fn main() -> io::Result<()> { Server::build() .bind("echo", "127.0.0.1:8080", || { - HttpService::build().finish(handle_request) + HttpService::build().finish(handle_request).tcp() })? .run() } diff --git a/actix-http/examples/hello-world.rs b/actix-http/examples/hello-world.rs index 6e382039..7d857686 100644 --- a/actix-http/examples/hello-world.rs +++ b/actix-http/examples/hello-world.rs @@ -21,6 +21,7 @@ fn main() -> io::Result<()> { res.header("x-head", HeaderValue::from_static("dummy value!")); future::ok::<_, ()>(res.body("Hello world!")) }) + .tcp() })? .run() } diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index b69c21ea..ff723562 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -133,7 +133,7 @@ pub enum Body { impl Body { /// Create body from slice (copy) 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. @@ -226,7 +226,7 @@ impl From for Body { impl<'a> From<&'a String> for 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))) } } diff --git a/actix-http/src/builder.rs b/actix-http/src/builder.rs index 7e1dae58..271abd43 100644 --- a/actix-http/src/builder.rs +++ b/actix-http/src/builder.rs @@ -1,9 +1,8 @@ -use std::fmt; use std::marker::PhantomData; use std::rc::Rc; +use std::{fmt, net}; use actix_codec::Framed; -use actix_server_config::ServerConfig as SrvConfig; use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use crate::body::MessageBody; @@ -24,6 +23,8 @@ pub struct HttpServiceBuilder> { keep_alive: KeepAlive, client_timeout: u64, client_disconnect: u64, + secure: bool, + local_addr: Option, expect: X, upgrade: Option, on_connect: Option Box>>, @@ -32,7 +33,7 @@ pub struct HttpServiceBuilder> { impl HttpServiceBuilder> where - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::InitError: fmt::Debug, ::Future: 'static, @@ -43,6 +44,8 @@ where keep_alive: KeepAlive::Timeout(5), client_timeout: 5000, client_disconnect: 0, + secure: false, + local_addr: None, expect: ExpectHandler, upgrade: None, on_connect: None, @@ -53,19 +56,15 @@ where impl HttpServiceBuilder where - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::InitError: fmt::Debug, ::Future: 'static, - X: ServiceFactory, + X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, ::Future: 'static, - U: ServiceFactory< - Config = SrvConfig, - Request = (Request, Framed), - Response = (), - >, + U: ServiceFactory), Response = ()>, U::Error: fmt::Display, U::InitError: fmt::Debug, ::Future: 'static, @@ -78,6 +77,18 @@ where 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. /// /// Defines a timeout for reading client request header. If a client does not transmit @@ -113,7 +124,7 @@ where pub fn expect(self, expect: F) -> HttpServiceBuilder where F: IntoServiceFactory, - X1: ServiceFactory, + X1: ServiceFactory, X1::Error: Into, X1::InitError: fmt::Debug, ::Future: 'static, @@ -122,6 +133,8 @@ where keep_alive: self.keep_alive, client_timeout: self.client_timeout, client_disconnect: self.client_disconnect, + secure: self.secure, + local_addr: self.local_addr, expect: expect.into_factory(), upgrade: self.upgrade, on_connect: self.on_connect, @@ -137,7 +150,7 @@ where where F: IntoServiceFactory, U1: ServiceFactory< - Config = SrvConfig, + Config = (), Request = (Request, Framed), Response = (), >, @@ -149,6 +162,8 @@ where keep_alive: self.keep_alive, client_timeout: self.client_timeout, client_disconnect: self.client_disconnect, + secure: self.secure, + local_addr: self.local_addr, expect: self.expect, upgrade: Some(upgrade.into_factory()), on_connect: self.on_connect, @@ -170,7 +185,7 @@ where } /// Finish service configuration and create *http service* for HTTP/1 protocol. - pub fn h1(self, service: F) -> H1Service + pub fn h1(self, service: F) -> H1Service where B: MessageBody, F: IntoServiceFactory, @@ -182,6 +197,8 @@ where self.keep_alive, self.client_timeout, self.client_disconnect, + self.secure, + self.local_addr, ); H1Service::with_config(cfg, service.into_factory()) .expect(self.expect) @@ -190,7 +207,7 @@ where } /// Finish service configuration and create *http service* for HTTP/2 protocol. - pub fn h2(self, service: F) -> H2Service + pub fn h2(self, service: F) -> H2Service where B: MessageBody + 'static, F: IntoServiceFactory, @@ -203,12 +220,14 @@ where self.keep_alive, self.client_timeout, self.client_disconnect, + self.secure, + self.local_addr, ); H2Service::with_config(cfg, service.into_factory()).on_connect(self.on_connect) } /// Finish service configuration and create `HttpService` instance. - pub fn finish(self, service: F) -> HttpService + pub fn finish(self, service: F) -> HttpService where B: MessageBody + 'static, F: IntoServiceFactory, @@ -221,6 +240,8 @@ where self.keep_alive, self.client_timeout, self.client_disconnect, + self.secure, + self.local_addr, ); HttpService::with_config(cfg, service.into_factory()) .expect(self.expect) diff --git a/actix-http/src/client/connection.rs b/actix-http/src/client/connection.rs index 75d393b1..9b590690 100644 --- a/actix-http/src/client/connection.rs +++ b/actix-http/src/client/connection.rs @@ -1,6 +1,6 @@ use std::pin::Pin; use std::task::{Context, Poll}; -use std::{fmt, io, time}; +use std::{fmt, io, mem, time}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; 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], + ) -> bool { match self { EitherIo::A(ref val) => val.prepare_uninitialized_buffer(buf), EitherIo::B(ref val) => val.prepare_uninitialized_buffer(buf), diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index eaa3d97e..2710252e 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -6,10 +6,10 @@ use actix_codec::{AsyncRead, AsyncWrite}; use actix_connect::{ default_connector, Connect as TcpConnect, Connection as TcpConnection, }; +use actix_rt::net::TcpStream; use actix_service::{apply_fn, Service}; use actix_utils::timeout::{TimeoutError, TimeoutService}; use http::Uri; -use tokio_net::tcp::TcpStream; use super::connection::Connection; use super::error::ConnectError; @@ -17,10 +17,10 @@ use super::pool::{ConnectionPool, Protocol}; use super::Connect; #[cfg(feature = "openssl")] -use open_ssl::ssl::SslConnector as OpensslConnector; +use actix_connect::ssl::openssl::SslConnector as OpensslConnector; #[cfg(feature = "rustls")] -use rust_tls::ClientConfig; +use actix_connect::ssl::rustls::ClientConfig; #[cfg(feature = "rustls")] use std::sync::Arc; @@ -74,7 +74,7 @@ impl Connector<(), ()> { let ssl = { #[cfg(feature = "openssl")] { - use open_ssl::ssl::SslMethod; + use actix_connect::ssl::openssl::SslMethod; let mut ssl = OpensslConnector::builder(SslMethod::tls()).unwrap(); let _ = ssl @@ -87,9 +87,9 @@ impl Connector<(), ()> { let protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; let mut config = ClientConfig::new(); config.set_protocols(&protos); - config - .root_store - .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); + config.root_store.add_server_trust_anchors( + &actix_connect::ssl::rustls::TLS_SERVER_ROOTS, + ); SslConnector::Rustls(Arc::new(config)) } #[cfg(not(any(feature = "openssl", feature = "rustls")))] @@ -242,12 +242,10 @@ where { const H2: &[u8] = b"h2"; #[cfg(feature = "openssl")] - use actix_connect::ssl::OpensslConnector; + use actix_connect::ssl::openssl::OpensslConnector; #[cfg(feature = "rustls")] - use actix_connect::ssl::RustlsConnector; + use actix_connect::ssl::rustls::{RustlsConnector, Session}; use actix_service::{boxed::service, pipeline}; - #[cfg(feature = "rustls")] - use rust_tls::Session; let ssl_service = TimeoutService::new( self.timeout, diff --git a/actix-http/src/client/error.rs b/actix-http/src/client/error.rs index ee568e8b..42ea47ee 100644 --- a/actix-http/src/client/error.rs +++ b/actix-http/src/client/error.rs @@ -1,10 +1,10 @@ use std::io; +use actix_connect::resolver::ResolveError; use derive_more::{Display, From}; -use trust_dns_resolver::error::ResolveError; #[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::http::{Error as HttpError, StatusCode}; @@ -21,6 +21,11 @@ pub enum ConnectError { #[display(fmt = "{}", _0)] SslError(SslError), + /// SSL Handshake error + #[cfg(feature = "openssl")] + #[display(fmt = "{}", _0)] + SslHandshakeError(String), + /// Failed to resolve the hostname #[display(fmt = "Failed resolving hostname: {}", _0)] Resolver(ResolveError), @@ -63,13 +68,9 @@ impl From for ConnectError { } #[cfg(feature = "openssl")] -impl From> for ConnectError { +impl From> for ConnectError { fn from(err: HandshakeError) -> ConnectError { - match err { - HandshakeError::SetupFailure(stack) => SslError::from(stack).into(), - HandshakeError::Failure(stream) => stream.into_error().into(), - HandshakeError::WouldBlock(stream) => stream.into_error().into(), - } + ConnectError::SslHandshakeError(format!("{:?}", err)) } } diff --git a/actix-http/src/client/h1proto.rs b/actix-http/src/client/h1proto.rs index ddfc7a31..048d3795 100644 --- a/actix-http/src/client/h1proto.rs +++ b/actix-http/src/client/h1proto.rs @@ -1,10 +1,11 @@ use std::io::Write; use std::pin::Pin; use std::task::{Context, Poll}; -use std::{io, time}; +use std::{io, mem, time}; 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::{SinkExt, Stream, StreamExt}; @@ -43,7 +44,7 @@ where 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 { RequestHeadType::Owned(ref mut head) => { head.headers.insert(HOST, value) @@ -199,7 +200,10 @@ where } impl AsyncRead for H1Connection { - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { + unsafe fn prepare_uninitialized_buffer( + &self, + buf: &mut [mem::MaybeUninit], + ) -> bool { self.io.as_ref().unwrap().prepare_uninitialized_buffer(buf) } diff --git a/actix-http/src/client/h2proto.rs b/actix-http/src/client/h2proto.rs index a94562f2..ff8f21d0 100644 --- a/actix-http/src/client/h2proto.rs +++ b/actix-http/src/client/h2proto.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::time; use actix_codec::{AsyncRead, AsyncWrite}; @@ -5,7 +6,7 @@ use bytes::Bytes; use futures::future::poll_fn; use h2::{client::SendRequest, SendStream}; 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::header::HeaderMap; diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index c6103986..ef5bd596 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -12,8 +12,8 @@ use actix_service::Service; use actix_utils::{oneshot, task::LocalWaker}; use bytes::Bytes; use futures::future::{poll_fn, FutureExt, LocalBoxFuture}; +use fxhash::FxHashMap; use h2::client::{handshake, Connection, SendRequest}; -use hashbrown::HashMap; use http::uri::Authority; use indexmap::IndexSet; use slab::Slab; @@ -66,7 +66,7 @@ where acquired: 0, waiters: Slab::new(), waiters_queue: IndexSet::new(), - available: HashMap::new(), + available: FxHashMap::default(), waker: LocalWaker::new(), })), ) @@ -108,7 +108,7 @@ where let inner = self.1.clone(); 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() } else { return Err(ConnectError::Unresolverd); @@ -259,7 +259,7 @@ pub(crate) struct Inner { disconnect_timeout: Option, limit: usize, acquired: usize, - available: HashMap>>, + available: FxHashMap>>, waiters: Slab< Option<( Connect, @@ -299,7 +299,7 @@ where ) { 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 token = entry.key(); entry.insert(Some((connect, tx))); diff --git a/actix-http/src/config.rs b/actix-http/src/config.rs index bab3cdc6..77633bdc 100644 --- a/actix-http/src/config.rs +++ b/actix-http/src/config.rs @@ -1,10 +1,10 @@ use std::cell::UnsafeCell; -use std::fmt; use std::fmt::Write; 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 futures::{future, FutureExt}; use time; @@ -47,6 +47,8 @@ struct Inner { client_timeout: u64, client_disconnect: u64, ka_enabled: bool, + secure: bool, + local_addr: Option, timer: DateService, } @@ -58,7 +60,7 @@ impl Clone for ServiceConfig { impl Default for ServiceConfig { 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, client_timeout: u64, client_disconnect: u64, + secure: bool, + local_addr: Option, ) -> ServiceConfig { let (keep_alive, ka_enabled) = match keep_alive { KeepAlive::Timeout(val) => (val as u64, true), @@ -85,10 +89,24 @@ impl ServiceConfig { ka_enabled, client_timeout, client_disconnect, + secure, + local_addr, 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 { + self.0.local_addr + } + #[inline] /// Keep alive duration if configured. pub fn keep_alive(&self) -> Option { @@ -106,7 +124,7 @@ impl ServiceConfig { pub fn client_timer(&self) -> Option { let delay_time = self.0.client_timeout; if delay_time != 0 { - Some(delay( + Some(delay_until( self.0.timer.now() + Duration::from_millis(delay_time), )) } else { @@ -138,7 +156,7 @@ impl ServiceConfig { /// Return keep-alive timer delay is configured. pub fn keep_alive_timer(&self) -> Option { if let Some(ka) = self.0.keep_alive { - Some(delay(self.0.timer.now() + ka)) + Some(delay_until(self.0.timer.now() + ka)) } else { None } @@ -271,7 +289,7 @@ mod tests { #[actix_rt::test] 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); settings.set_date(&mut buf1); let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); diff --git a/actix-http/src/encoding/decoder.rs b/actix-http/src/encoding/decoder.rs index 1e51e8b5..dca77483 100644 --- a/actix-http/src/encoding/decoder.rs +++ b/actix-http/src/encoding/decoder.rs @@ -21,7 +21,7 @@ pub struct Decoder { decoder: Option, stream: S, eof: bool, - fut: Option, ContentDecoder), io::Error>>>, + fut: Option, ContentDecoder), io::Error>>, } impl Decoder @@ -85,8 +85,7 @@ where loop { if let Some(ref mut fut) = self.fut { let (chunk, decoder) = match ready!(Pin::new(fut).poll(cx)) { - Ok(Ok(item)) => item, - Ok(Err(e)) => return Poll::Ready(Some(Err(e.into()))), + Ok(item) => item, Err(e) => return Poll::Ready(Some(Err(e.into()))), }; self.decoder = Some(decoder); diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index 295d99a2..d1b64bfb 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -13,7 +13,7 @@ use flate2::write::{GzEncoder, ZlibEncoder}; use crate::body::{Body, BodySize, MessageBody, ResponseBody}; use crate::http::header::{ContentEncoding, CONTENT_ENCODING}; -use crate::http::{HeaderValue, HttpTryFrom, StatusCode}; +use crate::http::{HeaderValue, StatusCode}; use crate::{Error, ResponseHead}; use super::Writer; @@ -24,7 +24,7 @@ pub struct Encoder { eof: bool, body: EncoderBody, encoder: Option, - fut: Option>>, + fut: Option>, } impl Encoder { @@ -104,8 +104,7 @@ impl MessageBody for Encoder { if let Some(ref mut fut) = self.fut { let mut encoder = match futures::ready!(Pin::new(fut).poll(cx)) { - Ok(Ok(item)) => item, - Ok(Err(e)) => return Poll::Ready(Some(Err(e.into()))), + Ok(item) => item, Err(e) => return Poll::Ready(Some(Err(e.into()))), }; let chunk = encoder.take(); @@ -169,7 +168,7 @@ impl MessageBody for Encoder { fn update_head(encoding: ContentEncoding, head: &mut ResponseHead) { head.headers_mut().insert( CONTENT_ENCODING, - HeaderValue::try_from(Bytes::from_static(encoding.as_str().as_bytes())).unwrap(), + HeaderValue::from_static(encoding.as_str()), ); } diff --git a/actix-http/src/encoding/mod.rs b/actix-http/src/encoding/mod.rs index b55a43a7..9f65f800 100644 --- a/actix-http/src/encoding/mod.rs +++ b/actix-http/src/encoding/mod.rs @@ -20,7 +20,7 @@ impl Writer { } } fn take(&mut self) -> Bytes { - self.buf.take().freeze() + self.buf.split().freeze() } } diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 587849bd..c580f384 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -6,6 +6,7 @@ use std::str::Utf8Error; use std::string::FromUtf8Error; use std::{fmt, io, result}; +pub use actix_threadpool::BlockingError; use actix_utils::timeout::TimeoutError; use bytes::BytesMut; 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 { fn description(&self) -> &str { "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 for Error { fn from(_: std::convert::Infallible) -> Self { // `std::convert::Infallible` indicates an error @@ -181,11 +182,11 @@ impl ResponseError for FormError {} #[cfg(feature = "openssl")] /// `InternalServerError` for `openssl::ssl::Error` -impl ResponseError for open_ssl::ssl::Error {} +impl ResponseError for actix_connect::ssl::openssl::SslError {} #[cfg(feature = "openssl")] /// `InternalServerError` for `openssl::ssl::HandshakeError` -impl ResponseError for open_ssl::ssl::HandshakeError {} +impl ResponseError for actix_tls::openssl::HandshakeError {} /// Return `BAD_REQUEST` for `de::value::Error` impl ResponseError for DeError { @@ -197,6 +198,9 @@ impl ResponseError for DeError { /// `InternalServerError` for `Canceled` impl ResponseError for Canceled {} +/// `InternalServerError` for `BlockingError` +impl ResponseError for BlockingError {} + /// Return `BAD_REQUEST` for `Utf8Error` impl ResponseError for Utf8Error { 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 #[derive(Debug, Display)] pub enum ParseError { @@ -359,12 +356,15 @@ impl From for PayloadError { } } -impl From for PayloadError { - fn from(_: Canceled) -> Self { - PayloadError::Io(io::Error::new( - io::ErrorKind::Other, - "Operation is canceled", - )) +impl From> for PayloadError { + fn from(err: BlockingError) -> Self { + match err { + BlockingError::Error(e) => PayloadError::Io(e), + BlockingError::Canceled => PayloadError::Io(io::Error::new( + io::ErrorKind::Other, + "Operation is canceled", + )), + } } } diff --git a/actix-http/src/extensions.rs b/actix-http/src/extensions.rs index c6266f56..066e0a3c 100644 --- a/actix-http/src/extensions.rs +++ b/actix-http/src/extensions.rs @@ -1,12 +1,12 @@ use std::any::{Any, TypeId}; use std::fmt; -use hashbrown::HashMap; +use fxhash::FxHashMap; #[derive(Default)] /// A type map of request extensions. pub struct Extensions { - map: HashMap>, + map: FxHashMap>, } impl Extensions { @@ -14,7 +14,7 @@ impl Extensions { #[inline] pub fn new() -> Extensions { Extensions { - map: HashMap::default(), + map: FxHashMap::default(), } } diff --git a/actix-http/src/h1/decoder.rs b/actix-http/src/h1/decoder.rs index ffa00288..32ec64ba 100644 --- a/actix-http/src/h1/decoder.rs +++ b/actix-http/src/h1/decoder.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::io; use std::marker::PhantomData; use std::mem::MaybeUninit; @@ -6,7 +7,7 @@ use std::task::Poll; use actix_codec::Decoder; use bytes::{Bytes, BytesMut}; use http::header::{HeaderName, HeaderValue}; -use http::{header, HttpTryFrom, Method, StatusCode, Uri, Version}; +use http::{header, Method, StatusCode, Uri, Version}; use httparse; use log::{debug, error, trace}; @@ -79,8 +80,8 @@ pub(crate) trait MessageType: Sized { // Unsafe: httparse check header value for valid utf-8 let value = unsafe { - HeaderValue::from_shared_unchecked( - slice.slice(idx.value.0, idx.value.1), + HeaderValue::from_maybe_shared_unchecked( + slice.slice(idx.value.0..idx.value.1), ) }; match name { @@ -428,7 +429,7 @@ impl Decoder for PayloadDecoder { let len = src.len() as u64; let buf; if *remaining > len { - buf = src.take().freeze(); + buf = src.split().freeze(); *remaining -= len; } else { buf = src.split_to(*remaining as usize).freeze(); @@ -463,7 +464,7 @@ impl Decoder for PayloadDecoder { if src.is_empty() { Ok(None) } else { - Ok(Some(PayloadItem::Chunk(src.take().freeze()))) + Ok(Some(PayloadItem::Chunk(src.split().freeze()))) } } } @@ -581,7 +582,7 @@ impl ChunkedState { } else { let slice; if *rem > len { - slice = rdr.take().freeze(); + slice = rdr.split().freeze(); *rem -= len; } else { slice = rdr.split_to(*rem as usize).freeze(); diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 154b3ed4..0e2a5834 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -2,12 +2,10 @@ use std::collections::VecDeque; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; -use std::time::Instant; use std::{fmt, io, net}; -use actix_codec::{AsyncRead, Decoder, Encoder, Framed, FramedParts}; -use actix_rt::time::{delay, Delay}; -use actix_server_config::IoStream; +use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed, FramedParts}; +use actix_rt::time::{delay_until, Delay, Instant}; use actix_service::Service; use bitflags::bitflags; use bytes::{BufMut, BytesMut}; @@ -168,7 +166,7 @@ impl PartialEq for PollResponse { impl Dispatcher where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into, S::Response: Into>, @@ -186,6 +184,7 @@ where expect: CloneableService, upgrade: Option>, on_connect: Option>, + peer_addr: Option, ) -> Self { Dispatcher::with_timeout( stream, @@ -197,6 +196,7 @@ where expect, upgrade, on_connect, + peer_addr, ) } @@ -211,6 +211,7 @@ where expect: CloneableService, upgrade: Option>, on_connect: Option>, + peer_addr: Option, ) -> Self { let keepalive = config.keep_alive_enabled(); let flags = if keepalive { @@ -234,7 +235,6 @@ where payload: None, state: State::None, error: None, - peer_addr: io.peer_addr(), messages: VecDeque::new(), io, codec, @@ -244,6 +244,7 @@ where upgrade, on_connect, flags, + peer_addr, ka_expire, ka_timer, }), @@ -253,7 +254,7 @@ where impl InnerDispatcher where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into, S::Response: Into>, @@ -608,7 +609,7 @@ where // shutdown timeout if self.flags.contains(Flags::SHUTDOWN) { if let Some(interval) = self.codec.config().client_disconnect_timer() { - self.ka_timer = Some(delay(interval)); + self.ka_timer = Some(delay_until(interval)); } else { self.flags.insert(Flags::READ_DISCONNECT); if let Some(mut payload) = self.payload.take() { @@ -682,7 +683,7 @@ where impl Unpin for Dispatcher where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into, S::Response: Into>, @@ -696,7 +697,7 @@ where impl Future for Dispatcher where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into, S::Response: Into>, @@ -907,6 +908,7 @@ mod tests { CloneableService::new(ExpectHandler), None, None, + None, ); match Pin::new(&mut h1).poll(cx) { Poll::Pending => panic!(), diff --git a/actix-http/src/h1/encoder.rs b/actix-http/src/h1/encoder.rs index 6396f3b5..e83ce90c 100644 --- a/actix-http/src/h1/encoder.rs +++ b/actix-http/src/h1/encoder.rs @@ -2,11 +2,13 @@ use std::fmt::Write as FmtWrite; use std::io::Write; use std::marker::PhantomData; +use std::ptr::copy_nonoverlapping; use std::rc::Rc; +use std::slice::from_raw_parts_mut; use std::str::FromStr; use std::{cmp, fmt, io, mem}; -use bytes::{BufMut, Bytes, BytesMut}; +use bytes::{buf::BufMutExt, BufMut, Bytes, BytesMut}; use crate::body::BodySize; use crate::config::ServiceConfig; @@ -144,8 +146,8 @@ pub(crate) trait MessageType: Sized { // write headers let mut pos = 0; let mut has_date = false; - let mut remaining = dst.remaining_mut(); - let mut buf = unsafe { &mut *(dst.bytes_mut() as *mut [u8]) }; + let mut remaining = dst.capacity() - dst.len(); + let mut buf = dst.bytes_mut().as_mut_ptr() as *mut u8; for (key, value) in headers { match *key { CONNECTION => continue, @@ -159,61 +161,67 @@ pub(crate) trait MessageType: Sized { match value { map::Value::One(ref val) => { 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 { unsafe { dst.advance_mut(pos); } pos = 0; dst.reserve(len * 2); - remaining = dst.remaining_mut(); - unsafe { - buf = &mut *(dst.bytes_mut() as *mut _); - } + remaining = dst.capacity() - dst.len(); + buf = dst.bytes_mut().as_mut_ptr() as *mut u8; } // use upper Camel-Case - if camel_case { - write_camel_case(k, &mut buf[pos..pos + k.len()]); - } else { - buf[pos..pos + k.len()].copy_from_slice(k); + unsafe { + if camel_case { + write_camel_case(k, from_raw_parts_mut(buf, k_len)) + } 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) => { for val in vec { 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 { unsafe { dst.advance_mut(pos); } pos = 0; dst.reserve(len * 2); - remaining = dst.remaining_mut(); - unsafe { - buf = &mut *(dst.bytes_mut() as *mut _); - } + remaining = dst.capacity() - dst.len(); + buf = dst.bytes_mut().as_mut_ptr() as *mut u8; } // use upper Camel-Case - if camel_case { - write_camel_case(k, &mut buf[pos..pos + k.len()]); - } else { - buf[pos..pos + k.len()].copy_from_slice(k); - } - 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; + unsafe { + if camel_case { + write_camel_case(k, from_raw_parts_mut(buf, k_len)); + } 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; } } @@ -298,6 +306,12 @@ impl MessageType for RequestHeadType { Version::HTTP_10 => "HTTP/1.0", Version::HTTP_11 => "HTTP/1.1", 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)) @@ -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]) { let mut index = 0; let key = value; @@ -525,7 +543,7 @@ mod tests { assert!(enc.encode(b"", &mut bytes).ok().unwrap()); } assert_eq!( - bytes.take().freeze(), + bytes.split().freeze(), Bytes::from_static(b"4\r\ntest\r\n0\r\n\r\n") ); } @@ -548,7 +566,8 @@ mod tests { ConnectionType::Close, &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("Connection: close\r\n")); assert!(data.contains("Content-Type: plain/text\r\n")); @@ -561,7 +580,8 @@ mod tests { ConnectionType::KeepAlive, &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("Content-Type: plain/text\r\n")); assert!(data.contains("Date: date\r\n")); @@ -573,7 +593,8 @@ mod tests { ConnectionType::KeepAlive, &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-Type: plain/text\r\n")); assert!(data.contains("Date: date\r\n")); @@ -594,7 +615,8 @@ mod tests { ConnectionType::KeepAlive, &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("content-type: xml\r\n")); assert!(data.contains("content-type: plain/text\r\n")); @@ -627,7 +649,8 @@ mod tests { ConnectionType::Close, &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("connection: close\r\n")); assert!(data.contains("authorization: another authorization\r\n")); diff --git a/actix-http/src/h1/expect.rs b/actix-http/src/h1/expect.rs index d6b4a9f1..109491ba 100644 --- a/actix-http/src/h1/expect.rs +++ b/actix-http/src/h1/expect.rs @@ -1,6 +1,5 @@ use std::task::{Context, Poll}; -use actix_server_config::ServerConfig; use actix_service::{Service, ServiceFactory}; use futures::future::{ok, Ready}; @@ -10,7 +9,7 @@ use crate::request::Request; pub struct ExpectHandler; impl ServiceFactory for ExpectHandler { - type Config = ServerConfig; + type Config = (); type Request = Request; type Response = Request; type Error = Error; @@ -18,7 +17,7 @@ impl ServiceFactory for ExpectHandler { type InitError = Error; type Future = Ready>; - fn new_service(&self, _: &ServerConfig) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { ok(ExpectHandler) } } diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index 197c9288..d6494b5c 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -1,19 +1,19 @@ -use std::fmt; use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; use std::rc::Rc; use std::task::{Context, Poll}; +use std::{fmt, net}; -use actix_codec::Framed; -use actix_server_config::{Io, IoStream, ServerConfig as SrvConfig}; -use actix_service::{IntoServiceFactory, Service, ServiceFactory}; +use actix_codec::{AsyncRead, AsyncWrite, Framed}; +use actix_rt::net::TcpStream; +use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory}; use futures::future::{ok, Ready}; use futures::ready; use crate::body::MessageBody; use crate::cloneable::CloneableService; -use crate::config::{KeepAlive, ServiceConfig}; +use crate::config::ServiceConfig; use crate::error::{DispatchError, Error, ParseError}; use crate::helpers::DataFactory; use crate::request::Request; @@ -24,39 +24,25 @@ use super::dispatcher::Dispatcher; use super::{ExpectHandler, Message, UpgradeHandler}; /// `ServiceFactory` implementation for HTTP1 transport -pub struct H1Service> { +pub struct H1Service> { srv: S, cfg: ServiceConfig, expect: X, upgrade: Option, on_connect: Option Box>>, - _t: PhantomData<(T, P, B)>, + _t: PhantomData<(T, B)>, } -impl H1Service +impl H1Service where - S: ServiceFactory, + S: ServiceFactory, S::Error: Into, S::InitError: fmt::Debug, S::Response: Into>, B: MessageBody, { - /// Create new `HttpService` instance with default config. - pub fn new>(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. - pub fn with_config>( + pub(crate) fn with_config>( cfg: ServiceConfig, service: F, ) -> Self { @@ -71,15 +57,151 @@ where } } -impl H1Service +impl H1Service where - S: ServiceFactory, + S: ServiceFactory, + S::Error: Into, + S::InitError: fmt::Debug, + S::Response: Into>, + B: MessageBody, + X: ServiceFactory, + X::Error: Into, + X::InitError: fmt::Debug, + U: ServiceFactory< + Config = (), + Request = (Request, Framed), + 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 H1Service, S, B, X, U> + where + S: ServiceFactory, + S::Error: Into, + S::InitError: fmt::Debug, + S::Response: Into>, + B: MessageBody, + X: ServiceFactory, + X::Error: Into, + X::InitError: fmt::Debug, + U: ServiceFactory< + Config = (), + Request = (Request, Framed, 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, DispatchError>, + InitError = (), + > { + pipeline_factory( + Acceptor::new(acceptor) + .map_err(SslError::Ssl) + .map_init_err(|_| panic!()), + ) + .and_then(|io: SslStream| { + 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 H1Service, S, B, X, U> + where + S: ServiceFactory, + S::Error: Into, + S::InitError: fmt::Debug, + S::Response: Into>, + B: MessageBody, + X: ServiceFactory, + X::Error: Into, + X::InitError: fmt::Debug, + U: ServiceFactory< + Config = (), + Request = (Request, Framed, 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, + InitError = (), + > { + pipeline_factory( + Acceptor::new(config) + .map_err(SslError::Ssl) + .map_init_err(|_| panic!()), + ) + .and_then(|io: TlsStream| { + let peer_addr = io.get_ref().0.peer_addr().ok(); + ok((io, peer_addr)) + }) + .and_then(self.map_err(SslError::Service)) + } + } +} + +impl H1Service +where + S: ServiceFactory, S::Error: Into, S::Response: Into>, S::InitError: fmt::Debug, B: MessageBody, { - pub fn expect(self, expect: X1) -> H1Service + pub fn expect(self, expect: X1) -> H1Service where X1: ServiceFactory, X1::Error: Into, @@ -95,7 +217,7 @@ where } } - pub fn upgrade(self, upgrade: Option) -> H1Service + pub fn upgrade(self, upgrade: Option) -> H1Service where U1: ServiceFactory), Response = ()>, U1::Error: fmt::Display, @@ -121,38 +243,34 @@ where } } -impl ServiceFactory for H1Service +impl ServiceFactory for H1Service where - T: IoStream, - S: ServiceFactory, + T: AsyncRead + AsyncWrite + Unpin, + S: ServiceFactory, S::Error: Into, S::Response: Into>, S::InitError: fmt::Debug, B: MessageBody, - X: ServiceFactory, + X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - U: ServiceFactory< - Config = SrvConfig, - Request = (Request, Framed), - Response = (), - >, + U: ServiceFactory), Response = ()>, U::Error: fmt::Display, U::InitError: fmt::Debug, { - type Config = SrvConfig; - type Request = Io; + type Config = (); + type Request = (T, Option); type Response = (); type Error = DispatchError; type InitError = (); - type Service = H1ServiceHandler; - type Future = H1ServiceResponse; + type Service = H1ServiceHandler; + type Future = H1ServiceResponse; - fn new_service(&self, cfg: &SrvConfig) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { H1ServiceResponse { - fut: self.srv.new_service(cfg), - fut_ex: Some(self.expect.new_service(cfg)), - fut_upg: self.upgrade.as_ref().map(|f| f.new_service(cfg)), + fut: self.srv.new_service(()), + fut_ex: Some(self.expect.new_service(())), + fut_upg: self.upgrade.as_ref().map(|f| f.new_service(())), expect: None, upgrade: None, on_connect: self.on_connect.clone(), @@ -164,7 +282,7 @@ where #[doc(hidden)] #[pin_project::pin_project] -pub struct H1ServiceResponse +pub struct H1ServiceResponse where S: ServiceFactory, S::Error: Into, @@ -186,12 +304,12 @@ where upgrade: Option, on_connect: Option Box>>, cfg: Option, - _t: PhantomData<(T, P, B)>, + _t: PhantomData<(T, B)>, } -impl Future for H1ServiceResponse +impl Future for H1ServiceResponse where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: ServiceFactory, S::Error: Into, S::Response: Into>, @@ -204,8 +322,7 @@ where U::Error: fmt::Display, U::InitError: fmt::Debug, { - type Output = - Result, ()>; + type Output = Result, ()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { let mut this = self.as_mut().project(); @@ -247,16 +364,16 @@ where } /// `Service` implementation for HTTP1 transport -pub struct H1ServiceHandler { +pub struct H1ServiceHandler { srv: CloneableService, expect: CloneableService, upgrade: Option>, on_connect: Option Box>>, cfg: ServiceConfig, - _t: PhantomData<(T, P, B)>, + _t: PhantomData<(T, B)>, } -impl H1ServiceHandler +impl H1ServiceHandler where S: Service, S::Error: Into, @@ -273,7 +390,7 @@ where expect: X, upgrade: Option, on_connect: Option Box>>, - ) -> H1ServiceHandler { + ) -> H1ServiceHandler { H1ServiceHandler { srv: CloneableService::new(srv), expect: CloneableService::new(expect), @@ -285,9 +402,9 @@ where } } -impl Service for H1ServiceHandler +impl Service for H1ServiceHandler where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into, S::Response: Into>, @@ -297,7 +414,7 @@ where U: Service), Response = ()>, U::Error: fmt::Display, { - type Request = Io; + type Request = (T, Option); type Response = (); type Error = DispatchError; type Future = Dispatcher; @@ -331,9 +448,7 @@ where } } - fn call(&mut self, req: Self::Request) -> Self::Future { - let io = req.into_parts().0; - + fn call(&mut self, (io, addr): Self::Request) -> Self::Future { let on_connect = if let Some(ref on_connect) = self.on_connect { Some(on_connect(&io)) } else { @@ -347,20 +462,21 @@ where self.expect.clone(), self.upgrade.clone(), on_connect, + addr, ) } } /// `ServiceFactory` implementation for `OneRequestService` service #[derive(Default)] -pub struct OneRequest { +pub struct OneRequest { config: ServiceConfig, - _t: PhantomData<(T, P)>, + _t: PhantomData, } -impl OneRequest +impl OneRequest where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, { /// Create new `H1SimpleService` instance. pub fn new() -> Self { @@ -371,38 +487,38 @@ where } } -impl ServiceFactory for OneRequest +impl ServiceFactory for OneRequest where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, { - type Config = SrvConfig; - type Request = Io; + type Config = (); + type Request = T; type Response = (Request, Framed); type Error = ParseError; type InitError = (); - type Service = OneRequestService; + type Service = OneRequestService; type Future = Ready>; - fn new_service(&self, _: &SrvConfig) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { ok(OneRequestService { - config: self.config.clone(), _t: PhantomData, + config: self.config.clone(), }) } } /// `Service` implementation for HTTP1 transport. Reads one request and returns /// request and framed object. -pub struct OneRequestService { +pub struct OneRequestService { + _t: PhantomData, config: ServiceConfig, - _t: PhantomData<(T, P)>, } -impl Service for OneRequestService +impl Service for OneRequestService where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, { - type Request = Io; + type Request = T; type Response = (Request, Framed); type Error = ParseError; type Future = OneRequestServiceResponse; @@ -413,10 +529,7 @@ where fn call(&mut self, req: Self::Request) -> Self::Future { OneRequestServiceResponse { - framed: Some(Framed::new( - req.into_parts().0, - Codec::new(self.config.clone()), - )), + framed: Some(Framed::new(req, Codec::new(self.config.clone()))), } } } @@ -424,14 +537,14 @@ where #[doc(hidden)] pub struct OneRequestServiceResponse where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, { framed: Option>, } impl Future for OneRequestServiceResponse where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, { type Output = Result<(Request, Framed), ParseError>; diff --git a/actix-http/src/h1/upgrade.rs b/actix-http/src/h1/upgrade.rs index ce46fbe9..e3ce6652 100644 --- a/actix-http/src/h1/upgrade.rs +++ b/actix-http/src/h1/upgrade.rs @@ -2,7 +2,6 @@ use std::marker::PhantomData; use std::task::{Context, Poll}; use actix_codec::Framed; -use actix_server_config::ServerConfig; use actix_service::{Service, ServiceFactory}; use futures::future::Ready; @@ -13,7 +12,7 @@ use crate::request::Request; pub struct UpgradeHandler(PhantomData); impl ServiceFactory for UpgradeHandler { - type Config = ServerConfig; + type Config = (); type Request = (Request, Framed); type Response = (); type Error = Error; @@ -21,7 +20,7 @@ impl ServiceFactory for UpgradeHandler { type InitError = Error; type Future = Ready>; - fn new_service(&self, _: &ServerConfig) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { unimplemented!() } } diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 18855380..707ac7b9 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -1,14 +1,13 @@ use std::collections::VecDeque; +use std::convert::TryFrom; use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; -use std::time::Instant; use std::{fmt, mem, net}; use actix_codec::{AsyncRead, AsyncWrite}; -use actix_rt::time::Delay; -use actix_server_config::IoStream; +use actix_rt::time::{Delay, Instant}; use actix_service::Service; use bitflags::bitflags; use bytes::{Bytes, BytesMut}; @@ -18,7 +17,6 @@ use h2::{RecvStream, SendStream}; use http::header::{ HeaderValue, ACCEPT_ENCODING, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING, }; -use http::HttpTryFrom; use log::{debug, error, trace}; use crate::body::{Body, BodySize, MessageBody, ResponseBody}; @@ -36,7 +34,10 @@ const CHUNK_SIZE: usize = 16_384; /// Dispatcher for HTTP/2 protocol #[pin_project::pin_project] -pub struct Dispatcher, B: MessageBody> { +pub struct Dispatcher, B: MessageBody> +where + T: AsyncRead + AsyncWrite + Unpin, +{ service: CloneableService, connection: Connection, on_connect: Option>, @@ -49,7 +50,7 @@ pub struct Dispatcher, B: MessageBody impl Dispatcher where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into, // S::Future: 'static, @@ -95,7 +96,7 @@ where impl Future for Dispatcher where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into + 'static, S::Future: 'static, @@ -233,8 +234,9 @@ where if !has_date { let mut bytes = BytesMut::with_capacity(29); self.config.set_date_header(&mut bytes); - res.headers_mut() - .insert(DATE, HeaderValue::try_from(bytes.freeze()).unwrap()); + res.headers_mut().insert(DATE, unsafe { + HeaderValue::from_maybe_shared_unchecked(bytes.freeze()) + }); } res diff --git a/actix-http/src/h2/mod.rs b/actix-http/src/h2/mod.rs index 9c902f18..28dd7538 100644 --- a/actix-http/src/h2/mod.rs +++ b/actix-http/src/h2/mod.rs @@ -35,7 +35,7 @@ impl Stream for Payload { match Pin::new(&mut this.pl).poll_data(cx) { Poll::Ready(Some(Ok(chunk))) => { 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()))) } else { Poll::Ready(Some(Ok(chunk))) diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index 860a61f7..1bacc5c9 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -6,8 +6,11 @@ use std::task::{Context, Poll}; use std::{io, net, rc}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; -use actix_server_config::{Io, IoStream, ServerConfig as SrvConfig}; -use actix_service::{IntoServiceFactory, Service, ServiceFactory}; +use actix_rt::net::TcpStream; +use actix_service::{ + factory_fn, pipeline_factory, service_fn2, IntoServiceFactory, Service, + ServiceFactory, +}; use bytes::Bytes; use futures::future::{ok, Ready}; use futures::{ready, Stream}; @@ -23,39 +26,28 @@ use crate::helpers::DataFactory; use crate::payload::Payload; use crate::request::Request; use crate::response::Response; +use crate::Protocol; use super::dispatcher::Dispatcher; /// `ServiceFactory` implementation for HTTP2 transport -pub struct H2Service { +pub struct H2Service { srv: S, cfg: ServiceConfig, on_connect: Option Box>>, - _t: PhantomData<(T, P, B)>, + _t: PhantomData<(T, B)>, } -impl H2Service +impl H2Service where - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::Response: Into> + 'static, ::Future: 'static, B: MessageBody + 'static, { - /// Create new `HttpService` instance. - pub fn new>(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. - pub fn with_config>( + pub(crate) fn with_config>( cfg: ServiceConfig, service: F, ) -> Self { @@ -77,26 +69,144 @@ where } } -impl ServiceFactory for H2Service +impl H2Service where - T: IoStream, - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::Response: Into> + 'static, ::Future: 'static, B: MessageBody + 'static, { - type Config = SrvConfig; - type Request = Io; + /// Create simple tcp based service + 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 H2Service, S, B> + where + S: ServiceFactory, + S::Error: Into + 'static, + S::Response: Into> + 'static, + ::Future: 'static, + B: MessageBody + 'static, + { + /// Create ssl based service + pub fn openssl( + self, + acceptor: SslAcceptor, + ) -> impl ServiceFactory< + Config = (), + Request = TcpStream, + Response = (), + Error = SslError, 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| { + 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 H2Service, S, B> + where + S: ServiceFactory, + S::Error: Into + 'static, + S::Response: Into> + 'static, + ::Future: 'static, + B: MessageBody + 'static, + { + /// Create openssl based service + pub fn rustls( + self, + mut config: ServerConfig, + ) -> impl ServiceFactory< + Config = (), + Request = TcpStream, + Response = (), + Error = SslError, + 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| { + let peer_addr = io.get_ref().0.peer_addr().ok(); + ok((io, peer_addr)) + })) + })) + .and_then(self.map_err(SslError::Service)) + } + } +} + +impl ServiceFactory for H2Service +where + T: AsyncRead + AsyncWrite + Unpin, + S: ServiceFactory, + S::Error: Into + 'static, + S::Response: Into> + 'static, + ::Future: 'static, + B: MessageBody + 'static, +{ + type Config = (); + type Request = (T, Option); type Response = (); type Error = DispatchError; type InitError = S::InitError; - type Service = H2ServiceHandler; - type Future = H2ServiceResponse; + type Service = H2ServiceHandler; + type Future = H2ServiceResponse; - fn new_service(&self, cfg: &SrvConfig) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { H2ServiceResponse { - fut: self.srv.new_service(cfg), + fut: self.srv.new_service(()), cfg: Some(self.cfg.clone()), on_connect: self.on_connect.clone(), _t: PhantomData, @@ -106,24 +216,24 @@ where #[doc(hidden)] #[pin_project::pin_project] -pub struct H2ServiceResponse { +pub struct H2ServiceResponse { #[pin] fut: S::Future, cfg: Option, on_connect: Option Box>>, - _t: PhantomData<(T, P, B)>, + _t: PhantomData<(T, B)>, } -impl Future for H2ServiceResponse +impl Future for H2ServiceResponse where - T: IoStream, - S: ServiceFactory, + T: AsyncRead + AsyncWrite + Unpin, + S: ServiceFactory, S::Error: Into + 'static, S::Response: Into> + 'static, ::Future: 'static, B: MessageBody + 'static, { - type Output = Result, S::InitError>; + type Output = Result, S::InitError>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { let this = self.as_mut().project(); @@ -140,14 +250,14 @@ where } /// `Service` implementation for http/2 transport -pub struct H2ServiceHandler { +pub struct H2ServiceHandler { srv: CloneableService, cfg: ServiceConfig, on_connect: Option Box>>, - _t: PhantomData<(T, P, B)>, + _t: PhantomData<(T, B)>, } -impl H2ServiceHandler +impl H2ServiceHandler where S: Service, S::Error: Into + 'static, @@ -159,7 +269,7 @@ where cfg: ServiceConfig, on_connect: Option Box>>, srv: S, - ) -> H2ServiceHandler { + ) -> H2ServiceHandler { H2ServiceHandler { cfg, on_connect, @@ -169,16 +279,16 @@ where } } -impl Service for H2ServiceHandler +impl Service for H2ServiceHandler where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into + 'static, S::Future: 'static, S::Response: Into> + 'static, B: MessageBody + 'static, { - type Request = Io; + type Request = (T, Option); type Response = (); type Error = DispatchError; type Future = H2ServiceHandlerResponse; @@ -191,9 +301,7 @@ where }) } - fn call(&mut self, req: Self::Request) -> Self::Future { - let io = req.into_parts().0; - let peer_addr = io.peer_addr(); + fn call(&mut self, (io, addr): Self::Request) -> Self::Future { let on_connect = if let Some(ref on_connect) = self.on_connect { Some(on_connect(&io)) } else { @@ -204,7 +312,7 @@ where state: State::Handshake( Some(self.srv.clone()), Some(self.cfg.clone()), - peer_addr, + addr, on_connect, server::handshake(io), ), @@ -212,8 +320,9 @@ where } } -enum State, B: MessageBody> +enum State, B: MessageBody> where + T: AsyncRead + AsyncWrite + Unpin, S::Future: 'static, { Incoming(Dispatcher), @@ -228,7 +337,7 @@ where pub struct H2ServiceHandlerResponse where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into + 'static, S::Future: 'static, @@ -240,7 +349,7 @@ where impl Future for H2ServiceHandlerResponse where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into + 'static, S::Future: 'static, diff --git a/actix-http/src/header/common/cache_control.rs b/actix-http/src/header/common/cache_control.rs index 55774619..a3253b85 100644 --- a/actix-http/src/header/common/cache_control.rs +++ b/actix-http/src/header/common/cache_control.rs @@ -80,12 +80,12 @@ impl fmt::Display for CacheControl { } impl IntoHeaderValue for CacheControl { - type Error = header::InvalidHeaderValueBytes; + type Error = header::InvalidHeaderValue; fn try_into(self) -> Result { let mut writer = Writer::new(); let _ = write!(&mut writer, "{}", self); - header::HeaderValue::from_shared(writer.take()) + header::HeaderValue::from_maybe_shared(writer.take()) } } diff --git a/actix-http/src/header/common/content_disposition.rs b/actix-http/src/header/common/content_disposition.rs index b2b6f34d..d09024f3 100644 --- a/actix-http/src/header/common/content_disposition.rs +++ b/actix-http/src/header/common/content_disposition.rs @@ -462,12 +462,12 @@ impl ContentDisposition { } impl IntoHeaderValue for ContentDisposition { - type Error = header::InvalidHeaderValueBytes; + type Error = header::InvalidHeaderValue; fn try_into(self) -> Result { let mut writer = Writer::new(); 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. (And now, only UTF-8 is handled by this implementation.) */ - let a = - HeaderValue::from_str("form-data; name=upload; filename=\"文件.webp\"") - .unwrap(); + let a = HeaderValue::from_str("form-data; name=upload; filename=\"文件.webp\"") + .unwrap(); let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap(); let b = ContentDisposition { disposition: DispositionType::FormData, @@ -884,7 +883,11 @@ mod tests { assert!(ContentDisposition::from_raw(&a).is_err()); 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] diff --git a/actix-http/src/header/common/content_range.rs b/actix-http/src/header/common/content_range.rs index cc7f2754..a3e4d49b 100644 --- a/actix-http/src/header/common/content_range.rs +++ b/actix-http/src/header/common/content_range.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use crate::error::ParseError; use crate::header::{ - HeaderValue, IntoHeaderValue, InvalidHeaderValueBytes, Writer, CONTENT_RANGE, + HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer, CONTENT_RANGE, }; header! { @@ -198,11 +198,11 @@ impl Display for ContentRangeSpec { } impl IntoHeaderValue for ContentRangeSpec { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; fn try_into(self) -> Result { let mut writer = Writer::new(); let _ = write!(&mut writer, "{}", self); - HeaderValue::from_shared(writer.take()) + HeaderValue::from_maybe_shared(writer.take()) } } diff --git a/actix-http/src/header/common/if_range.rs b/actix-http/src/header/common/if_range.rs index e910ebd9..ea5d69a1 100644 --- a/actix-http/src/header/common/if_range.rs +++ b/actix-http/src/header/common/if_range.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Display, Write}; use crate::error::ParseError; use crate::header::{ self, from_one_raw_str, EntityTag, Header, HeaderName, HeaderValue, HttpDate, - IntoHeaderValue, InvalidHeaderValueBytes, Writer, + IntoHeaderValue, InvalidHeaderValue, Writer, }; use crate::httpmessage::HttpMessage; @@ -96,12 +96,12 @@ impl Display for IfRange { } impl IntoHeaderValue for IfRange { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; fn try_into(self) -> Result { let mut writer = Writer::new(); let _ = write!(&mut writer, "{}", self); - HeaderValue::from_shared(writer.take()) + HeaderValue::from_maybe_shared(writer.take()) } } diff --git a/actix-http/src/header/common/mod.rs b/actix-http/src/header/common/mod.rs index 30dfcaa6..814050b1 100644 --- a/actix-http/src/header/common/mod.rs +++ b/actix-http/src/header/common/mod.rs @@ -164,13 +164,13 @@ macro_rules! header { } } 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> { use std::fmt::Write; let mut writer = $crate::http::header::Writer::new(); 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 { - type Error = $crate::http::header::InvalidHeaderValueBytes; + type Error = $crate::http::header::InvalidHeaderValue; fn try_into(self) -> Result<$crate::http::header::HeaderValue, Self::Error> { use std::fmt::Write; let mut writer = $crate::http::header::Writer::new(); 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 { - type Error = $crate::http::header::InvalidHeaderValueBytes; + type Error = $crate::http::header::InvalidHeaderValue; fn try_into(self) -> Result<$crate::http::header::HeaderValue, Self::Error> { self.0.try_into() @@ -285,13 +285,13 @@ macro_rules! header { } } 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> { use std::fmt::Write; let mut writer = $crate::http::header::Writer::new(); let _ = write!(&mut writer, "{}", self); - $crate::http::header::HeaderValue::from_shared(writer.take()) + $crate::http::header::HeaderValue::from_maybe_shared(writer.take()) } } }; diff --git a/actix-http/src/header/map.rs b/actix-http/src/header/map.rs index f2f1ba51..dc49d53f 100644 --- a/actix-http/src/header/map.rs +++ b/actix-http/src/header/map.rs @@ -1,8 +1,9 @@ +use std::collections::hash_map::{self, Entry}; +use std::convert::TryFrom; + use either::Either; -use hashbrown::hash_map::{self, Entry}; -use hashbrown::HashMap; +use fxhash::FxHashMap; use http::header::{HeaderName, HeaderValue}; -use http::HttpTryFrom; /// A set of HTTP headers /// @@ -11,7 +12,7 @@ use http::HttpTryFrom; /// [`HeaderName`]: struct.HeaderName.html #[derive(Debug, Clone)] pub struct HeaderMap { - pub(crate) inner: HashMap, + pub(crate) inner: FxHashMap, } #[derive(Debug, Clone)] @@ -56,7 +57,7 @@ impl HeaderMap { /// allocate. pub fn new() -> Self { HeaderMap { - inner: HashMap::new(), + inner: FxHashMap::default(), } } @@ -70,7 +71,7 @@ impl HeaderMap { /// More capacity than requested may be allocated. pub fn with_capacity(capacity: usize) -> HeaderMap { HeaderMap { - inner: HashMap::with_capacity(capacity), + inner: FxHashMap::with_capacity_and_hasher(capacity, Default::default()), } } diff --git a/actix-http/src/header/mod.rs b/actix-http/src/header/mod.rs index 59cbb11c..6fd3c1b9 100644 --- a/actix-http/src/header/mod.rs +++ b/actix-http/src/header/mod.rs @@ -1,6 +1,7 @@ //! Various http headers // 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 bytes::{Bytes, BytesMut}; @@ -73,58 +74,58 @@ impl<'a> IntoHeaderValue for &'a [u8] { } impl IntoHeaderValue for Bytes { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; #[inline] fn try_into(self) -> Result { - HeaderValue::from_shared(self) + HeaderValue::from_maybe_shared(self) } } impl IntoHeaderValue for Vec { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; #[inline] fn try_into(self) -> Result { - HeaderValue::from_shared(Bytes::from(self)) + HeaderValue::try_from(self) } } impl IntoHeaderValue for String { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; #[inline] fn try_into(self) -> Result { - HeaderValue::from_shared(Bytes::from(self)) + HeaderValue::try_from(self) } } impl IntoHeaderValue for usize { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; #[inline] fn try_into(self) -> Result { let s = format!("{}", self); - HeaderValue::from_shared(Bytes::from(s)) + HeaderValue::try_from(s) } } impl IntoHeaderValue for u64 { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; #[inline] fn try_into(self) -> Result { let s = format!("{}", self); - HeaderValue::from_shared(Bytes::from(s)) + HeaderValue::try_from(s) } } impl IntoHeaderValue for Mime { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; #[inline] fn try_into(self) -> Result { - HeaderValue::from_shared(Bytes::from(format!("{}", self))) + HeaderValue::try_from(format!("{}", self)) } } @@ -204,7 +205,7 @@ impl Writer { } } fn take(&mut self) -> Bytes { - self.buf.take().freeze() + self.buf.split().freeze() } } diff --git a/actix-http/src/header/shared/entity.rs b/actix-http/src/header/shared/entity.rs index da02dc19..7ef51a7d 100644 --- a/actix-http/src/header/shared/entity.rs +++ b/actix-http/src/header/shared/entity.rs @@ -1,7 +1,7 @@ use std::fmt::{self, Display, Write}; 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: /// 1. `%x21`, or @@ -157,12 +157,12 @@ impl FromStr for EntityTag { } impl IntoHeaderValue for EntityTag { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; fn try_into(self) -> Result { let mut wrt = Writer::new(); write!(wrt, "{}", self).unwrap(); - HeaderValue::from_shared(wrt.take()) + HeaderValue::from_maybe_shared(wrt.take()) } } diff --git a/actix-http/src/header/shared/httpdate.rs b/actix-http/src/header/shared/httpdate.rs index 350f77bb..c8d26ef5 100644 --- a/actix-http/src/header/shared/httpdate.rs +++ b/actix-http/src/header/shared/httpdate.rs @@ -3,8 +3,8 @@ use std::io::Write; use std::str::FromStr; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use bytes::{BufMut, BytesMut}; -use http::header::{HeaderValue, InvalidHeaderValueBytes}; +use bytes::{buf::BufMutExt, BytesMut}; +use http::header::{HeaderValue, InvalidHeaderValue}; use crate::error::ParseError; use crate::header::IntoHeaderValue; @@ -58,12 +58,12 @@ impl From for HttpDate { } impl IntoHeaderValue for HttpDate { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; fn try_into(self) -> Result { let mut wrt = BytesMut::with_capacity(29).writer(); write!(wrt, "{}", self.0.rfc822()).unwrap(); - HeaderValue::from_shared(wrt.get_mut().take().freeze()) + HeaderValue::from_maybe_shared(wrt.get_mut().split().freeze()) } } diff --git a/actix-http/src/helpers.rs b/actix-http/src/helpers.rs index 84403d8f..58ebff61 100644 --- a/actix-http/src/helpers.rs +++ b/actix-http/src/helpers.rs @@ -60,7 +60,7 @@ pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesM bytes.put_slice(&buf); if four { - bytes.put(b' '); + bytes.put_u8(b' '); } } @@ -203,33 +203,33 @@ mod tests { let mut bytes = BytesMut::new(); bytes.reserve(50); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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"[..]); } } diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index b57fdddc..190d5fbd 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -51,7 +51,7 @@ pub mod http { // re-exports pub use http::header::{HeaderName, HeaderValue}; 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 crate::cookie::{Cookie, CookieBuilder}; @@ -64,3 +64,10 @@ pub mod http { pub use crate::header::ContentEncoding; pub use crate::message::ConnectionType; } + +/// Http protocol +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum Protocol { + Http1, + Http2, +} diff --git a/actix-http/src/request.rs b/actix-http/src/request.rs index 77ece01c..371e2ccd 100644 --- a/actix-http/src/request.rs +++ b/actix-http/src/request.rs @@ -187,7 +187,7 @@ impl

fmt::Debug for Request

{ #[cfg(test)] mod tests { use super::*; - use http::HttpTryFrom; + use std::convert::TryFrom; #[test] fn test_basics() { diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index e9147aa4..e7f145d3 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -1,12 +1,12 @@ //! Http response use std::cell::{Ref, RefMut}; +use std::convert::TryFrom; use std::future::Future; -use std::io::Write; use std::pin::Pin; use std::task::{Context, Poll}; use std::{fmt, str}; -use bytes::{BufMut, Bytes, BytesMut}; +use bytes::{Bytes, BytesMut}; use futures::stream::Stream; use serde::Serialize; use serde_json; @@ -17,7 +17,7 @@ use crate::error::Error; use crate::extensions::Extensions; use crate::header::{Header, IntoHeaderValue}; 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}; /// An HTTP Response @@ -384,7 +384,8 @@ impl ResponseBuilder { /// ``` pub fn header(&mut self, key: K, value: V) -> &mut Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { if let Some(parts) = parts(&mut self.head, &self.err) { @@ -416,7 +417,8 @@ impl ResponseBuilder { /// ``` pub fn set_header(&mut self, key: K, value: V) -> &mut Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { if let Some(parts) = parts(&mut self.head, &self.err) { @@ -485,7 +487,8 @@ impl ResponseBuilder { #[inline] pub fn content_type(&mut self, value: V) -> &mut Self where - HeaderValue: HttpTryFrom, + HeaderValue: TryFrom, + >::Error: Into, { if let Some(parts) = parts(&mut self.head, &self.err) { match HeaderValue::try_from(value) { @@ -501,9 +504,7 @@ impl ResponseBuilder { /// Set content length #[inline] pub fn content_length(&mut self, len: u64) -> &mut Self { - let mut wrt = BytesMut::new().writer(); - let _ = write!(wrt, "{}", len); - self.header(header::CONTENT_LENGTH, wrt.get_mut().take().freeze()) + self.header(header::CONTENT_LENGTH, len) } /// Set a cookie diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index 7340c15f..3624060b 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -1,15 +1,13 @@ use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; -use std::{fmt, io, net, rc}; +use std::{fmt, net, rc}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; -use actix_server_config::{ - Io as ServerIo, IoStream, Protocol, ServerConfig as SrvConfig, -}; -use actix_service::{IntoServiceFactory, Service, ServiceFactory}; -use bytes::{BufMut, Bytes, BytesMut}; -use futures::{ready, Future}; +use actix_rt::net::TcpStream; +use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory}; +use bytes::Bytes; +use futures::{future::ok, ready, Future}; use h2::server::{self, Handshake}; use pin_project::{pin_project, project}; @@ -21,21 +19,21 @@ use crate::error::{DispatchError, Error}; use crate::helpers::DataFactory; use crate::request::Request; use crate::response::Response; -use crate::{h1, h2::Dispatcher}; +use crate::{h1, h2::Dispatcher, Protocol}; /// `ServiceFactory` HTTP1.1/HTTP2 transport implementation -pub struct HttpService> { +pub struct HttpService> { srv: S, cfg: ServiceConfig, expect: X, upgrade: Option, on_connect: Option Box>>, - _t: PhantomData<(T, P, B)>, + _t: PhantomData<(T, B)>, } -impl HttpService +impl HttpService where - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::InitError: fmt::Debug, S::Response: Into> + 'static, @@ -48,9 +46,9 @@ where } } -impl HttpService +impl HttpService where - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::InitError: fmt::Debug, S::Response: Into> + 'static, @@ -59,7 +57,7 @@ where { /// Create new `HttpService` instance. pub fn new>(service: F) -> Self { - let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0); + let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0, false, None); HttpService { cfg, @@ -87,9 +85,9 @@ where } } -impl HttpService +impl HttpService where - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::InitError: fmt::Debug, S::Response: Into> + 'static, @@ -101,9 +99,9 @@ where /// Service get called with request that contains `EXPECT` header. /// Service must return request in case of success, in that case /// request will be forwarded to main service. - pub fn expect(self, expect: X1) -> HttpService + pub fn expect(self, expect: X1) -> HttpService where - X1: ServiceFactory, + X1: ServiceFactory, X1::Error: Into, X1::InitError: fmt::Debug, ::Future: 'static, @@ -122,10 +120,10 @@ where /// /// If service is provided then normal requests handling get halted /// and this service get called with original request and framed object. - pub fn upgrade(self, upgrade: Option) -> HttpService + pub fn upgrade(self, upgrade: Option) -> HttpService where U1: ServiceFactory< - Config = SrvConfig, + Config = (), Request = (Request, Framed), Response = (), >, @@ -153,21 +151,186 @@ where } } -impl ServiceFactory for HttpService +impl HttpService where - T: IoStream, - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::InitError: fmt::Debug, S::Response: Into> + 'static, ::Future: 'static, B: MessageBody + 'static, - X: ServiceFactory, + X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, ::Future: 'static, U: ServiceFactory< - Config = SrvConfig, + Config = (), + Request = (Request, Framed), + Response = (), + >, + U::Error: fmt::Display, + U::InitError: fmt::Debug, + ::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 HttpService, S, B, X, U> + where + S: ServiceFactory, + S::Error: Into + 'static, + S::InitError: fmt::Debug, + S::Response: Into> + 'static, + ::Future: 'static, + B: MessageBody + 'static, + X: ServiceFactory, + X::Error: Into, + X::InitError: fmt::Debug, + ::Future: 'static, + U: ServiceFactory< + Config = (), + Request = (Request, Framed, h1::Codec>), + Response = (), + >, + U::Error: fmt::Display, + U::InitError: fmt::Debug, + ::Future: 'static, + { + /// Create openssl based service + pub fn openssl( + self, + acceptor: SslAcceptor, + ) -> impl ServiceFactory< + Config = (), + Request = TcpStream, + Response = (), + Error = SslError, DispatchError>, + InitError = (), + > { + pipeline_factory( + Acceptor::new(acceptor) + .map_err(SslError::Ssl) + .map_init_err(|_| panic!()), + ) + .and_then(|io: SslStream| { + 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 HttpService, S, B, X, U> + where + S: ServiceFactory, + S::Error: Into + 'static, + S::InitError: fmt::Debug, + S::Response: Into> + 'static, + ::Future: 'static, + B: MessageBody + 'static, + X: ServiceFactory, + X::Error: Into, + X::InitError: fmt::Debug, + ::Future: 'static, + U: ServiceFactory< + Config = (), + Request = (Request, Framed, h1::Codec>), + Response = (), + >, + U::Error: fmt::Display, + U::InitError: fmt::Debug, + ::Future: 'static, + { + /// Create openssl based service + pub fn rustls( + self, + mut config: ServerConfig, + ) -> impl ServiceFactory< + Config = (), + Request = TcpStream, + Response = (), + Error = SslError, + 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| { + 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 ServiceFactory for HttpService +where + T: AsyncRead + AsyncWrite + Unpin, + S: ServiceFactory, + S::Error: Into + 'static, + S::InitError: fmt::Debug, + S::Response: Into> + 'static, + ::Future: 'static, + B: MessageBody + 'static, + X: ServiceFactory, + X::Error: Into, + X::InitError: fmt::Debug, + ::Future: 'static, + U: ServiceFactory< + Config = (), Request = (Request, Framed), Response = (), >, @@ -175,23 +338,23 @@ where U::InitError: fmt::Debug, ::Future: 'static, { - type Config = SrvConfig; - type Request = ServerIo; + type Config = (); + type Request = (T, Protocol, Option); type Response = (); type Error = DispatchError; type InitError = (); - type Service = HttpServiceHandler; - type Future = HttpServiceResponse; + type Service = HttpServiceHandler; + type Future = HttpServiceResponse; - fn new_service(&self, cfg: &SrvConfig) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { HttpServiceResponse { - fut: self.srv.new_service(cfg), - fut_ex: Some(self.expect.new_service(cfg)), - fut_upg: self.upgrade.as_ref().map(|f| f.new_service(cfg)), + fut: self.srv.new_service(()), + fut_ex: Some(self.expect.new_service(())), + fut_upg: self.upgrade.as_ref().map(|f| f.new_service(())), expect: None, upgrade: None, on_connect: self.on_connect.clone(), - cfg: Some(self.cfg.clone()), + cfg: self.cfg.clone(), _t: PhantomData, } } @@ -201,7 +364,6 @@ where #[pin_project] pub struct HttpServiceResponse< T, - P, S: ServiceFactory, B, X: ServiceFactory, @@ -216,13 +378,13 @@ pub struct HttpServiceResponse< expect: Option, upgrade: Option, on_connect: Option Box>>, - cfg: Option, - _t: PhantomData<(T, P, B)>, + cfg: ServiceConfig, + _t: PhantomData<(T, B)>, } -impl Future for HttpServiceResponse +impl Future for HttpServiceResponse where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: ServiceFactory, S::Error: Into + 'static, S::InitError: fmt::Debug, @@ -239,7 +401,7 @@ where ::Future: 'static, { type Output = - Result, ()>; + Result, ()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { let mut this = self.as_mut().project(); @@ -269,7 +431,7 @@ where Poll::Ready(result.map(|service| { let this = self.as_mut().project(); HttpServiceHandler::new( - this.cfg.take().unwrap(), + this.cfg.clone(), service, this.expect.take().unwrap(), this.upgrade.take(), @@ -280,16 +442,16 @@ where } /// `Service` implementation for http transport -pub struct HttpServiceHandler { +pub struct HttpServiceHandler { srv: CloneableService, expect: CloneableService, upgrade: Option>, cfg: ServiceConfig, on_connect: Option Box>>, - _t: PhantomData<(T, P, B, X)>, + _t: PhantomData<(T, B, X)>, } -impl HttpServiceHandler +impl HttpServiceHandler where S: Service, S::Error: Into + 'static, @@ -307,7 +469,7 @@ where expect: X, upgrade: Option, on_connect: Option Box>>, - ) -> HttpServiceHandler { + ) -> HttpServiceHandler { HttpServiceHandler { cfg, on_connect, @@ -319,9 +481,9 @@ where } } -impl Service for HttpServiceHandler +impl Service for HttpServiceHandler where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into + 'static, S::Future: 'static, @@ -332,7 +494,7 @@ where U: Service), Response = ()>, U::Error: fmt::Display, { - type Request = ServerIo; + type Request = (T, Protocol, Option); type Response = (); type Error = DispatchError; type Future = HttpServiceHandlerResponse; @@ -366,9 +528,7 @@ where } } - fn call(&mut self, req: Self::Request) -> Self::Future { - let (io, _, proto) = req.into_parts(); - + fn call(&mut self, (io, proto, peer_addr): Self::Request) -> Self::Future { let on_connect = if let Some(ref on_connect) = self.on_connect { Some(on_connect(&io)) } else { @@ -376,23 +536,16 @@ where }; match proto { - Protocol::Http2 => { - let peer_addr = io.peer_addr(); - let io = Io { - inner: io, - unread: None, - }; - HttpServiceHandlerResponse { - state: State::Handshake(Some(( - server::handshake(io), - self.cfg.clone(), - self.srv.clone(), - peer_addr, - on_connect, - ))), - } - } - Protocol::Http10 | Protocol::Http11 => HttpServiceHandlerResponse { + Protocol::Http2 => HttpServiceHandlerResponse { + state: State::H2Handshake(Some(( + server::handshake(io), + self.cfg.clone(), + self.srv.clone(), + on_connect, + peer_addr, + ))), + }, + Protocol::Http1 => HttpServiceHandlerResponse { state: State::H1(h1::Dispatcher::new( io, self.cfg.clone(), @@ -400,19 +553,9 @@ where self.expect.clone(), self.upgrade.clone(), 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, S::Future: 'static, S::Error: Into, - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, B: MessageBody, X: Service, X::Error: Into, @@ -431,25 +574,14 @@ where U::Error: fmt::Display, { H1(#[pin] h1::Dispatcher), - H2(#[pin] Dispatcher, S, B>), - Unknown( + H2(#[pin] Dispatcher), + H2Handshake( Option<( - T, - BytesMut, + Handshake, ServiceConfig, CloneableService, - CloneableService, - Option>, Option>, - )>, - ), - Handshake( - Option<( - Handshake, Bytes>, - ServiceConfig, - CloneableService, Option, - Option>, )>, ), } @@ -457,7 +589,7 @@ where #[pin_project] pub struct HttpServiceHandlerResponse where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into + 'static, S::Future: 'static, @@ -472,11 +604,9 @@ where state: State, } -const HTTP2_PREFACE: [u8; 14] = *b"PRI * HTTP/2.0"; - impl Future for HttpServiceHandlerResponse where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into + 'static, S::Future: 'static, @@ -496,7 +626,7 @@ where impl State where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into + 'static, S::Response: Into> + 'static, @@ -515,57 +645,7 @@ where match self.as_mut().project() { State::H1(disp) => disp.poll(cx), State::H2(disp) => disp.poll(cx), - State::Unknown(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) => { + State::H2Handshake(ref mut data) => { let conn = if let Some(ref mut item) = data { match Pin::new(&mut item.0).poll(cx) { Poll::Ready(Ok(conn)) => conn, @@ -578,7 +658,7 @@ where } else { 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( srv, conn, on_connect, cfg, None, peer_addr, ))); @@ -587,117 +667,3 @@ where } } } - -/// Wrapper for `AsyncRead + AsyncWrite` types -#[pin_project::pin_project] -struct Io { - unread: Option, - #[pin] - inner: T, -} - -impl io::Read for Io { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - 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 io::Write for Io { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.inner.write(buf) - } - fn flush(&mut self) -> io::Result<()> { - self.inner.flush() - } -} - -impl AsyncRead for Io { - // 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> { - 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> { - // self.get_mut().inner.poll_read_vectored(cx, bufs) - // } -} - -impl actix_codec::AsyncWrite for Io { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - self.project().inner.poll_write(cx, buf) - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.project().inner.poll_flush(cx) - } - - fn poll_shutdown( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.project().inner.poll_shutdown(cx) - } -} - -impl actix_server_config::IoStream for Io { - #[inline] - fn peer_addr(&self) -> Option { - 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) -> io::Result<()> { - self.inner.set_linger(dur) - } - - #[inline] - fn set_keepalive(&mut self, dur: Option) -> io::Result<()> { - self.inner.set_keepalive(dur) - } -} diff --git a/actix-http/src/test.rs b/actix-http/src/test.rs index 744f057d..b629ad78 100644 --- a/actix-http/src/test.rs +++ b/actix-http/src/test.rs @@ -1,4 +1,5 @@ //! Test Various helpers for Actix applications to use during testing. +use std::convert::TryFrom; use std::fmt::Write as FmtWrite; use std::io::{self, Read, Write}; use std::pin::Pin; @@ -6,10 +7,9 @@ use std::str::FromStr; use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite}; -use actix_server_config::IoStream; use bytes::{Bytes, BytesMut}; 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 crate::cookie::{Cookie, CookieJar, USERINFO}; @@ -83,7 +83,8 @@ impl TestRequest { /// Create TestRequest and set header pub fn with_header(key: K, value: V) -> TestRequest where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { TestRequest::default().header(key, value).take() @@ -119,7 +120,8 @@ impl TestRequest { /// Set a header pub fn header(&mut self, key: K, value: V) -> &mut Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { if let Ok(key) = HeaderName::try_from(key) { @@ -272,17 +274,3 @@ impl AsyncWrite for TestBuffer { Poll::Ready(Ok(())) } } - -impl IoStream for TestBuffer { - fn set_nodelay(&mut self, _nodelay: bool) -> io::Result<()> { - Ok(()) - } - - fn set_linger(&mut self, _dur: Option) -> io::Result<()> { - Ok(()) - } - - fn set_keepalive(&mut self, _dur: Option) -> io::Result<()> { - Ok(()) - } -} diff --git a/actix-http/src/ws/frame.rs b/actix-http/src/ws/frame.rs index 46e9f36d..0949b711 100644 --- a/actix-http/src/ws/frame.rs +++ b/actix-http/src/ws/frame.rs @@ -180,11 +180,11 @@ impl Parser { } else if payload_len <= 65_535 { dst.reserve(p_len + 4 + if mask { 4 } else { 0 }); dst.put_slice(&[one, two | 126]); - dst.put_u16_be(payload_len as u16); + dst.put_u16(payload_len as u16); } else { dst.reserve(p_len + 10 + if mask { 4 } else { 0 }); dst.put_slice(&[one, two | 127]); - dst.put_u64_be(payload_len as u64); + dst.put_u64(payload_len as u64); }; if mask { diff --git a/actix-http/tests/test_client.rs b/actix-http/tests/test_client.rs index cdcaea02..711ee7af 100644 --- a/actix-http/tests/test_client.rs +++ b/actix-http/tests/test_client.rs @@ -30,7 +30,9 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[actix_rt::test] async fn test_h1_v2() { 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(); @@ -57,6 +59,7 @@ async fn test_connection_close() { let srv = TestServer::start(move || { HttpService::build() .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) + .tcp() .map(|_| ()) }); @@ -75,6 +78,7 @@ async fn test_with_query_parameter() { ok::<_, ()>(Response::BadRequest().finish()) } }) + .tcp() .map(|_| ()) }); diff --git a/actix-http/tests/test_openssl.rs b/actix-http/tests/test_openssl.rs index 0fdddaa1..35e234af 100644 --- a/actix-http/tests/test_openssl.rs +++ b/actix-http/tests/test_openssl.rs @@ -1,11 +1,8 @@ #![cfg(feature = "openssl")] use std::io; -use actix_codec::{AsyncRead, AsyncWrite}; use actix_http_test::TestServer; -use actix_server::ssl::OpensslAcceptor; -use actix_server_config::ServerConfig; -use actix_service::{factory_fn_cfg, pipeline_factory, service_fn2, ServiceFactory}; +use actix_service::{service_fn, ServiceFactory}; use bytes::{Bytes, BytesMut}; use futures::future::{err, ok, ready}; @@ -36,7 +33,7 @@ where Ok(body) } -fn ssl_acceptor() -> io::Result> { +fn ssl_acceptor() -> SslAcceptor { // load ssl keys let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); builder @@ -47,30 +44,29 @@ fn ssl_acceptor() -> io::Result io::Result<()> { - let openssl = ssl_acceptor()?; let srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| ok::<_, Error>(Response::Ok().finish())) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| ok::<_, Error>(Response::Ok().finish())) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send().await.unwrap(); @@ -80,22 +76,15 @@ async fn test_h2() -> io::Result<()> { #[actix_rt::test] async fn test_h2_1() -> io::Result<()> { - let openssl = ssl_acceptor()?; let srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .finish(|req: Request| { - assert!(req.peer_addr().is_some()); - assert_eq!(req.version(), Version::HTTP_2); - ok::<_, Error>(Response::Ok().finish()) - }) - .map_err(|_| ()), - ) + HttpService::build() + .finish(|req: Request| { + assert!(req.peer_addr().is_some()); + assert_eq!(req.version(), Version::HTTP_2); + ok::<_, Error>(Response::Ok().finish()) + }) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send().await.unwrap(); @@ -106,23 +95,16 @@ async fn test_h2_1() -> io::Result<()> { #[actix_rt::test] async fn test_h2_body() -> io::Result<()> { let data = "HELLOWORLD".to_owned().repeat(64 * 1024); - let openssl = ssl_acceptor()?; let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|mut req: Request<_>| { - async move { - let body = load_body(req.take_payload()).await?; - Ok::<_, Error>(Response::Ok().body(body)) - } - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|mut req: Request<_>| { + async move { + let body = load_body(req.take_payload()).await?; + Ok::<_, Error>(Response::Ok().body(body)) + } + }) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send_body(data.clone()).await.unwrap(); @@ -135,30 +117,22 @@ async fn test_h2_body() -> io::Result<()> { #[actix_rt::test] async fn test_h2_content_length() { - let openssl = ssl_acceptor().unwrap(); - let srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|req: Request| { - let indx: usize = req.uri().path()[1..].parse().unwrap(); - let statuses = [ - StatusCode::NO_CONTENT, - StatusCode::CONTINUE, - StatusCode::SWITCHING_PROTOCOLS, - StatusCode::PROCESSING, - StatusCode::OK, - StatusCode::NOT_FOUND, - ]; - ok::<_, ()>(Response::new(statuses[indx])) - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|req: Request| { + let indx: usize = req.uri().path()[1..].parse().unwrap(); + let statuses = [ + StatusCode::NO_CONTENT, + StatusCode::CONTINUE, + StatusCode::SWITCHING_PROTOCOLS, + StatusCode::PROCESSING, + StatusCode::OK, + StatusCode::NOT_FOUND, + ]; + ok::<_, ()>(Response::new(statuses[indx])) + }) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let header = HeaderName::from_static("content-length"); @@ -193,14 +167,9 @@ async fn test_h2_content_length() { async fn test_h2_headers() { let data = STR.repeat(10); let data2 = data.clone(); - let openssl = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { let data = data.clone(); - pipeline_factory(openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e))) - .and_then( HttpService::build().h2(move |_| { let mut builder = Response::Ok(); for idx in 0..90 { @@ -222,7 +191,9 @@ async fn test_h2_headers() { ); } ok::<_, ()>(builder.body(data.clone())) - }).map_err(|_| ())) + }) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); 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] async fn test_h2_body2() { - let openssl = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send().await.unwrap(); @@ -281,18 +245,11 @@ async fn test_h2_body2() { #[actix_rt::test] async fn test_h2_head_empty() { - let openssl = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) + HttpService::build() + .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.shead("/").send().await.unwrap(); @@ -311,22 +268,13 @@ async fn test_h2_head_empty() { #[actix_rt::test] async fn test_h2_head_binary() { - let openssl = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| { - ok::<_, ()>( - Response::Ok().content_length(STR.len() as u64).body(STR), - ) - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| { + ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) + }) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.shead("/").send().await.unwrap(); @@ -344,18 +292,11 @@ async fn test_h2_head_binary() { #[actix_rt::test] async fn test_h2_head_binary2() { - let openssl = ssl_acceptor().unwrap(); let srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.shead("/").send().await.unwrap(); @@ -369,24 +310,16 @@ async fn test_h2_head_binary2() { #[actix_rt::test] async fn test_h2_body_length() { - let openssl = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| { - let body = once(ok(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok() - .body(body::SizedStream::new(STR.len() as u64, body)), - ) - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| { + let body = once(ok(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)), + ) + }) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send().await.unwrap(); @@ -399,25 +332,18 @@ async fn test_h2_body_length() { #[actix_rt::test] async fn test_h2_body_chunked_explicit() { - let openssl = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| { - let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok() - .header(header::TRANSFER_ENCODING, "chunked") - .streaming(body), - ) - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| { + let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .header(header::TRANSFER_ENCODING, "chunked") + .streaming(body), + ) + }) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send().await.unwrap(); @@ -433,28 +359,18 @@ async fn test_h2_body_chunked_explicit() { #[actix_rt::test] async fn test_h2_response_http_error_handling() { - let openssl = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(factory_fn_cfg(|_: &ServerConfig| { - ok::<_, ()>(service_fn2(|_| { - let broken_header = Bytes::from_static(b"\0\0\0"); - ok::<_, ()>( - Response::Ok() - .header(header::CONTENT_TYPE, broken_header) - .body(STR), - ) - })) - })) - .map_err(|_| ()), - ) + HttpService::build() + .h2(service_fn(|_| { + let broken_header = Bytes::from_static(b"\0\0\0"); + ok::<_, ()>( + Response::Ok() + .header(header::CONTENT_TYPE, broken_header) + .body(STR), + ) + })) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send().await.unwrap(); @@ -467,19 +383,11 @@ async fn test_h2_response_http_error_handling() { #[actix_rt::test] async fn test_h2_service_error() { - let openssl = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| err::(ErrorBadRequest("error"))) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| err::(ErrorBadRequest("error"))) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send().await.unwrap(); @@ -492,23 +400,15 @@ async fn test_h2_service_error() { #[actix_rt::test] async fn test_h2_on_connect() { - let openssl = ssl_acceptor().unwrap(); - let srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .on_connect(|_| 10usize) - .h2(|req: Request| { - assert!(req.extensions().contains::()); - ok::<_, ()>(Response::Ok().finish()) - }) - .map_err(|_| ()), - ) + HttpService::build() + .on_connect(|_| 10usize) + .h2(|req: Request| { + assert!(req.extensions().contains::()); + ok::<_, ()>(Response::Ok().finish()) + }) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send().await.unwrap(); diff --git a/actix-http/tests/test_rustls.rs b/actix-http/tests/test_rustls.rs index 4a649ca3..89719221 100644 --- a/actix-http/tests/test_rustls.rs +++ b/actix-http/tests/test_rustls.rs @@ -1,13 +1,10 @@ #![cfg(feature = "rustls")] -use actix_codec::{AsyncRead, AsyncWrite}; use actix_http::error::PayloadError; use actix_http::http::header::{self, HeaderName, HeaderValue}; use actix_http::http::{Method, StatusCode, Version}; use actix_http::{body, error, Error, HttpService, Request, Response}; use actix_http_test::TestServer; -use actix_server::ssl::RustlsAcceptor; -use actix_server_config::ServerConfig; -use actix_service::{factory_fn_cfg, pipeline_factory, service_fn2, ServiceFactory}; +use actix_service::{factory_fn_cfg, service_fn2}; use bytes::{Bytes, BytesMut}; use futures::future::{self, err, ok}; @@ -31,7 +28,7 @@ where Ok(body) } -fn ssl_acceptor() -> io::Result> { +fn ssl_acceptor() -> RustlsServerConfig { // load ssl keys let mut config = RustlsServerConfig::new(NoClientAuth::new()); let cert_file = &mut BufReader::new(File::open("../tests/cert.pem").unwrap()); @@ -39,22 +36,45 @@ fn ssl_acceptor() -> io::Result let cert_chain = certs(cert_file).unwrap(); let mut keys = pkcs8_private_keys(key_file).unwrap(); config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); + config +} - let protos = vec![b"h2".to_vec()]; - config.set_protocols(&protos); - Ok(RustlsAcceptor::new(config)) +#[actix_rt::test] +async fn test_h1() -> io::Result<()> { + 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] async fn test_h2() -> io::Result<()> { - let rustls = ssl_acceptor()?; let srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| future::ok::<_, Error>(Response::Ok().finish())) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| 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] +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(); @@ -64,18 +84,14 @@ async fn test_h2() -> io::Result<()> { #[actix_rt::test] async fn test_h2_1() -> io::Result<()> { - let rustls = ssl_acceptor()?; let srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .finish(|req: Request| { - assert!(req.peer_addr().is_some()); - assert_eq!(req.version(), Version::HTTP_2); - future::ok::<_, Error>(Response::Ok().finish()) - }) - .map_err(|_| ()), - ) + HttpService::build() + .finish(|req: Request| { + assert!(req.peer_addr().is_some()); + assert_eq!(req.version(), Version::HTTP_2); + future::ok::<_, Error>(Response::Ok().finish()) + }) + .rustls(ssl_acceptor()) }); let response = srv.sget("/").send().await.unwrap(); @@ -86,19 +102,15 @@ async fn test_h2_1() -> io::Result<()> { #[actix_rt::test] async fn test_h2_body1() -> io::Result<()> { let data = "HELLOWORLD".to_owned().repeat(64 * 1024); - let rustls = ssl_acceptor()?; let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|mut req: Request<_>| { - async move { - let body = load_body(req.take_payload()).await?; - Ok::<_, Error>(Response::Ok().body(body)) - } - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|mut req: Request<_>| { + async move { + let body = load_body(req.take_payload()).await?; + Ok::<_, Error>(Response::Ok().body(body)) + } + }) + .rustls(ssl_acceptor()) }); let response = srv.sget("/").send_body(data.clone()).await.unwrap(); @@ -111,31 +123,25 @@ async fn test_h2_body1() -> io::Result<()> { #[actix_rt::test] async fn test_h2_content_length() { - let rustls = ssl_acceptor().unwrap(); - let srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|req: Request| { - let indx: usize = req.uri().path()[1..].parse().unwrap(); - let statuses = [ - StatusCode::NO_CONTENT, - StatusCode::CONTINUE, - StatusCode::SWITCHING_PROTOCOLS, - StatusCode::PROCESSING, - StatusCode::OK, - StatusCode::NOT_FOUND, - ]; - future::ok::<_, ()>(Response::new(statuses[indx])) - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|req: Request| { + let indx: usize = req.uri().path()[1..].parse().unwrap(); + let statuses = [ + StatusCode::NO_CONTENT, + StatusCode::CONTINUE, + StatusCode::SWITCHING_PROTOCOLS, + StatusCode::PROCESSING, + StatusCode::OK, + StatusCode::NOT_FOUND, + ]; + future::ok::<_, ()>(Response::new(statuses[indx])) + }) + .rustls(ssl_acceptor()) }); let header = HeaderName::from_static("content-length"); let value = HeaderValue::from_static("0"); - { for i in 0..4 { let req = srv @@ -165,14 +171,9 @@ async fn test_h2_content_length() { async fn test_h2_headers() { let data = STR.repeat(10); let data2 = data.clone(); - let rustls = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { let data = data.clone(); - pipeline_factory(rustls - .clone() - .map_err(|e| println!("Rustls error: {}", e))) - .and_then( HttpService::build().h2(move |_| { let mut config = Response::Ok(); for idx in 0..90 { @@ -194,7 +195,8 @@ async fn test_h2_headers() { ); } future::ok::<_, ()>(config.body(data.clone())) - }).map_err(|_| ())) + }) + .rustls(ssl_acceptor()) }); 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] async fn test_h2_body2() { - let rustls = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| future::ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| future::ok::<_, ()>(Response::Ok().body(STR))) + .rustls(ssl_acceptor()) }); let response = srv.sget("/").send().await.unwrap(); @@ -249,14 +247,10 @@ async fn test_h2_body2() { #[actix_rt::test] async fn test_h2_head_empty() { - let rustls = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) + HttpService::build() + .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) + .rustls(ssl_acceptor()) }); let response = srv.shead("/").send().await.unwrap(); @@ -278,18 +272,12 @@ async fn test_h2_head_empty() { #[actix_rt::test] async fn test_h2_head_binary() { - let rustls = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| { - ok::<_, ()>( - Response::Ok().content_length(STR.len() as u64).body(STR), - ) - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| { + ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) + }) + .rustls(ssl_acceptor()) }); let response = srv.shead("/").send().await.unwrap(); @@ -310,14 +298,10 @@ async fn test_h2_head_binary() { #[actix_rt::test] async fn test_h2_head_binary2() { - let rustls = ssl_acceptor().unwrap(); let srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) + .rustls(ssl_acceptor()) }); let response = srv.shead("/").send().await.unwrap(); @@ -334,20 +318,15 @@ async fn test_h2_head_binary2() { #[actix_rt::test] async fn test_h2_body_length() { - let rustls = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| { - let body = once(ok(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok() - .body(body::SizedStream::new(STR.len() as u64, body)), - ) - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| { + let body = once(ok(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)), + ) + }) + .rustls(ssl_acceptor()) }); let response = srv.sget("/").send().await.unwrap(); @@ -360,22 +339,17 @@ async fn test_h2_body_length() { #[actix_rt::test] async fn test_h2_body_chunked_explicit() { - let rustls = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| { - let body = - once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok() - .header(header::TRANSFER_ENCODING, "chunked") - .streaming(body), - ) - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| { + let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .header(header::TRANSFER_ENCODING, "chunked") + .streaming(body), + ) + }) + .rustls(ssl_acceptor()) }); let response = srv.sget("/").send().await.unwrap(); @@ -391,24 +365,19 @@ async fn test_h2_body_chunked_explicit() { #[actix_rt::test] async fn test_h2_response_http_error_handling() { - let rustls = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(factory_fn_cfg(|_: &ServerConfig| { - ok::<_, ()>(service_fn2(|_| { - let broken_header = Bytes::from_static(b"\0\0\0"); - ok::<_, ()>( - Response::Ok() - .header(http::header::CONTENT_TYPE, broken_header) - .body(STR), - ) - })) - })) - .map_err(|_| ()), - ) + HttpService::build() + .h2(factory_fn_cfg(|_: ()| { + ok::<_, ()>(service_fn2(|_| { + let broken_header = Bytes::from_static(b"\0\0\0"); + ok::<_, ()>( + Response::Ok() + .header(http::header::CONTENT_TYPE, broken_header) + .body(STR), + ) + })) + })) + .rustls(ssl_acceptor()) }); let response = srv.sget("/").send().await.unwrap(); @@ -421,15 +390,26 @@ async fn test_h2_response_http_error_handling() { #[actix_rt::test] async fn test_h2_service_error() { - let rustls = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| err::(error::ErrorBadRequest("error"))) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| err::(error::ErrorBadRequest("error"))) + .rustls(ssl_acceptor()) + }); + + 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::(error::ErrorBadRequest("error"))) + .rustls(ssl_acceptor()) }); let response = srv.sget("/").send().await.unwrap(); diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index a3ce3f9c..850d30a3 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -4,8 +4,7 @@ use std::{net, thread}; use actix_http_test::TestServer; use actix_rt::time::delay_for; -use actix_server_config::ServerConfig; -use actix_service::{factory_fn_cfg, pipeline, service_fn, ServiceFactory}; +use actix_service::service_fn; use bytes::Bytes; use futures::future::{self, err, ok, ready, FutureExt}; use futures::stream::{once, StreamExt}; @@ -27,6 +26,7 @@ async fn test_h1() { assert!(req.peer_addr().is_some()); future::ok::<_, ()>(Response::Ok().finish()) }) + .tcp() }); let response = srv.get("/").send().await.unwrap(); @@ -45,7 +45,7 @@ async fn test_h1_2() { assert_eq!(req.version(), http::Version::HTTP_11); future::ok::<_, ()>(Response::Ok().finish()) }) - .map(|_| ()) + .tcp() }); let response = srv.get("/").send().await.unwrap(); @@ -64,6 +64,7 @@ async fn test_expect_continue() { } })) .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) + .tcp() }); 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(); @@ -114,18 +116,20 @@ async fn test_chunked_payload() { let total_size: usize = chunk_sizes.iter().sum(); let srv = TestServer::start(|| { - HttpService::build().h1(service_fn(|mut request: Request| { - request - .take_payload() - .map(|res| match res { - Ok(pl) => pl, - Err(e) => panic!(format!("Error reading payload: {}", e)), - }) - .fold(0usize, |acc, chunk| ready(acc + chunk.len())) - .map(|req_size| { - Ok::<_, Error>(Response::Ok().body(format!("size={}", req_size))) - }) - })) + HttpService::build() + .h1(service_fn(|mut request: Request| { + request + .take_payload() + .map(|res| match res { + Ok(pl) => pl, + Err(e) => panic!(format!("Error reading payload: {}", e)), + }) + .fold(0usize, |acc, chunk| ready(acc + chunk.len())) + .map(|req_size| { + Ok::<_, Error>(Response::Ok().body(format!("size={}", req_size))) + }) + })) + .tcp() }); let returned_size = { @@ -167,6 +171,7 @@ async fn test_slow_request() { HttpService::build() .client_timeout(100) .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) + .tcp() }); let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); @@ -179,7 +184,9 @@ async fn test_slow_request() { #[actix_rt::test] async fn test_http1_malformed_request() { 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(); @@ -192,7 +199,9 @@ async fn test_http1_malformed_request() { #[actix_rt::test] async fn test_http1_keepalive() { 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(); @@ -213,6 +222,7 @@ async fn test_http1_keepalive_timeout() { HttpService::build() .keep_alive(1) .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + .tcp() }); let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); @@ -230,7 +240,9 @@ async fn test_http1_keepalive_timeout() { #[actix_rt::test] async fn test_http1_keepalive_close() { 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(); @@ -248,7 +260,9 @@ async fn test_http1_keepalive_close() { #[actix_rt::test] async fn test_http10_keepalive_default_close() { 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(); @@ -265,7 +279,9 @@ async fn test_http10_keepalive_default_close() { #[actix_rt::test] async fn test_http10_keepalive() { 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(); @@ -292,6 +308,7 @@ async fn test_http1_keepalive_disabled() { HttpService::build() .keep_alive(KeepAlive::Disabled) .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + .tcp() }); let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); @@ -313,18 +330,20 @@ async fn test_content_length() { }; let srv = TestServer::start(|| { - HttpService::build().h1(|req: Request| { - let indx: usize = req.uri().path()[1..].parse().unwrap(); - let statuses = [ - StatusCode::NO_CONTENT, - StatusCode::CONTINUE, - StatusCode::SWITCHING_PROTOCOLS, - StatusCode::PROCESSING, - StatusCode::OK, - StatusCode::NOT_FOUND, - ]; - future::ok::<_, ()>(Response::new(statuses[indx])) - }) + HttpService::build() + .h1(|req: Request| { + let indx: usize = req.uri().path()[1..].parse().unwrap(); + let statuses = [ + StatusCode::NO_CONTENT, + StatusCode::CONTINUE, + StatusCode::SWITCHING_PROTOCOLS, + StatusCode::PROCESSING, + StatusCode::OK, + StatusCode::NOT_FOUND, + ]; + future::ok::<_, ()>(Response::new(statuses[indx])) + }) + .tcp() }); let header = HeaderName::from_static("content-length"); @@ -377,7 +396,7 @@ async fn test_h1_headers() { ); } future::ok::<_, ()>(builder.body(data.clone())) - }) + }).tcp() }); 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] async fn test_h1_body() { 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(); @@ -427,7 +448,9 @@ async fn test_h1_body() { #[actix_rt::test] async fn test_h1_head_empty() { 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(); @@ -449,9 +472,11 @@ async fn test_h1_head_empty() { #[actix_rt::test] async fn test_h1_head_binary() { let mut srv = TestServer::start(|| { - HttpService::build().h1(|_| { - ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) - }) + HttpService::build() + .h1(|_| { + ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) + }) + .tcp() }); let response = srv.head("/").send().await.unwrap(); @@ -473,7 +498,9 @@ async fn test_h1_head_binary() { #[actix_rt::test] async fn test_h1_head_binary2() { 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(); @@ -491,12 +518,14 @@ async fn test_h1_head_binary2() { #[actix_rt::test] async fn test_h1_body_length() { let mut srv = TestServer::start(|| { - HttpService::build().h1(|_| { - let body = once(ok(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)), - ) - }) + HttpService::build() + .h1(|_| { + let body = once(ok(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)), + ) + }) + .tcp() }); let response = srv.get("/").send().await.unwrap(); @@ -510,14 +539,16 @@ async fn test_h1_body_length() { #[actix_rt::test] async fn test_h1_body_chunked_explicit() { let mut srv = TestServer::start(|| { - HttpService::build().h1(|_| { - let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok() - .header(header::TRANSFER_ENCODING, "chunked") - .streaming(body), - ) - }) + HttpService::build() + .h1(|_| { + let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .header(header::TRANSFER_ENCODING, "chunked") + .streaming(body), + ) + }) + .tcp() }); let response = srv.get("/").send().await.unwrap(); @@ -542,10 +573,12 @@ async fn test_h1_body_chunked_explicit() { #[actix_rt::test] async fn test_h1_body_chunked_implicit() { let mut srv = TestServer::start(|| { - HttpService::build().h1(|_| { - let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>(Response::Ok().streaming(body)) - }) + HttpService::build() + .h1(|_| { + let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>(Response::Ok().streaming(body)) + }) + .tcp() }); let response = srv.get("/").send().await.unwrap(); @@ -568,8 +601,8 @@ async fn test_h1_body_chunked_implicit() { #[actix_rt::test] async fn test_h1_response_http_error_handling() { let mut srv = TestServer::start(|| { - HttpService::build().h1(factory_fn_cfg(|_: &ServerConfig| { - ok::<_, ()>(pipeline(|_| { + HttpService::build() + .h1(service_fn(|_| { let broken_header = Bytes::from_static(b"\0\0\0"); ok::<_, ()>( Response::Ok() @@ -577,7 +610,7 @@ async fn test_h1_response_http_error_handling() { .body(STR), ) })) - })) + .tcp() }); let response = srv.get("/").send().await.unwrap(); @@ -593,6 +626,7 @@ async fn test_h1_service_error() { let mut srv = TestServer::start(|| { HttpService::build() .h1(|_| future::err::(error::ErrorBadRequest("error"))) + .tcp() }); let response = srv.get("/").send().await.unwrap(); @@ -612,6 +646,7 @@ async fn test_h1_on_connect() { assert!(req.extensions().contains::()); future::ok::<_, ()>(Response::Ok().finish()) }) + .tcp() }); let response = srv.get("/").send().await.unwrap(); diff --git a/actix-http/tests/test_ws.rs b/actix-http/tests/test_ws.rs index aa81bc41..284a4218 100644 --- a/actix-http/tests/test_ws.rs +++ b/actix-http/tests/test_ws.rs @@ -2,7 +2,7 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_http::{body, h1, ws, Error, HttpService, Request, Response}; use actix_http_test::TestServer; use actix_utils::framed::FramedTransport; -use bytes::{Bytes, BytesMut}; +use bytes::BytesMut; use futures::future; use futures::{SinkExt, StreamExt}; @@ -40,6 +40,7 @@ async fn test_simple() { HttpService::build() .upgrade(actix_service::service_fn(ws_service)) .finish(|_| future::ok::<_, ()>(Response::NotFound())) + .tcp() }); // client service @@ -61,7 +62,7 @@ async fn test_simple() { let (item, mut framed) = framed.into_future().await; assert_eq!( 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(); diff --git a/actix-identity/Cargo.toml b/actix-identity/Cargo.toml index d05b3768..eea85426 100644 --- a/actix-identity/Cargo.toml +++ b/actix-identity/Cargo.toml @@ -17,14 +17,14 @@ name = "actix_identity" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.1", default-features = false, features = ["secure-cookies"] } -actix-service = "1.0.0-alpha.1" +actix-web = { version = "2.0.0-alpha.2", default-features = false, features = ["secure-cookies"] } +actix-service = "1.0.0-alpha.2" futures = "0.3.1" serde = "1.0" serde_json = "1.0" time = "0.1.42" [dev-dependencies] -actix-rt = "1.0.0-alpha.1" -actix-http = "0.3.0-alpha.1" -bytes = "0.4" \ No newline at end of file +actix-rt = "1.0.0-alpha.2" +actix-http = "0.3.0-alpha.2" +bytes = "0.5.2" \ No newline at end of file diff --git a/actix-multipart/CHANGES.md b/actix-multipart/CHANGES.md index ca61176c..ae6f5bf1 100644 --- a/actix-multipart/CHANGES.md +++ b/actix-multipart/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [0.2.0-alpha.2] - 2019-12-03 + +* Migrate to `std::future` + ## [0.1.4] - 2019-09-12 * Multipart handling now parses requests which do not end in CRLF #1038 diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 52b33d58..f1913c3f 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-multipart" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" authors = ["Nikolay Kim "] description = "Multipart support for actix web framework." readme = "README.md" @@ -18,10 +18,10 @@ name = "actix_multipart" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.1", default-features = false } -actix-service = "1.0.0-alpha.1" -actix-utils = "0.5.0-alpha.1" -bytes = "0.4" +actix-web = { version = "2.0.0-alpha.2", default-features = false } +actix-service = "1.0.0-alpha.2" +actix-utils = "1.0.0-alpha.2" +bytes = "0.5.2" derive_more = "0.99.2" httparse = "1.3" futures = "0.3.1" @@ -31,5 +31,5 @@ time = "0.1" twoway = "0.2" [dev-dependencies] -actix-rt = "1.0.0-alpha.1" -actix-http = "0.3.0-alpha.1" \ No newline at end of file +actix-rt = "1.0.0-alpha.2" +actix-http = "0.3.0-alpha.2" \ No newline at end of file diff --git a/actix-multipart/README.md b/actix-multipart/README.md index ac0d0564..a453f489 100644 --- a/actix-multipart/README.md +++ b/actix-multipart/README.md @@ -5,4 +5,4 @@ * [API Documentation](https://docs.rs/actix-multipart/) * [Chat on gitter](https://gitter.im/actix/actix) * Cargo package: [actix-multipart](https://crates.io/crates/actix-multipart) -* Minimum supported Rust version: 1.33 or later +* Minimum supported Rust version: 1.39 or later diff --git a/actix-multipart/src/server.rs b/actix-multipart/src/server.rs index c4989676..7d1bbca4 100644 --- a/actix-multipart/src/server.rs +++ b/actix-multipart/src/server.rs @@ -1,5 +1,6 @@ //! Multipart payload support use std::cell::{Cell, RefCell, RefMut}; +use std::convert::TryFrom; use std::marker::PhantomData; use std::pin::Pin; use std::rc::Rc; @@ -16,7 +17,6 @@ use actix_web::error::{ParseError, PayloadError}; use actix_web::http::header::{ self, ContentDisposition, HeaderMap, HeaderName, HeaderValue, }; -use actix_web::http::HttpTryFrom; use crate::error::MultipartError; @@ -582,7 +582,7 @@ impl InnerField { } } } 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, MultipartError> { match self.readline() { Err(MultipartError::Incomplete) if self.eof => { - Ok(Some(self.buf.take().freeze())) + Ok(Some(self.buf.split().freeze())) } line => line, } @@ -800,7 +800,7 @@ impl PayloadBuffer { /// Put unprocessed data back to the buffer 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); self.buf.extend_from_slice(&buf); } @@ -893,8 +893,8 @@ mod tests { #[actix_rt::test] async fn test_multipart_no_end_crlf() { let (sender, payload) = create_stream(); - let (bytes, headers) = create_simple_request_with_header(); - let bytes_stripped = bytes.slice_to(bytes.len()); // strip crlf + let (mut bytes, headers) = create_simple_request_with_header(); + let bytes_stripped = bytes.split_to(bytes.len()); // strip crlf sender.send(Ok(bytes_stripped)).unwrap(); drop(sender); // eof diff --git a/actix-session/CHANGES.md b/actix-session/CHANGES.md index d85f6d5f..0c9dca56 100644 --- a/actix-session/CHANGES.md +++ b/actix-session/CHANGES.md @@ -1,11 +1,19 @@ # 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 -* Enhanced ``actix-session`` to facilitate state changes. Use ``Session.renew()`` - at successful login to cycle a session (new key/cookie but keeps state). - Use ``Session.purge()`` at logout to invalid a session cookie (and remove - from redis cache, if applicable). +* Enhanced ``actix-session`` to facilitate state changes. Use ``Session.renew()`` + at successful login to cycle a session (new key/cookie but keeps state). + Use ``Session.purge()`` at logout to invalid a session cookie (and remove + from redis cache, if applicable). ## [0.1.1] - 2019-06-03 diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml index a4c53e56..d8b3ecc9 100644 --- a/actix-session/Cargo.toml +++ b/actix-session/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-session" -version = "0.3.0-alpha.1" +version = "0.3.0-alpha.3" authors = ["Nikolay Kim "] description = "Session for actix web framework." readme = "README.md" @@ -24,15 +24,14 @@ default = ["cookie-session"] cookie-session = ["actix-web/secure-cookies"] [dependencies] -actix-web = "2.0.0-alpha.1" -actix-service = "1.0.0-alpha.1" -bytes = "0.4" +actix-web = "2.0.0-alpha.3" +actix-service = "1.0.0-alpha.3" +bytes = "0.5.2" derive_more = "0.99.2" futures = "0.3.1" -hashbrown = "0.6.3" serde = "1.0" serde_json = "1.0" time = "0.1.42" [dev-dependencies] -actix-rt = "1.0.0-alpha.1" +actix-rt = "1.0.0-alpha.3" diff --git a/actix-session/src/lib.rs b/actix-session/src/lib.rs index def35a1e..771c4f67 100644 --- a/actix-session/src/lib.rs +++ b/actix-session/src/lib.rs @@ -43,12 +43,14 @@ //! } //! ``` use std::cell::RefCell; +use std::collections::HashMap; 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 futures::future::{ok, Ready}; -use hashbrown::HashMap; use serde::de::DeserializeOwned; use serde::Serialize; 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)] pub enum SessionStatus { Changed, @@ -281,6 +289,20 @@ mod tests { assert_eq!(res, Some("value".to_string())); } + #[test] + fn get_session_from_request_head() { + let mut req = test::TestRequest::default().to_srv_request(); + + Session::set_session( + vec![("key".to_string(), "\"value\"".to_string())].into_iter(), + &mut req, + ); + + let session = req.head_mut().get_session(); + let res = session.get::("key").unwrap(); + assert_eq!(res, Some("value".to_string())); + } + #[test] fn purge_session() { let req = test::TestRequest::default().to_srv_request(); diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 95883363..6a8a8a79 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web-codegen" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" description = "Actix web proc macros" readme = "README.md" authors = ["Nikolay Kim "] @@ -17,8 +17,8 @@ syn = { version = "^1", features = ["full", "parsing"] } proc-macro2 = "^1" [dev-dependencies] -actix-rt = { version = "1.0.0-alpha.1" } -actix-web = { version = "2.0.0-alpha.1" } -actix-http = { version = "0.3.0-alpha.1", features=["openssl"] } -actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } +actix-rt = { version = "1.0.0-alpha.2" } +actix-web = { version = "2.0.0-alpha.2" } +actix-http = { version = "0.3.0-alpha.2", features=["openssl"] } +actix-http-test = { version = "0.3.0-alpha.2", features=["openssl"] } futures = { version = "0.3.1" } diff --git a/actix-web-codegen/tests/test_macro.rs b/actix-web-codegen/tests/test_macro.rs index b6ac6dd1..c4f2d7e8 100644 --- a/actix-web-codegen/tests/test_macro.rs +++ b/actix-web-codegen/tests/test_macro.rs @@ -78,6 +78,7 @@ async fn test_params() { .service(put_param_test) .service(delete_param_test), ) + .tcp() }); let request = srv.request(http::Method::GET, srv.url("/test/it")); @@ -107,6 +108,7 @@ async fn test_body() { .service(patch_test) .service(test), ) + .tcp() }); let request = srv.request(http::Method::GET, srv.url("/test")); let response = request.send().await.unwrap(); @@ -149,7 +151,8 @@ async fn test_body() { #[actix_rt::test] 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 response = request.send().await.unwrap(); diff --git a/awc/Cargo.toml b/awc/Cargo.toml index e9268aac..99ccd4bb 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "0.3.0-alpha.1" +version = "0.3.0-alpha.3" authors = ["Nikolay Kim "] description = "Actix http client." readme = "README.md" @@ -21,7 +21,7 @@ name = "awc" path = "src/lib.rs" [package.metadata.docs.rs] -features = ["openssl", "brotli", "flate2-zlib"] +features = ["openssl", "rustls", "brotli", "flate2-zlib"] [features] default = ["brotli", "flate2-zlib"] @@ -30,7 +30,7 @@ default = ["brotli", "flate2-zlib"] openssl = ["open-ssl", "actix-http/openssl"] # rustls -# rustls = ["rust-tls", "actix-http/rustls"] +rustls = ["rust-tls", "actix-http/rustls"] # brotli encoding, requires c compiler brotli = ["actix-http/brotli"] @@ -42,13 +42,13 @@ flate2-zlib = ["actix-http/flate2-zlib"] flate2-rust = ["actix-http/flate2-rust"] [dependencies] -actix-codec = "0.2.0-alpha.1" -actix-service = "1.0.0-alpha.1" -actix-http = "0.3.0-alpha.1" -actix-rt = "1.0.0-alpha.1" +actix-codec = "0.2.0-alpha.3" +actix-service = "1.0.0-alpha.3" +actix-http = "0.3.0-alpha.3" +actix-rt = "1.0.0-alpha.3" -base64 = "0.10.1" -bytes = "0.4" +base64 = "0.11" +bytes = "0.5.2" derive_more = "0.99.2" futures = "0.3.1" log =" 0.4" @@ -59,17 +59,17 @@ serde = "1.0" serde_json = "1.0" serde_urlencoded = "0.6.1" open-ssl = { version="0.10", package="openssl", optional = true } -# rust-tls = { version = "0.16.0", package="rustls", optional = true, features = ["dangerous_configuration"] } +rust-tls = { version = "0.16.0", package="rustls", optional = true, features = ["dangerous_configuration"] } [dev-dependencies] -actix-connect = { version = "1.0.0-alpha.1", features=["openssl"] } -actix-web = { version = "2.0.0-alpha.1", features=["openssl"] } -actix-http = { version = "0.3.0-alpha.1", features=["openssl"] } -actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } -actix-utils = "0.5.0-alpha.1" -actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } +actix-connect = { version = "1.0.0-alpha.3", features=["openssl"] } +actix-web = { version = "2.0.0-alpha.3", features=["openssl"] } +actix-http = { version = "0.3.0-alpha.3", features=["openssl"] } +actix-http-test = { version = "0.3.0-alpha.3", features=["openssl"] } +actix-utils = "1.0.0-alpha.3" +actix-server = { version = "1.0.0-alpha.3" } +actix-tls = { version = "1.0.0-alpha.3", features=["openssl", "rustls"] } brotli2 = { version="0.3.2" } flate2 = { version="1.0.2" } env_logger = "0.6" -rand = "0.7" webpki = { version = "0.21" } diff --git a/awc/src/builder.rs b/awc/src/builder.rs index 463f4030..7bd0171e 100644 --- a/awc/src/builder.rs +++ b/awc/src/builder.rs @@ -1,10 +1,11 @@ use std::cell::RefCell; +use std::convert::TryFrom; use std::fmt; use std::rc::Rc; use std::time::Duration; 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 crate::connect::ConnectorWrapper; @@ -97,8 +98,8 @@ impl ClientBuilder { /// get added to every request. pub fn header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, - >::Error: fmt::Debug, + HeaderName: TryFrom, + >::Error: fmt::Debug + Into, V: header::IntoHeaderValue, V::Error: fmt::Debug, { diff --git a/awc/src/connect.rs b/awc/src/connect.rs index cc92fdbb..44dbcd60 100644 --- a/awc/src/connect.rs +++ b/awc/src/connect.rs @@ -1,7 +1,7 @@ use std::pin::Pin; use std::rc::Rc; use std::task::{Context, Poll}; -use std::{fmt, io, net}; +use std::{fmt, io, mem, net}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_http::body::Body; @@ -201,7 +201,10 @@ impl fmt::Debug 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], + ) -> bool { self.0.as_read().prepare_uninitialized_buffer(buf) } diff --git a/awc/src/error.rs b/awc/src/error.rs index 8816c407..7fece74e 100644 --- a/awc/src/error.rs +++ b/awc/src/error.rs @@ -3,13 +3,14 @@ pub use actix_http::client::{ ConnectError, FreezeRequestError, InvalidUrl, SendRequestError, }; 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::ProtocolError as WsProtocolError; use actix_http::ResponseError; 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}; /// Websocket client error diff --git a/awc/src/frozen.rs b/awc/src/frozen.rs index 61ba87aa..748a15d3 100644 --- a/awc/src/frozen.rs +++ b/awc/src/frozen.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::net; use std::rc::Rc; use std::time::Duration; @@ -8,9 +9,7 @@ use serde::Serialize; use actix_http::body::Body; use actix_http::http::header::IntoHeaderValue; -use actix_http::http::{ - Error as HttpError, HeaderMap, HeaderName, HttpTryFrom, Method, Uri, -}; +use actix_http::http::{Error as HttpError, HeaderMap, HeaderName, Method, Uri}; use actix_http::{Error, RequestHead}; use crate::sender::{RequestSender, SendClientRequest}; @@ -112,7 +111,8 @@ impl FrozenClientRequest { /// Create a `FrozenSendBuilder` with an extra header pub fn extra_header(&self, key: K, value: V) -> FrozenSendBuilder where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { self.extra_headers(HeaderMap::new()) @@ -139,7 +139,8 @@ impl FrozenSendBuilder { /// Insert a header, it overrides existing header in `FrozenClientRequest`. pub fn extra_header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { match HeaderName::try_from(key) { diff --git a/awc/src/lib.rs b/awc/src/lib.rs index e995519e..64784ed9 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -19,12 +19,13 @@ //! } //! ``` use std::cell::RefCell; +use std::convert::TryFrom; use std::rc::Rc; use std::time::Duration; 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; mod builder; @@ -102,7 +103,8 @@ impl Client { /// Construct HTTP request. pub fn request(&self, method: Method, url: U) -> ClientRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { let mut req = ClientRequest::new(method, url, self.0.clone()); @@ -118,7 +120,8 @@ impl Client { /// copies all headers and the method. pub fn request_from(&self, url: U, head: &RequestHead) -> ClientRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { let mut req = self.request(head.method.clone(), url); for (key, value) in head.headers.iter() { @@ -130,7 +133,8 @@ impl Client { /// Construct HTTP *GET* request. pub fn get(&self, url: U) -> ClientRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { self.request(Method::GET, url) } @@ -138,7 +142,8 @@ impl Client { /// Construct HTTP *HEAD* request. pub fn head(&self, url: U) -> ClientRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { self.request(Method::HEAD, url) } @@ -146,7 +151,8 @@ impl Client { /// Construct HTTP *PUT* request. pub fn put(&self, url: U) -> ClientRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { self.request(Method::PUT, url) } @@ -154,7 +160,8 @@ impl Client { /// Construct HTTP *POST* request. pub fn post(&self, url: U) -> ClientRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { self.request(Method::POST, url) } @@ -162,7 +169,8 @@ impl Client { /// Construct HTTP *PATCH* request. pub fn patch(&self, url: U) -> ClientRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { self.request(Method::PATCH, url) } @@ -170,7 +178,8 @@ impl Client { /// Construct HTTP *DELETE* request. pub fn delete(&self, url: U) -> ClientRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { self.request(Method::DELETE, url) } @@ -178,7 +187,8 @@ impl Client { /// Construct HTTP *OPTIONS* request. pub fn options(&self, url: U) -> ClientRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { self.request(Method::OPTIONS, url) } @@ -186,7 +196,8 @@ impl Client { /// Construct WebSockets request. pub fn ws(&self, url: U) -> ws::WebsocketsRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { let mut req = ws::WebsocketsRequest::new(url, self.0.clone()); for (key, value) in self.0.headers.iter() { diff --git a/awc/src/request.rs b/awc/src/request.rs index 3660f808..5ca4973c 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -1,10 +1,10 @@ +use std::convert::TryFrom; use std::fmt::Write as FmtWrite; -use std::io::Write; use std::rc::Rc; use std::time::Duration; use std::{fmt, net}; -use bytes::{BufMut, Bytes, BytesMut}; +use bytes::Bytes; use futures::Stream; use percent_encoding::percent_encode; use serde::Serialize; @@ -13,8 +13,8 @@ use actix_http::body::Body; use actix_http::cookie::{Cookie, CookieJar, USERINFO}; use actix_http::http::header::{self, Header, IntoHeaderValue}; use actix_http::http::{ - uri, ConnectionType, Error as HttpError, HeaderMap, HeaderName, HeaderValue, - HttpTryFrom, Method, Uri, Version, + uri, ConnectionType, Error as HttpError, HeaderMap, HeaderName, HeaderValue, Method, + Uri, Version, }; use actix_http::{Error, RequestHead}; @@ -67,7 +67,8 @@ impl ClientRequest { /// Create new client request builder. pub(crate) fn new(method: Method, uri: U, config: Rc) -> Self where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { ClientRequest { config, @@ -86,7 +87,8 @@ impl ClientRequest { #[inline] pub fn uri(mut self, uri: U) -> Self where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { match Uri::try_from(uri) { Ok(uri) => self.head.uri = uri, @@ -196,7 +198,8 @@ impl ClientRequest { /// ``` pub fn header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { match HeaderName::try_from(key) { @@ -212,7 +215,8 @@ impl ClientRequest { /// Insert a header, replaces existing header. pub fn set_header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { match HeaderName::try_from(key) { @@ -228,7 +232,8 @@ impl ClientRequest { /// Insert a header only if it is not yet set. pub fn set_header_if_none(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { match HeaderName::try_from(key) { @@ -264,7 +269,8 @@ impl ClientRequest { #[inline] pub fn content_type(mut self, value: V) -> Self where - HeaderValue: HttpTryFrom, + HeaderValue: TryFrom, + >::Error: Into, { match HeaderValue::try_from(value) { Ok(value) => self.head.headers.insert(header::CONTENT_TYPE, value), @@ -276,9 +282,7 @@ impl ClientRequest { /// Set content length #[inline] pub fn content_length(self, len: u64) -> Self { - let mut wrt = BytesMut::new().writer(); - let _ = write!(wrt, "{}", len); - self.header(header::CONTENT_LENGTH, wrt.get_mut().take().freeze()) + self.header(header::CONTENT_LENGTH, len) } /// Set HTTP basic authorization header @@ -513,9 +517,9 @@ impl ClientRequest { let uri = &self.head.uri; if uri.host().is_none() { return Err(InvalidUrl::MissingHost.into()); - } else if uri.scheme_part().is_none() { + } else if uri.scheme().is_none() { return Err(InvalidUrl::MissingScheme.into()); - } else if let Some(scheme) = uri.scheme_part() { + } else if let Some(scheme) = uri.scheme() { match scheme.as_str() { "http" | "ws" | "https" | "wss" => (), _ => return Err(InvalidUrl::UnknownScheme.into()), @@ -551,7 +555,7 @@ impl ClientRequest { let https = slf .head .uri - .scheme_part() + .scheme() .map(|s| s == &uri::Scheme::HTTPS) .unwrap_or(true); diff --git a/awc/src/response.rs b/awc/src/response.rs index 00ab4cee..cb33e8a2 100644 --- a/awc/src/response.rs +++ b/awc/src/response.rs @@ -348,7 +348,7 @@ where 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, }; } diff --git a/awc/src/test.rs b/awc/src/test.rs index 641ecaa8..a6cbd03e 100644 --- a/awc/src/test.rs +++ b/awc/src/test.rs @@ -1,9 +1,10 @@ //! Test helpers for actix http client to use during testing. +use std::convert::TryFrom; use std::fmt::Write as FmtWrite; use actix_http::cookie::{Cookie, CookieJar, USERINFO}; 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 bytes::Bytes; use percent_encoding::percent_encode; @@ -31,7 +32,8 @@ impl TestResponse { /// Create TestResponse and set header pub fn with_header(key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { Self::default().header(key, value) @@ -55,7 +57,8 @@ impl TestResponse { /// Append a header pub fn header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { if let Ok(key) = HeaderName::try_from(key) { diff --git a/awc/src/ws.rs b/awc/src/ws.rs index 075c8356..e6f8e696 100644 --- a/awc/src/ws.rs +++ b/awc/src/ws.rs @@ -1,4 +1,5 @@ //! Websockets client +use std::convert::TryFrom; use std::fmt::Write as FmtWrite; use std::net::SocketAddr; use std::rc::Rc; @@ -7,7 +8,7 @@ use std::{fmt, str}; use actix_codec::Framed; use actix_http::cookie::{Cookie, CookieJar}; use actix_http::{ws, Payload, RequestHead}; -use actix_rt::time::Timeout; +use actix_rt::time::timeout; use percent_encoding::percent_encode; use actix_http::cookie::USERINFO; @@ -19,7 +20,7 @@ use crate::http::header::{ self, HeaderName, HeaderValue, IntoHeaderValue, AUTHORIZATION, }; 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::ClientConfig; @@ -41,7 +42,8 @@ impl WebsocketsRequest { /// Create new websocket connection pub(crate) fn new(uri: U, config: Rc) -> Self where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { let mut err = None; let mut head = RequestHead::default(); @@ -102,9 +104,10 @@ impl WebsocketsRequest { } /// Set request Origin - pub fn origin(mut self, origin: V) -> Self + pub fn origin(mut self, origin: V) -> Self where - HeaderValue: HttpTryFrom, + HeaderValue: TryFrom, + HttpError: From, { match HeaderValue::try_from(origin) { Ok(value) => self.origin = Some(value), @@ -133,7 +136,8 @@ impl WebsocketsRequest { /// To override header use `set_header()` method. pub fn header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { match HeaderName::try_from(key) { @@ -151,7 +155,8 @@ impl WebsocketsRequest { /// Insert a header, replaces existing header. pub fn set_header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { match HeaderName::try_from(key) { @@ -169,7 +174,8 @@ impl WebsocketsRequest { /// Insert a header only if it is not yet set. pub fn set_header_if_none(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { match HeaderName::try_from(key) { @@ -220,9 +226,9 @@ impl WebsocketsRequest { let uri = &self.head.uri; if uri.host().is_none() { return Err(InvalidUrl::MissingHost.into()); - } else if uri.scheme_part().is_none() { + } else if uri.scheme().is_none() { return Err(InvalidUrl::MissingScheme.into()); - } else if let Some(scheme) = uri.scheme_part() { + } else if let Some(scheme) = uri.scheme() { match scheme.as_str() { "http" | "ws" | "https" | "wss" => (), _ => return Err(InvalidUrl::UnknownScheme.into()), @@ -295,8 +301,8 @@ impl WebsocketsRequest { .open_tunnel(head, self.addr); // set request timeout - let (head, framed) = if let Some(timeout) = self.config.timeout { - Timeout::new(fut, timeout) + let (head, framed) = if let Some(to) = self.config.timeout { + timeout(to, fut) .await .map_err(|_| SendRequestError::Timeout.into()) .and_then(|res| res)? diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs index 15e9a07a..6bd39973 100644 --- a/awc/tests/test_client.rs +++ b/awc/tests/test_client.rs @@ -49,6 +49,7 @@ async fn test_simple() { HttpService::new(App::new().service( web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), )) + .tcp() }); let request = srv.get("/").header("x-test", "111").send(); @@ -77,6 +78,7 @@ async fn test_json() { HttpService::new(App::new().service( web::resource("/").route(web::to(|_: web::Json| HttpResponse::Ok())), )) + .tcp() }); let request = srv @@ -93,6 +95,7 @@ async fn test_form() { HttpService::new(App::new().service(web::resource("/").route(web::to( |_: web::Form>| HttpResponse::Ok(), )))) + .tcp() }); let mut data = HashMap::new(); @@ -112,6 +115,7 @@ async fn test_timeout() { Ok::<_, Error>(HttpResponse::Ok().body(STR)) } })))) + .tcp() }); let connector = awc::Connector::new() @@ -142,6 +146,7 @@ async fn test_timeout_override() { Ok::<_, Error>(HttpResponse::Ok().body(STR)) } })))) + .tcp() }); let client = awc::Client::build() @@ -168,9 +173,13 @@ async fn test_connection_reuse() { num2.fetch_add(1, Ordering::Relaxed); ok(io) }) - .and_then(HttpService::new( - App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), - )) + .and_then( + HttpService::new( + App::new() + .service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), + ) + .tcp(), + ) }); let client = awc::Client::default(); @@ -200,9 +209,13 @@ async fn test_connection_force_close() { num2.fetch_add(1, Ordering::Relaxed); ok(io) }) - .and_then(HttpService::new( - App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), - )) + .and_then( + HttpService::new( + App::new() + .service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), + ) + .tcp(), + ) }); let client = awc::Client::default(); @@ -232,12 +245,15 @@ async fn test_connection_server_close() { num2.fetch_add(1, Ordering::Relaxed); ok(io) }) - .and_then(HttpService::new( - App::new().service( - web::resource("/") - .route(web::to(|| HttpResponse::Ok().force_close().finish())), - ), - )) + .and_then( + HttpService::new( + App::new().service( + web::resource("/") + .route(web::to(|| HttpResponse::Ok().force_close().finish())), + ), + ) + .tcp(), + ) }); let client = awc::Client::default(); @@ -267,9 +283,12 @@ async fn test_connection_wait_queue() { num2.fetch_add(1, Ordering::Relaxed); ok(io) }) - .and_then(HttpService::new(App::new().service( - web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), - ))) + .and_then( + HttpService::new(App::new().service( + web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), + )) + .tcp(), + ) }); let client = awc::Client::build() @@ -308,12 +327,15 @@ async fn test_connection_wait_queue_force_close() { num2.fetch_add(1, Ordering::Relaxed); ok(io) }) - .and_then(HttpService::new( - App::new().service( - web::resource("/") - .route(web::to(|| HttpResponse::Ok().force_close().body(STR))), - ), - )) + .and_then( + HttpService::new( + App::new().service( + web::resource("/") + .route(web::to(|| HttpResponse::Ok().force_close().body(STR))), + ), + ) + .tcp(), + ) }); let client = awc::Client::build() @@ -353,6 +375,7 @@ async fn test_with_query_parameter() { } }, ))) + .tcp() }); let res = awc::Client::new() @@ -373,6 +396,7 @@ async fn test_no_decompress() { res })), )) + .tcp() }); let mut res = awc::Client::new() @@ -419,6 +443,7 @@ async fn test_client_gzip_encoding() { .header("content-encoding", "gzip") .body(data) })))) + .tcp() }); // client request @@ -442,6 +467,7 @@ async fn test_client_gzip_encoding_large() { .header("content-encoding", "gzip") .body(data) })))) + .tcp() }); // client request @@ -471,6 +497,7 @@ async fn test_client_gzip_encoding_large_random() { .body(data) }, )))) + .tcp() }); // client request @@ -495,6 +522,7 @@ async fn test_client_brotli_encoding() { .body(data) }, )))) + .tcp() }); // client request @@ -707,6 +735,7 @@ async fn test_client_cookie_handling() { } }), )) + .tcp() }); 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 @@ -796,6 +826,7 @@ async fn client_bearer_auth() { } }), )) + .tcp() }); // set authorization header to Bearer diff --git a/awc/tests/test_rustls_client.rs b/awc/tests/test_rustls_client.rs index ac60d8e8..a6ced89d 100644 --- a/awc/tests/test_rustls_client.rs +++ b/awc/tests/test_rustls_client.rs @@ -1,21 +1,17 @@ #![cfg(feature = "rustls")] -use rust_tls::ClientConfig; - -use std::io::Result; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; -use actix_codec::{AsyncRead, AsyncWrite}; use actix_http::HttpService; use actix_http_test::TestServer; -use actix_server::ssl::OpensslAcceptor; use actix_service::{pipeline_factory, ServiceFactory}; use actix_web::http::Version; use actix_web::{web, App, HttpResponse}; use futures::future::ok; use open_ssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslVerifyMode}; +use rust_tls::ClientConfig; -fn ssl_acceptor() -> Result> { +fn ssl_acceptor() -> SslAcceptor { // load ssl keys let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); builder.set_verify_callback(SslVerifyMode::NONE, |_, _| true); @@ -33,8 +29,8 @@ fn ssl_acceptor() -> Result> { Err(open_ssl::ssl::AlpnError::NOACK) } }); - builder.set_alpn_protos(b"\x02h2")?; - Ok(actix_server::ssl::OpensslAcceptor::new(builder.build())) + builder.set_alpn_protos(b"\x02h2").unwrap(); + builder.build() } mod danger { @@ -55,7 +51,6 @@ mod danger { // #[actix_rt::test] async fn _test_connection_reuse_h2() { - let openssl = ssl_acceptor().unwrap(); let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); @@ -65,15 +60,11 @@ async fn _test_connection_reuse_h2() { num2.fetch_add(1, Ordering::Relaxed); ok(io) }) - .and_then( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) .and_then( HttpService::build() .h2(App::new() .service(web::resource("/").route(web::to(|| HttpResponse::Ok())))) + .openssl(ssl_acceptor()) .map_err(|_| ()), ) }); diff --git a/awc/tests/test_ssl_client.rs b/awc/tests/test_ssl_client.rs index 1abb071a..a9a7fa2f 100644 --- a/awc/tests/test_ssl_client.rs +++ b/awc/tests/test_ssl_client.rs @@ -1,20 +1,16 @@ #![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::Arc; -use actix_codec::{AsyncRead, AsyncWrite}; use actix_http::HttpService; use actix_http_test::TestServer; -use actix_server::ssl::OpensslAcceptor; use actix_service::{pipeline_factory, ServiceFactory}; use actix_web::http::Version; use actix_web::{web, App, HttpResponse}; use futures::future::ok; +use open_ssl::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode}; -fn ssl_acceptor() -> Result> { +fn ssl_acceptor() -> SslAcceptor { // load ssl keys let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); builder @@ -31,13 +27,12 @@ fn ssl_acceptor() -> Result> { Err(open_ssl::ssl::AlpnError::NOACK) } }); - builder.set_alpn_protos(b"\x02h2")?; - Ok(actix_server::ssl::OpensslAcceptor::new(builder.build())) + builder.set_alpn_protos(b"\x02h2").unwrap(); + builder.build() } #[actix_rt::test] async fn test_connection_reuse_h2() { - let openssl = ssl_acceptor().unwrap(); let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); @@ -47,15 +42,11 @@ async fn test_connection_reuse_h2() { num2.fetch_add(1, Ordering::Relaxed); ok(io) }) - .and_then( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) .and_then( HttpService::build() .h2(App::new() .service(web::resource("/").route(web::to(|| HttpResponse::Ok())))) + .openssl(ssl_acceptor()) .map_err(|_| ()), ) }); diff --git a/awc/tests/test_ws.rs b/awc/tests/test_ws.rs index 2e1d3981..2f7ba273 100644 --- a/awc/tests/test_ws.rs +++ b/awc/tests/test_ws.rs @@ -46,6 +46,7 @@ async fn test_simple() { } }) .finish(|_| ok::<_, Error>(Response::NotFound())) + .tcp() }); // client service @@ -62,10 +63,7 @@ async fn test_simple() { .await .unwrap(); let item = framed.next().await.unwrap().unwrap(); - assert_eq!( - item, - ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) - ); + assert_eq!(item, ws::Frame::Binary(Some(BytesMut::from(&b"text"[..])))); framed.send(ws::Message::Ping("text".into())).await.unwrap(); let item = framed.next().await.unwrap().unwrap(); diff --git a/src/app_service.rs b/src/app_service.rs index 3fa5a6ee..6a91fa07 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -7,7 +7,6 @@ use std::task::{Context, Poll}; use actix_http::{Extensions, Request, Response}; use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url}; -use actix_server_config::ServerConfig; use actix_service::boxed::{self, BoxService, BoxServiceFactory}; use actix_service::{service_fn, Service, ServiceFactory}; use futures::future::{ok, FutureExt, LocalBoxFuture}; @@ -59,7 +58,7 @@ where InitError = (), >, { - type Config = ServerConfig; + type Config = (); type Request = Request; type Response = ServiceResponse; type Error = T::Error; @@ -67,7 +66,7 @@ where type Service = AppInitService; type Future = AppInitResult; - fn new_service(&self, cfg: &ServerConfig) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { // update resource default service let default = self.default.clone().unwrap_or_else(|| { Rc::new(boxed::factory(service_fn(|req: ServiceRequest| { @@ -76,13 +75,6 @@ where }); // 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( self.config.borrow().clone(), default.clone(), @@ -123,7 +115,7 @@ where AppInitResult { endpoint: None, - endpoint_fut: self.endpoint.new_service(&()), + endpoint_fut: self.endpoint.new_service(()), data: self.data.clone(), data_factories: Vec::new(), data_factories_fut: self.data_factories.iter().map(|f| f()).collect(), @@ -281,7 +273,7 @@ impl ServiceFactory for AppRoutingFactory { type Service = AppRouting; type Future = AppRoutingFactoryResponse; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { AppRoutingFactoryResponse { fut: self .services @@ -290,12 +282,12 @@ impl ServiceFactory for AppRoutingFactory { CreateAppRoutingItem::Future( Some(path.clone()), guards.borrow_mut().take(), - service.new_service(&()).boxed_local(), + service.new_service(()).boxed_local(), ) }) .collect(), 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 Future = AppRoutingFactoryResponse; - fn new_service(&self, _: &()) -> Self::Future { - self.factory.borrow_mut().as_mut().unwrap().new_service(&()) + fn new_service(&self, _: ()) -> Self::Future { + self.factory.borrow_mut().as_mut().unwrap().new_service(()) } } diff --git a/src/guard.rs b/src/guard.rs index 3db525f9..aaa99a9e 100644 --- a/src/guard.rs +++ b/src/guard.rs @@ -24,9 +24,10 @@ //! ); //! } //! ``` - #![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; /// Trait defines resource guards. Guards are used for route selection. diff --git a/src/handler.rs b/src/handler.rs index a7023422..d1b070d8 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -177,7 +177,7 @@ where type Service = ExtractService; type Future = Ready>; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { ok(ExtractService { _t: PhantomData, service: self.service.clone(), diff --git a/src/info.rs b/src/info.rs index a9c3e4ee..c9a642b3 100644 --- a/src/info.rs +++ b/src/info.rs @@ -76,7 +76,7 @@ impl ConnectionInfo { } } 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() { scheme = Some("https") } @@ -98,7 +98,7 @@ impl ConnectionInfo { host = h.to_str().ok(); } 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() { host = Some(cfg.host()); } diff --git a/src/middleware/defaultheaders.rs b/src/middleware/defaultheaders.rs index 05a03106..14d035ab 100644 --- a/src/middleware/defaultheaders.rs +++ b/src/middleware/defaultheaders.rs @@ -1,4 +1,5 @@ //! Middleware for setting default response headers +use std::convert::TryFrom; use std::rc::Rc; use std::task::{Context, Poll}; @@ -6,7 +7,7 @@ use actix_service::{Service, Transform}; use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; 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::Error; @@ -58,8 +59,10 @@ impl DefaultHeaders { #[inline] pub fn header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, - HeaderValue: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, + HeaderValue: TryFrom, + >::Error: Into, { #[allow(clippy::match_wild_err_arm)] match HeaderName::try_from(key) { diff --git a/src/middleware/errhandlers.rs b/src/middleware/errhandlers.rs index 3dc1f082..7a868493 100644 --- a/src/middleware/errhandlers.rs +++ b/src/middleware/errhandlers.rs @@ -4,7 +4,7 @@ use std::task::{Context, Poll}; use actix_service::{Service, Transform}; use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; -use hashbrown::HashMap; +use fxhash::FxHashMap; use crate::dev::{ServiceRequest, ServiceResponse}; use crate::error::{Error, Result}; @@ -52,13 +52,13 @@ type ErrorHandler = dyn Fn(ServiceResponse) -> Result { - handlers: Rc>>>, + handlers: Rc>>>, } impl Default for ErrorHandlers { fn default() -> Self { ErrorHandlers { - handlers: Rc::new(HashMap::new()), + handlers: Rc::new(FxHashMap::default()), } } } @@ -105,7 +105,7 @@ where #[doc(hidden)] pub struct ErrorHandlersMiddleware { service: S, - handlers: Rc>>>, + handlers: Rc>>>, } impl Service for ErrorHandlersMiddleware diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index a57ea296..60c10b20 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -1,5 +1,6 @@ //! Request logging middleware use std::collections::HashSet; +use std::convert::TryFrom; use std::env; use std::fmt::{self, Display, Formatter}; use std::future::Future; @@ -17,7 +18,7 @@ use time; use crate::dev::{BodySize, MessageBody, ResponseBody}; use crate::error::{Error, Result}; -use crate::http::{HeaderName, HttpTryFrom, StatusCode}; +use crate::http::{HeaderName, StatusCode}; use crate::service::{ServiceRequest, ServiceResponse}; use crate::HttpResponse; diff --git a/src/middleware/normalize.rs b/src/middleware/normalize.rs index 2926eacc..6bff068b 100644 --- a/src/middleware/normalize.rs +++ b/src/middleware/normalize.rs @@ -1,7 +1,7 @@ //! `Middleware` to normalize request's URI use std::task::{Context, Poll}; -use actix_http::http::{HttpTryFrom, PathAndQuery, Uri}; +use actix_http::http::{PathAndQuery, Uri}; use actix_service::{Service, Transform}; use bytes::Bytes; use futures::future::{ok, Ready}; @@ -74,7 +74,6 @@ where fn call(&mut self, mut req: ServiceRequest) -> Self::Future { let head = req.head_mut(); - let path = head.uri.path(); let original_len = path.len(); let path = self.merge_slash.replace_all(path, "/"); @@ -86,9 +85,10 @@ where let path = if let Some(q) = pq.query() { Bytes::from(format!("{}?{}", path, q)) } 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(); req.match_info_mut().get_mut().update(&uri); diff --git a/src/resource.rs b/src/resource.rs index 866cbecf..7ee0506a 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -435,9 +435,9 @@ impl ServiceFactory for ResourceFactory { type Service = ResourceService; 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() { - Some(default.new_service(&())) + Some(default.new_service(())) } else { None }; @@ -446,7 +446,7 @@ impl ServiceFactory for ResourceFactory { fut: self .routes .iter() - .map(|route| CreateRouteServiceItem::Future(route.new_service(&()))) + .map(|route| CreateRouteServiceItem::Future(route.new_service(()))) .collect(), data: self.data.clone(), default: None, @@ -575,8 +575,8 @@ impl ServiceFactory for ResourceEndpoint { type Service = ResourceService; type Future = CreateResourceService; - fn new_service(&self, _: &()) -> Self::Future { - self.factory.borrow_mut().as_mut().unwrap().new_service(&()) + fn new_service(&self, _: ()) -> Self::Future { + self.factory.borrow_mut().as_mut().unwrap().new_service(()) } } diff --git a/src/responder.rs b/src/responder.rs index 7b30315f..48eae09b 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; @@ -5,8 +6,7 @@ use std::task::{Context, Poll}; use actix_http::error::InternalError; use actix_http::http::{ - header::IntoHeaderValue, Error as HttpError, HeaderMap, HeaderName, HttpTryFrom, - StatusCode, + header::IntoHeaderValue, Error as HttpError, HeaderMap, HeaderName, StatusCode, }; use actix_http::{Error, Response, ResponseBuilder}; use bytes::{Bytes, BytesMut}; @@ -68,7 +68,8 @@ pub trait Responder { fn with_header(self, key: K, value: V) -> CustomResponder where Self: Sized, - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { CustomResponder::new(self).with_header(key, value) @@ -267,7 +268,8 @@ impl CustomResponder { /// ``` pub fn with_header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { if self.headers.is_none() { diff --git a/src/rmap.rs b/src/rmap.rs index 42ddb134..47092608 100644 --- a/src/rmap.rs +++ b/src/rmap.rs @@ -2,7 +2,7 @@ use std::cell::RefCell; use std::rc::Rc; use actix_router::ResourceDef; -use hashbrown::HashMap; +use fxhash::FxHashMap; use url::Url; use crate::error::UrlGenerationError; @@ -12,7 +12,7 @@ use crate::request::HttpRequest; pub struct ResourceMap { root: ResourceDef, parent: RefCell>>, - named: HashMap, + named: FxHashMap, patterns: Vec<(ResourceDef, Option>)>, } @@ -21,7 +21,7 @@ impl ResourceMap { ResourceMap { root, parent: RefCell::new(None), - named: HashMap::new(), + named: FxHashMap::default(), patterns: Vec::new(), } } diff --git a/src/route.rs b/src/route.rs index 93f88bfe..2c643099 100644 --- a/src/route.rs +++ b/src/route.rs @@ -69,9 +69,9 @@ impl ServiceFactory for Route { type Service = RouteService; type Future = CreateRouteService; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { CreateRouteService { - fut: self.service.new_service(&()), + fut: self.service.new_service(()), guards: self.guards.clone(), } } @@ -280,9 +280,9 @@ where type Service = BoxedRouteService; type Future = LocalBoxFuture<'static, Result>; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { self.service - .new_service(&()) + .new_service(()) .map(|result| match result { Ok(service) => { let service: BoxedRouteService<_, _> = diff --git a/src/scope.rs b/src/scope.rs index db6f5da5..d6b88577 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -488,9 +488,9 @@ impl ServiceFactory for ScopeFactory { type Service = ScopeService; type Future = ScopeFactoryResponse; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { let default_fut = if let Some(ref default) = *self.default.borrow() { - Some(default.new_service(&())) + Some(default.new_service(())) } else { None }; @@ -503,7 +503,7 @@ impl ServiceFactory for ScopeFactory { CreateScopeServiceItem::Future( Some(path.clone()), guards.borrow_mut().take(), - service.new_service(&()), + service.new_service(()), ) }) .collect(), @@ -656,8 +656,8 @@ impl ServiceFactory for ScopeEndpoint { type Service = ScopeService; type Future = ScopeFactoryResponse; - fn new_service(&self, _: &()) -> Self::Future { - self.factory.borrow_mut().as_mut().unwrap().new_service(&()) + fn new_service(&self, _: ()) -> Self::Future { + self.factory.borrow_mut().as_mut().unwrap().new_service(()) } } diff --git a/src/server.rs b/src/server.rs index a98d0627..f5883c0d 100644 --- a/src/server.rs +++ b/src/server.rs @@ -2,19 +2,21 @@ use std::marker::PhantomData; use std::sync::Arc; use std::{fmt, io, net}; -use actix_http::{body::MessageBody, Error, HttpService, KeepAlive, Request, Response}; +use actix_http::{ + body::MessageBody, Error, HttpService, KeepAlive, Protocol, Request, Response, +}; use actix_rt::System; use actix_server::{Server, ServerBuilder}; -use actix_server_config::ServerConfig; -use actix_service::{IntoServiceFactory, Service, ServiceFactory}; +use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory}; +use futures::future::ok; use parking_lot::Mutex; use net2::TcpBuilder; #[cfg(feature = "openssl")] -use open_ssl::ssl::{SslAcceptor, SslAcceptorBuilder}; +use actix_tls::openssl::{SslAcceptor, SslAcceptorBuilder}; #[cfg(feature = "rustls")] -use rust_tls::ServerConfig as RustlsServerConfig; +use actix_tls::rustls::ServerConfig as RustlsServerConfig; struct Socket { scheme: &'static str, @@ -52,7 +54,7 @@ pub struct HttpServer where F: Fn() -> I + Send + Clone + 'static, I: IntoServiceFactory, - S: ServiceFactory, + S: ServiceFactory, S::Error: Into, S::InitError: fmt::Debug, S::Response: Into>, @@ -71,7 +73,7 @@ impl HttpServer where F: Fn() -> I + Send + Clone + 'static, I: IntoServiceFactory, - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::InitError: fmt::Debug, S::Response: Into> + 'static, @@ -137,8 +139,8 @@ where /// can be used to limit the global SSL CPU usage. /// /// By default max connections is set to a 256. - pub fn maxconnrate(mut self, num: usize) -> Self { - self.builder = self.builder.maxconnrate(num); + pub fn maxconnrate(self, num: usize) -> Self { + actix_tls::max_concurrent_ssl_connect(num); self } @@ -247,7 +249,9 @@ where HttpService::build() .keep_alive(c.keep_alive) .client_timeout(c.client_timeout) + .local_addr(addr) .finish(factory()) + .tcp() }, )?; Ok(self) @@ -271,10 +275,6 @@ where lst: net::TcpListener, acceptor: SslAcceptor, ) -> io::Result { - use actix_server::ssl::{OpensslAcceptor, SslError}; - use actix_service::pipeline_factory; - - let acceptor = OpensslAcceptor::new(acceptor); let factory = self.factory.clone(); let cfg = self.config.clone(); let addr = lst.local_addr().unwrap(); @@ -288,15 +288,12 @@ where lst, move || { let c = cfg.lock(); - pipeline_factory(acceptor.clone().map_err(SslError::Ssl)).and_then( - HttpService::build() - .keep_alive(c.keep_alive) - .client_timeout(c.client_timeout) - .client_disconnect(c.client_shutdown) - .finish(factory()) - .map_err(SslError::Service) - .map_init_err(|_| ()), - ) + HttpService::build() + .keep_alive(c.keep_alive) + .client_timeout(c.client_timeout) + .client_disconnect(c.client_shutdown) + .finish(factory()) + .openssl(acceptor.clone()) }, )?; Ok(self) @@ -318,15 +315,8 @@ where fn listen_rustls_inner( mut self, lst: net::TcpListener, - mut config: RustlsServerConfig, + config: RustlsServerConfig, ) -> io::Result { - use actix_server::ssl::{RustlsAcceptor, SslError}; - use actix_service::pipeline_factory; - - let protos = vec!["h2".to_string().into(), "http/1.1".to_string().into()]; - config.set_protocols(&protos); - - let acceptor = RustlsAcceptor::new(config); let factory = self.factory.clone(); let cfg = self.config.clone(); let addr = lst.local_addr().unwrap(); @@ -340,15 +330,12 @@ where lst, move || { let c = cfg.lock(); - pipeline_factory(acceptor.clone().map_err(SslError::Ssl)).and_then( - HttpService::build() - .keep_alive(c.keep_alive) - .client_timeout(c.client_timeout) - .client_disconnect(c.client_shutdown) - .finish(factory()) - .map_err(SslError::Service) - .map_init_err(|_| ()), - ) + HttpService::build() + .keep_alive(c.keep_alive) + .client_timeout(c.client_timeout) + .client_disconnect(c.client_shutdown) + .finish(factory()) + .rustls(config.clone()) }, )?; Ok(self) @@ -444,6 +431,8 @@ where mut self, lst: std::os::unix::net::UnixListener, ) -> io::Result { + use actix_rt::net::UnixStream; + let cfg = self.config.clone(); let factory = self.factory.clone(); // todo duplicated: @@ -459,10 +448,12 @@ where self.builder = self.builder.listen_uds(addr, lst, move || { let c = cfg.lock(); - HttpService::build() - .keep_alive(c.keep_alive) - .client_timeout(c.client_timeout) - .finish(factory()) + pipeline_factory(|io: UnixStream| ok((io, Protocol::Http1, None))).and_then( + HttpService::build() + .keep_alive(c.keep_alive) + .client_timeout(c.client_timeout) + .finish(factory()), + ) })?; Ok(self) } @@ -475,6 +466,8 @@ where where A: AsRef, { + use actix_rt::net::UnixStream; + let cfg = self.config.clone(); let factory = self.factory.clone(); self.sockets.push(Socket { @@ -490,10 +483,13 @@ where addr, move || { let c = cfg.lock(); - HttpService::build() - .keep_alive(c.keep_alive) - .client_timeout(c.client_timeout) - .finish(factory()) + pipeline_factory(|io: UnixStream| ok((io, Protocol::Http1, None))) + .and_then( + HttpService::build() + .keep_alive(c.keep_alive) + .client_timeout(c.client_timeout) + .finish(factory()), + ) }, )?; Ok(self) @@ -504,7 +500,7 @@ impl HttpServer where F: Fn() -> I + Send + Clone + 'static, I: IntoServiceFactory, - S: ServiceFactory, + S: ServiceFactory, S::Error: Into, S::InitError: fmt::Debug, S::Response: Into>, @@ -524,14 +520,13 @@ where /// use std::io; /// use actix_web::{web, App, HttpResponse, HttpServer}; /// - /// fn main() -> io::Result<()> { - /// let sys = actix_rt::System::new("example"); // <- create Actix system - /// + /// #[actix_rt::main] + /// async fn main() -> io::Result<()> { + /// # actix_rt::System::current().stop(); /// HttpServer::new(|| App::new().service(web::resource("/").to(|| HttpResponse::Ok()))) /// .bind("127.0.0.1:0")? - /// .start(); - /// # actix_rt::System::current().stop(); - /// sys.run() // <- Run actix system, this method starts all async processes + /// .start() + /// .await /// } /// ``` pub fn start(self) -> Server { @@ -585,8 +580,11 @@ fn openssl_acceptor(mut builder: SslAcceptorBuilder) -> io::Result builder.set_alpn_select_callback(|_, protos| { const H2: &[u8] = b"\x02h2"; + const H11: &[u8] = b"\x08http/1.1"; if protos.windows(3).any(|window| window == H2) { Ok(b"h2") + } else if protos.windows(9).any(|window| window == H11) { + Ok(b"http/1.1") } else { Err(AlpnError::NOACK) } diff --git a/src/test.rs b/src/test.rs index e1939315..5e50f24e 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,12 +1,12 @@ //! Various helpers for Actix applications to use during testing. +use std::convert::TryFrom; use std::rc::Rc; use actix_http::http::header::{ContentType, Header, HeaderName, IntoHeaderValue}; -use actix_http::http::{HttpTryFrom, Method, StatusCode, Uri, Version}; +use actix_http::http::{Error as HttpError, Method, StatusCode, Uri, Version}; use actix_http::test::TestRequest as HttpTestRequest; use actix_http::{cookie::Cookie, Extensions, Request}; use actix_router::{Path, ResourceDef, Url}; -use actix_server_config::ServerConfig; use actix_service::{IntoService, IntoServiceFactory, Service, ServiceFactory}; use bytes::{Bytes, BytesMut}; use futures::future::ok; @@ -71,16 +71,15 @@ pub async fn init_service( where R: IntoServiceFactory, S: ServiceFactory< - Config = ServerConfig, + Config = (), Request = Request, Response = ServiceResponse, Error = E, >, S::InitError: std::fmt::Debug, { - let cfg = ServerConfig::new("127.0.0.1:8080".parse().unwrap()); let srv = app.into_factory(); - srv.new_service(&cfg).await.unwrap() + srv.new_service(()).await.unwrap() } /// Calls service and waits for response future completion. @@ -321,7 +320,8 @@ impl TestRequest { /// Create TestRequest and set header pub fn with_header(key: K, value: V) -> TestRequest where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { TestRequest::default().header(key, value) @@ -379,7 +379,8 @@ impl TestRequest { /// Set a header pub fn header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { self.req.header(key, value); @@ -669,7 +670,7 @@ mod tests { async fn async_with_block() -> Result { let res = web::block(move || Some(4usize).ok_or("wrong")).await; - match res? { + match res { Ok(value) => Ok(HttpResponse::Ok() .content_type("text/plain") .body(format!("Async with block value: {}", value))), diff --git a/src/web.rs b/src/web.rs index 7f1e8d8f..2a66a132 100644 --- a/src/web.rs +++ b/src/web.rs @@ -6,6 +6,7 @@ pub use actix_http::Response as HttpResponse; pub use bytes::{Bytes, BytesMut}; pub use futures::channel::oneshot::Canceled; +use crate::error::BlockingError; use crate::extract::FromRequest; use crate::handler::Factory; use crate::resource::Resource; @@ -274,10 +275,11 @@ pub fn service(path: &str) -> WebService { /// Execute blocking function on a thread pool, returns future that resolves /// to result of the function execution. -pub fn block(f: F) -> impl Future> +pub async fn block(f: F) -> Result> where - F: FnOnce() -> R + Send + 'static, - R: Send + 'static, + F: FnOnce() -> Result + Send + 'static, + I: Send + 'static, + E: Send + std::fmt::Debug + 'static, { - actix_threadpool::run(f) + actix_threadpool::run(f).await } diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index e59e439f..0f6af8ff 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http-test" -version = "0.3.0-alpha.1" +version = "0.3.0-alpha.3" authors = ["Nikolay Kim "] description = "Actix http test server" readme = "README.md" @@ -27,23 +27,22 @@ path = "src/lib.rs" default = [] # openssl -openssl = ["open-ssl", "actix-server/openssl", "awc/openssl"] +openssl = ["open-ssl", "awc/openssl", ] # "actix-tls/openssl"] [dependencies] -actix-service = "1.0.0-alpha.1" -actix-codec = "0.2.0-alpha.1" -actix-connect = "1.0.0-alpha.1" -actix-utils = "0.5.0-alpha.1" -actix-rt = "1.0.0-alpha.1" -actix-server = "0.8.0-alpha.1" -actix-server-config = "0.3.0-alpha.1" -actix-testing = "0.3.0-alpha.1" -awc = "0.3.0-alpha.1" +actix-service = "1.0.0-alpha.3" +actix-codec = "0.2.0-alpha.3" +actix-connect = "1.0.0-alpha.3" +actix-utils = "1.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" +actix-server = "1.0.0-alpha.3" +actix-testing = "1.0.0-alpha.3" +awc = "0.3.0-alpha.3" -base64 = "0.10" -bytes = "0.4" +base64 = "0.11" +bytes = "0.5.2" futures = "0.3.1" -http = "0.1.8" +http = "0.2.0" log = "0.4" env_logger = "0.6" net2 = "0.2" @@ -53,9 +52,8 @@ sha1 = "0.6" slab = "0.4" serde_urlencoded = "0.6.1" time = "0.1" -tokio-net = "0.2.0-alpha.6" open-ssl = { version="0.10", package="openssl", optional = true } [dev-dependencies] -actix-web = "2.0.0-alpha.1" -actix-http = "0.3.0-alpha.1" +actix-web = "2.0.0-alpha.3" +actix-http = "0.3.0-alpha.3" diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index 9ad06397..a2881148 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -3,14 +3,13 @@ use std::sync::mpsc; use std::{net, thread, time}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; -use actix_rt::System; +use actix_rt::{net::TcpStream, System}; use actix_server::{Server, ServiceFactory}; use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connector}; use bytes::Bytes; use futures::Stream; use http::Method; use net2::TcpBuilder; -use tokio_net::tcp::TcpStream; pub use actix_testing::*; diff --git a/tests/test_server.rs b/tests/test_server.rs index bfdf3f0e..7cfda04a 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -4,7 +4,7 @@ use actix_http::http::header::{ ContentEncoding, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING, }; -use actix_http::{h1, Error, HttpService, Response}; +use actix_http::{Error, HttpService, Response}; use actix_http_test::TestServer; use brotli2::write::{BrotliDecoder, BrotliEncoder}; use bytes::Bytes; @@ -42,10 +42,10 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[actix_rt::test] async fn test_body() { let srv = TestServer::start(|| { - h1::H1Service::new( - App::new() - .service(web::resource("/").route(web::to(|| Response::Ok().body(STR)))), - ) + HttpService::build() + .h1(App::new() + .service(web::resource("/").route(web::to(|| Response::Ok().body(STR))))) + .tcp() }); let mut response = srv.get("/").send().await.unwrap(); @@ -60,11 +60,11 @@ async fn test_body() { #[actix_rt::test] async fn test_body_gzip() { let srv = TestServer::start(|| { - h1::H1Service::new( - App::new() + HttpService::build() + .h1(App::new() .wrap(Compress::new(ContentEncoding::Gzip)) - .service(web::resource("/").route(web::to(|| Response::Ok().body(STR)))), - ) + .service(web::resource("/").route(web::to(|| Response::Ok().body(STR))))) + .tcp() }); let mut response = srv @@ -90,13 +90,13 @@ async fn test_body_gzip() { #[actix_rt::test] async fn test_body_gzip2() { let srv = TestServer::start(|| { - h1::H1Service::new( - App::new() + HttpService::build() + .h1(App::new() .wrap(Compress::new(ContentEncoding::Gzip)) .service(web::resource("/").route(web::to(|| { Response::Ok().body(STR).into_body::() - }))), - ) + })))) + .tcp() }); let mut response = srv @@ -122,8 +122,8 @@ async fn test_body_gzip2() { #[actix_rt::test] async fn test_body_encoding_override() { let srv = TestServer::start(|| { - h1::H1Service::new( - App::new() + HttpService::build() + .h1(App::new() .wrap(Compress::new(ContentEncoding::Gzip)) .service(web::resource("/").route(web::to(|| { Response::Ok().encoding(ContentEncoding::Deflate).body(STR) @@ -136,8 +136,8 @@ async fn test_body_encoding_override() { response.encoding(ContentEncoding::Deflate); response - }))), - ) + })))) + .tcp() }); // Builder @@ -187,14 +187,14 @@ async fn test_body_gzip_large() { let srv = TestServer::start(move || { let data = srv_data.clone(); - h1::H1Service::new( - App::new() + HttpService::build() + .h1(App::new() .wrap(Compress::new(ContentEncoding::Gzip)) .service( web::resource("/") .route(web::to(move || Response::Ok().body(data.clone()))), - ), - ) + )) + .tcp() }); let mut response = srv @@ -227,14 +227,14 @@ async fn test_body_gzip_large_random() { let srv = TestServer::start(move || { let data = srv_data.clone(); - h1::H1Service::new( - App::new() + HttpService::build() + .h1(App::new() .wrap(Compress::new(ContentEncoding::Gzip)) .service( web::resource("/") .route(web::to(move || Response::Ok().body(data.clone()))), - ), - ) + )) + .tcp() }); let mut response = srv @@ -261,15 +261,15 @@ async fn test_body_gzip_large_random() { #[actix_rt::test] async fn test_body_chunked_implicit() { let srv = TestServer::start(move || { - h1::H1Service::new( - App::new() + HttpService::build() + .h1(App::new() .wrap(Compress::new(ContentEncoding::Gzip)) .service(web::resource("/").route(web::get().to(move || { Response::Ok().streaming(once(ok::<_, Error>(Bytes::from_static( STR.as_ref(), )))) - }))), - ) + })))) + .tcp() }); let mut response = srv @@ -299,12 +299,15 @@ async fn test_body_chunked_implicit() { #[cfg(feature = "brotli")] async fn test_body_br_streaming() { let srv = TestServer::start(move || { - h1::H1Service::new(App::new().wrap(Compress::new(ContentEncoding::Br)).service( - web::resource("/").route(web::to(move || { - Response::Ok() - .streaming(once(ok::<_, Error>(Bytes::from_static(STR.as_ref())))) - })), - )) + HttpService::build() + .h1(App::new().wrap(Compress::new(ContentEncoding::Br)).service( + web::resource("/").route(web::to(move || { + Response::Ok().streaming(once(ok::<_, Error>(Bytes::from_static( + STR.as_ref(), + )))) + })), + )) + .tcp() }); let mut response = srv @@ -329,9 +332,11 @@ async fn test_body_br_streaming() { #[actix_rt::test] async fn test_head_binary() { let srv = TestServer::start(move || { - h1::H1Service::new(App::new().service(web::resource("/").route( - web::head().to(move || Response::Ok().content_length(100).body(STR)), - ))) + HttpService::build() + .h1(App::new().service(web::resource("/").route( + web::head().to(move || Response::Ok().content_length(100).body(STR)), + ))) + .tcp() }); let mut response = srv.head("/").send().await.unwrap(); @@ -350,14 +355,18 @@ async fn test_head_binary() { #[actix_rt::test] async fn test_no_chunking() { let srv = TestServer::start(move || { - h1::H1Service::new(App::new().service(web::resource("/").route(web::to( - move || { - Response::Ok() - .no_chunking() - .content_length(STR.len() as u64) - .streaming(once(ok::<_, Error>(Bytes::from_static(STR.as_ref())))) - }, - )))) + HttpService::build() + .h1( + App::new().service(web::resource("/").route(web::to(move || { + Response::Ok() + .no_chunking() + .content_length(STR.len() as u64) + .streaming(once(ok::<_, Error>(Bytes::from_static( + STR.as_ref(), + )))) + }))), + ) + .tcp() }); let mut response = srv.get("/").send().await.unwrap(); @@ -373,13 +382,13 @@ async fn test_no_chunking() { #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] async fn test_body_deflate() { let srv = TestServer::start(move || { - h1::H1Service::new( - App::new() + HttpService::build() + .h1(App::new() .wrap(Compress::new(ContentEncoding::Deflate)) .service( web::resource("/").route(web::to(move || Response::Ok().body(STR))), - ), - ) + )) + .tcp() }); // client request @@ -405,9 +414,11 @@ async fn test_body_deflate() { #[cfg(any(feature = "brotli"))] async fn test_body_brotli() { let srv = TestServer::start(move || { - h1::H1Service::new(App::new().wrap(Compress::new(ContentEncoding::Br)).service( - web::resource("/").route(web::to(move || Response::Ok().body(STR))), - )) + HttpService::build() + .h1(App::new().wrap(Compress::new(ContentEncoding::Br)).service( + web::resource("/").route(web::to(move || Response::Ok().body(STR))), + )) + .tcp() }); // client request @@ -434,12 +445,12 @@ async fn test_body_brotli() { #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] async fn test_encoding() { let srv = TestServer::start(move || { - HttpService::new( - App::new().wrap(Compress::default()).service( + HttpService::build() + .h1(App::new().wrap(Compress::default()).service( web::resource("/") .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) + )) + .tcp() }); // client request @@ -463,12 +474,12 @@ async fn test_encoding() { #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] async fn test_gzip_encoding() { let srv = TestServer::start(move || { - HttpService::new( - App::new().service( + HttpService::build() + .h1(App::new().service( web::resource("/") .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) + )) + .tcp() }); // client request @@ -493,12 +504,12 @@ async fn test_gzip_encoding() { async fn test_gzip_encoding_large() { let data = STR.repeat(10); let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().service( + HttpService::build() + .h1(App::new().service( web::resource("/") .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) + )) + .tcp() }); // client request @@ -527,12 +538,12 @@ async fn test_reading_gzip_encoding_large_random() { .collect::(); let srv = TestServer::start(move || { - HttpService::new( - App::new().service( + HttpService::build() + .h1(App::new().service( web::resource("/") .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) + )) + .tcp() }); // client request @@ -557,12 +568,12 @@ async fn test_reading_gzip_encoding_large_random() { #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] async fn test_reading_deflate_encoding() { let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().service( + HttpService::build() + .h1(App::new().service( web::resource("/") .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) + )) + .tcp() }); let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); @@ -587,12 +598,12 @@ async fn test_reading_deflate_encoding() { async fn test_reading_deflate_encoding_large() { let data = STR.repeat(10); let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().service( + HttpService::build() + .h1(App::new().service( web::resource("/") .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) + )) + .tcp() }); let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); @@ -621,12 +632,12 @@ async fn test_reading_deflate_encoding_large_random() { .collect::(); let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().service( + HttpService::build() + .h1(App::new().service( web::resource("/") .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) + )) + .tcp() }); let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); @@ -651,12 +662,12 @@ async fn test_reading_deflate_encoding_large_random() { #[cfg(feature = "brotli")] async fn test_brotli_encoding() { let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().service( + HttpService::build() + .h1(App::new().service( web::resource("/") .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) + )) + .tcp() }); let mut e = BrotliEncoder::new(Vec::new(), 5); @@ -681,12 +692,12 @@ async fn test_brotli_encoding() { async fn test_brotli_encoding_large() { let data = STR.repeat(10); let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().service( + HttpService::build() + .h1(App::new().service( web::resource("/") .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) + )) + .tcp() }); let mut e = BrotliEncoder::new(Vec::new(), 5);