mirror of
https://github.com/fafhrd91/actix-web
synced 2024-11-24 00:21:08 +01:00
drop unpin constraint
This commit is contained in:
parent
687884fb94
commit
1ffa7d18d3
@ -74,6 +74,7 @@ language-tags = "0.2"
|
|||||||
log = "0.4"
|
log = "0.4"
|
||||||
mime = "0.3"
|
mime = "0.3"
|
||||||
percent-encoding = "2.1"
|
percent-encoding = "2.1"
|
||||||
|
pin-project = "0.4.5"
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
regex = "1.0"
|
regex = "1.0"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
@ -107,7 +108,7 @@ webpki-roots = { version = "0.18", optional = true }
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "1.0.0-alpha.1"
|
actix-rt = "1.0.0-alpha.1"
|
||||||
actix-server = { version = "0.8.0-alpha.1", features=["openssl"] }
|
actix-server = { version = "0.8.0-alpha.1", features=["openssl", "rustls"] }
|
||||||
actix-connect = { version = "1.0.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-http-test = { version = "0.3.0-alpha.1", features=["openssl"] }
|
||||||
env_logger = "0.6"
|
env_logger = "0.6"
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use std::{env, io};
|
use std::{env, io};
|
||||||
|
|
||||||
use actix_http::{error::PayloadError, HttpService, Request, Response};
|
use actix_http::{Error, HttpService, Request, Response};
|
||||||
use actix_server::Server;
|
use actix_server::Server;
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use futures::{Future, Stream};
|
use futures::StreamExt;
|
||||||
use http::header::HeaderValue;
|
use http::header::HeaderValue;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
@ -17,20 +17,22 @@ fn main() -> io::Result<()> {
|
|||||||
.client_timeout(1000)
|
.client_timeout(1000)
|
||||||
.client_disconnect(1000)
|
.client_disconnect(1000)
|
||||||
.finish(|mut req: Request| {
|
.finish(|mut req: Request| {
|
||||||
req.take_payload()
|
async move {
|
||||||
.fold(BytesMut::new(), move |mut body, chunk| {
|
let mut body = BytesMut::new();
|
||||||
body.extend_from_slice(&chunk);
|
while let Some(item) = req.payload().next().await {
|
||||||
Ok::<_, PayloadError>(body)
|
body.extend_from_slice(&item?);
|
||||||
})
|
}
|
||||||
.and_then(|bytes| {
|
|
||||||
info!("request body: {:?}", bytes);
|
info!("request body: {:?}", body);
|
||||||
let mut res = Response::Ok();
|
Ok::<_, Error>(
|
||||||
res.header(
|
Response::Ok()
|
||||||
"x-head",
|
.header(
|
||||||
HeaderValue::from_static("dummy value!"),
|
"x-head",
|
||||||
);
|
HeaderValue::from_static("dummy value!"),
|
||||||
Ok(res.body(bytes))
|
)
|
||||||
})
|
.body(body),
|
||||||
|
)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})?
|
})?
|
||||||
.run()
|
.run()
|
||||||
|
@ -1,25 +1,22 @@
|
|||||||
use std::{env, io};
|
use std::{env, io};
|
||||||
|
|
||||||
use actix_http::http::HeaderValue;
|
use actix_http::http::HeaderValue;
|
||||||
use actix_http::{error::PayloadError, Error, HttpService, Request, Response};
|
use actix_http::{Error, HttpService, Request, Response};
|
||||||
use actix_server::Server;
|
use actix_server::Server;
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use futures::{Future, Stream};
|
use futures::StreamExt;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
fn handle_request(mut req: Request) -> impl Future<Item = Response, Error = Error> {
|
async fn handle_request(mut req: Request) -> Result<Response, Error> {
|
||||||
req.take_payload()
|
let mut body = BytesMut::new();
|
||||||
.fold(BytesMut::new(), move |mut body, chunk| {
|
while let Some(item) = req.payload().next().await {
|
||||||
body.extend_from_slice(&chunk);
|
body.extend_from_slice(&item?)
|
||||||
Ok::<_, PayloadError>(body)
|
}
|
||||||
})
|
|
||||||
.from_err()
|
info!("request body: {:?}", body);
|
||||||
.and_then(|bytes| {
|
Ok(Response::Ok()
|
||||||
info!("request body: {:?}", bytes);
|
.header("x-head", HeaderValue::from_static("dummy value!"))
|
||||||
let mut res = Response::Ok();
|
.body(body))
|
||||||
res.header("x-head", HeaderValue::from_static("dummy value!"));
|
|
||||||
Ok(res.body(bytes))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> io::Result<()> {
|
fn main() -> io::Result<()> {
|
||||||
@ -28,7 +25,7 @@ fn main() -> io::Result<()> {
|
|||||||
|
|
||||||
Server::build()
|
Server::build()
|
||||||
.bind("echo", "127.0.0.1:8080", || {
|
.bind("echo", "127.0.0.1:8080", || {
|
||||||
HttpService::build().finish(|_req: Request| handle_request(_req))
|
HttpService::build().finish(handle_request)
|
||||||
})?
|
})?
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ use std::{fmt, mem};
|
|||||||
|
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
|
use pin_project::{pin_project, project};
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ impl BodySize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Type that provides this trait can be streamed to a peer.
|
/// Type that provides this trait can be streamed to a peer.
|
||||||
pub trait MessageBody: Unpin {
|
pub trait MessageBody {
|
||||||
fn size(&self) -> BodySize;
|
fn size(&self) -> BodySize;
|
||||||
|
|
||||||
fn poll_next(&mut self, cx: &mut Context) -> Poll<Option<Result<Bytes, Error>>>;
|
fn poll_next(&mut self, cx: &mut Context) -> Poll<Option<Result<Bytes, Error>>>;
|
||||||
@ -57,6 +58,7 @@ impl<T: MessageBody> MessageBody for Box<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pin_project]
|
||||||
pub enum ResponseBody<B> {
|
pub enum ResponseBody<B> {
|
||||||
Body(B),
|
Body(B),
|
||||||
Other(Body),
|
Other(Body),
|
||||||
@ -106,8 +108,13 @@ impl<B: MessageBody> MessageBody for ResponseBody<B> {
|
|||||||
impl<B: MessageBody> Stream for ResponseBody<B> {
|
impl<B: MessageBody> Stream for ResponseBody<B> {
|
||||||
type Item = Result<Bytes, Error>;
|
type Item = Result<Bytes, Error>;
|
||||||
|
|
||||||
|
#[project]
|
||||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
|
||||||
self.get_mut().poll_next(cx)
|
#[project]
|
||||||
|
match self.project() {
|
||||||
|
ResponseBody::Body(ref mut body) => body.poll_next(cx),
|
||||||
|
ResponseBody::Other(ref mut body) => body.poll_next(cx),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,7 +250,7 @@ impl From<serde_json::Value> for Body {
|
|||||||
|
|
||||||
impl<S> From<SizedStream<S>> for Body
|
impl<S> From<SizedStream<S>> for Body
|
||||||
where
|
where
|
||||||
S: Stream<Item = Result<Bytes, Error>> + Unpin + 'static,
|
S: Stream<Item = Result<Bytes, Error>> + 'static,
|
||||||
{
|
{
|
||||||
fn from(s: SizedStream<S>) -> Body {
|
fn from(s: SizedStream<S>) -> Body {
|
||||||
Body::from_message(s)
|
Body::from_message(s)
|
||||||
@ -252,7 +259,7 @@ where
|
|||||||
|
|
||||||
impl<S, E> From<BodyStream<S, E>> for Body
|
impl<S, E> From<BodyStream<S, E>> for Body
|
||||||
where
|
where
|
||||||
S: Stream<Item = Result<Bytes, E>> + Unpin + 'static,
|
S: Stream<Item = Result<Bytes, E>> + 'static,
|
||||||
E: Into<Error> + 'static,
|
E: Into<Error> + 'static,
|
||||||
{
|
{
|
||||||
fn from(s: BodyStream<S, E>) -> Body {
|
fn from(s: BodyStream<S, E>) -> Body {
|
||||||
@ -350,7 +357,9 @@ impl MessageBody for String {
|
|||||||
|
|
||||||
/// Type represent streaming body.
|
/// Type represent streaming body.
|
||||||
/// Response does not contain `content-length` header and appropriate transfer encoding is used.
|
/// Response does not contain `content-length` header and appropriate transfer encoding is used.
|
||||||
|
#[pin_project]
|
||||||
pub struct BodyStream<S, E> {
|
pub struct BodyStream<S, E> {
|
||||||
|
#[pin]
|
||||||
stream: S,
|
stream: S,
|
||||||
_t: PhantomData<E>,
|
_t: PhantomData<E>,
|
||||||
}
|
}
|
||||||
@ -368,16 +377,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, E> Unpin for BodyStream<S, E>
|
|
||||||
where
|
|
||||||
S: Stream<Item = Result<Bytes, E>> + Unpin,
|
|
||||||
E: Into<Error>,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, E> MessageBody for BodyStream<S, E>
|
impl<S, E> MessageBody for BodyStream<S, E>
|
||||||
where
|
where
|
||||||
S: Stream<Item = Result<Bytes, E>> + Unpin,
|
S: Stream<Item = Result<Bytes, E>>,
|
||||||
E: Into<Error>,
|
E: Into<Error>,
|
||||||
{
|
{
|
||||||
fn size(&self) -> BodySize {
|
fn size(&self) -> BodySize {
|
||||||
@ -385,7 +387,9 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn poll_next(&mut self, cx: &mut Context) -> Poll<Option<Result<Bytes, Error>>> {
|
fn poll_next(&mut self, cx: &mut Context) -> Poll<Option<Result<Bytes, Error>>> {
|
||||||
Pin::new(&mut self.stream)
|
unsafe { Pin::new_unchecked(self) }
|
||||||
|
.project()
|
||||||
|
.stream
|
||||||
.poll_next(cx)
|
.poll_next(cx)
|
||||||
.map(|res| res.map(|res| res.map_err(std::convert::Into::into)))
|
.map(|res| res.map(|res| res.map_err(std::convert::Into::into)))
|
||||||
}
|
}
|
||||||
@ -393,8 +397,10 @@ where
|
|||||||
|
|
||||||
/// Type represent streaming body. This body implementation should be used
|
/// Type represent streaming body. This body implementation should be used
|
||||||
/// if total size of stream is known. Data get sent as is without using transfer encoding.
|
/// if total size of stream is known. Data get sent as is without using transfer encoding.
|
||||||
|
#[pin_project]
|
||||||
pub struct SizedStream<S> {
|
pub struct SizedStream<S> {
|
||||||
size: u64,
|
size: u64,
|
||||||
|
#[pin]
|
||||||
stream: S,
|
stream: S,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,20 +415,25 @@ where
|
|||||||
|
|
||||||
impl<S> MessageBody for SizedStream<S>
|
impl<S> MessageBody for SizedStream<S>
|
||||||
where
|
where
|
||||||
S: Stream<Item = Result<Bytes, Error>> + Unpin,
|
S: Stream<Item = Result<Bytes, Error>>,
|
||||||
{
|
{
|
||||||
fn size(&self) -> BodySize {
|
fn size(&self) -> BodySize {
|
||||||
BodySize::Sized64(self.size)
|
BodySize::Sized64(self.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_next(&mut self, cx: &mut Context) -> Poll<Option<Result<Bytes, Error>>> {
|
fn poll_next(&mut self, cx: &mut Context) -> Poll<Option<Result<Bytes, Error>>> {
|
||||||
Pin::new(&mut self.stream).poll_next(cx)
|
unsafe { Pin::new_unchecked(self) }
|
||||||
|
.project()
|
||||||
|
.stream
|
||||||
|
.poll_next(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use actix_http_test::block_on;
|
||||||
|
use futures::future::{lazy, poll_fn};
|
||||||
|
|
||||||
impl Body {
|
impl Body {
|
||||||
pub(crate) fn get_ref(&self) -> &[u8] {
|
pub(crate) fn get_ref(&self) -> &[u8] {
|
||||||
@ -450,8 +461,8 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!("test".size(), BodySize::Sized(4));
|
assert_eq!("test".size(), BodySize::Sized(4));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"test".poll_next().unwrap(),
|
block_on(poll_fn(|cx| "test".poll_next(cx))).unwrap().ok(),
|
||||||
Async::Ready(Some(Bytes::from("test")))
|
Some(Bytes::from("test"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -467,8 +478,10 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!((&b"test"[..]).size(), BodySize::Sized(4));
|
assert_eq!((&b"test"[..]).size(), BodySize::Sized(4));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(&b"test"[..]).poll_next().unwrap(),
|
block_on(poll_fn(|cx| (&b"test"[..]).poll_next(cx)))
|
||||||
Async::Ready(Some(Bytes::from("test")))
|
.unwrap()
|
||||||
|
.ok(),
|
||||||
|
Some(Bytes::from("test"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -479,8 +492,10 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(Vec::from("test").size(), BodySize::Sized(4));
|
assert_eq!(Vec::from("test").size(), BodySize::Sized(4));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Vec::from("test").poll_next().unwrap(),
|
block_on(poll_fn(|cx| Vec::from("test").poll_next(cx)))
|
||||||
Async::Ready(Some(Bytes::from("test")))
|
.unwrap()
|
||||||
|
.ok(),
|
||||||
|
Some(Bytes::from("test"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,8 +507,8 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(b.size(), BodySize::Sized(4));
|
assert_eq!(b.size(), BodySize::Sized(4));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
b.poll_next().unwrap(),
|
block_on(poll_fn(|cx| b.poll_next(cx))).unwrap().ok(),
|
||||||
Async::Ready(Some(Bytes::from("test")))
|
Some(Bytes::from("test"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -505,8 +520,8 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(b.size(), BodySize::Sized(4));
|
assert_eq!(b.size(), BodySize::Sized(4));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
b.poll_next().unwrap(),
|
block_on(poll_fn(|cx| b.poll_next(cx))).unwrap().ok(),
|
||||||
Async::Ready(Some(Bytes::from("test")))
|
Some(Bytes::from("test"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -520,22 +535,22 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(b.size(), BodySize::Sized(4));
|
assert_eq!(b.size(), BodySize::Sized(4));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
b.poll_next().unwrap(),
|
block_on(poll_fn(|cx| b.poll_next(cx))).unwrap().ok(),
|
||||||
Async::Ready(Some(Bytes::from("test")))
|
Some(Bytes::from("test"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unit() {
|
fn test_unit() {
|
||||||
assert_eq!(().size(), BodySize::Empty);
|
assert_eq!(().size(), BodySize::Empty);
|
||||||
assert_eq!(().poll_next().unwrap(), Async::Ready(None));
|
assert!(block_on(poll_fn(|cx| ().poll_next(cx))).is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_box() {
|
fn test_box() {
|
||||||
let mut val = Box::new(());
|
let mut val = Box::new(());
|
||||||
assert_eq!(val.size(), BodySize::Empty);
|
assert_eq!(val.size(), BodySize::Empty);
|
||||||
assert_eq!(val.poll_next().unwrap(), Async::Ready(None));
|
assert!(block_on(poll_fn(|cx| val.poll_next(cx))).is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -33,11 +33,9 @@ pub struct HttpServiceBuilder<T, S, X = ExpectHandler, U = UpgradeHandler<T>> {
|
|||||||
impl<T, S> HttpServiceBuilder<T, S, ExpectHandler, UpgradeHandler<T>>
|
impl<T, S> HttpServiceBuilder<T, S, ExpectHandler, UpgradeHandler<T>>
|
||||||
where
|
where
|
||||||
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Future: Unpin,
|
<S::Service as Service>::Future: 'static,
|
||||||
S::Service: Unpin,
|
|
||||||
<S::Service as Service>::Future: Unpin + 'static,
|
|
||||||
{
|
{
|
||||||
/// Create instance of `ServiceConfigBuilder`
|
/// Create instance of `ServiceConfigBuilder`
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@ -56,17 +54,13 @@ where
|
|||||||
impl<T, S, X, U> HttpServiceBuilder<T, S, X, U>
|
impl<T, S, X, U> HttpServiceBuilder<T, S, X, U>
|
||||||
where
|
where
|
||||||
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Future: Unpin,
|
<S::Service as Service>::Future: 'static,
|
||||||
S::Service: Unpin,
|
|
||||||
<S::Service as Service>::Future: Unpin + 'static,
|
|
||||||
X: ServiceFactory<Config = SrvConfig, Request = Request, Response = Request>,
|
X: ServiceFactory<Config = SrvConfig, Request = Request, Response = Request>,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::InitError: fmt::Debug,
|
X::InitError: fmt::Debug,
|
||||||
X::Future: Unpin,
|
<X::Service as Service>::Future: 'static,
|
||||||
X::Service: Unpin,
|
|
||||||
<X::Service as Service>::Future: Unpin + 'static,
|
|
||||||
U: ServiceFactory<
|
U: ServiceFactory<
|
||||||
Config = SrvConfig,
|
Config = SrvConfig,
|
||||||
Request = (Request, Framed<T, Codec>),
|
Request = (Request, Framed<T, Codec>),
|
||||||
@ -74,9 +68,7 @@ where
|
|||||||
>,
|
>,
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
U::InitError: fmt::Debug,
|
U::InitError: fmt::Debug,
|
||||||
U::Future: Unpin,
|
<U::Service as Service>::Future: 'static,
|
||||||
U::Service: Unpin,
|
|
||||||
<U::Service as Service>::Future: Unpin + 'static,
|
|
||||||
{
|
{
|
||||||
/// Set server keep-alive setting.
|
/// Set server keep-alive setting.
|
||||||
///
|
///
|
||||||
@ -124,9 +116,7 @@ where
|
|||||||
X1: ServiceFactory<Config = SrvConfig, Request = Request, Response = Request>,
|
X1: ServiceFactory<Config = SrvConfig, Request = Request, Response = Request>,
|
||||||
X1::Error: Into<Error>,
|
X1::Error: Into<Error>,
|
||||||
X1::InitError: fmt::Debug,
|
X1::InitError: fmt::Debug,
|
||||||
X1::Future: Unpin,
|
<X1::Service as Service>::Future: 'static,
|
||||||
X1::Service: Unpin,
|
|
||||||
<X1::Service as Service>::Future: Unpin + 'static,
|
|
||||||
{
|
{
|
||||||
HttpServiceBuilder {
|
HttpServiceBuilder {
|
||||||
keep_alive: self.keep_alive,
|
keep_alive: self.keep_alive,
|
||||||
@ -153,9 +143,7 @@ where
|
|||||||
>,
|
>,
|
||||||
U1::Error: fmt::Display,
|
U1::Error: fmt::Display,
|
||||||
U1::InitError: fmt::Debug,
|
U1::InitError: fmt::Debug,
|
||||||
U1::Future: Unpin,
|
<U1::Service as Service>::Future: 'static,
|
||||||
U1::Service: Unpin,
|
|
||||||
<U1::Service as Service>::Future: Unpin + 'static,
|
|
||||||
{
|
{
|
||||||
HttpServiceBuilder {
|
HttpServiceBuilder {
|
||||||
keep_alive: self.keep_alive,
|
keep_alive: self.keep_alive,
|
||||||
@ -186,13 +174,10 @@ where
|
|||||||
where
|
where
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
F: IntoServiceFactory<S>,
|
F: IntoServiceFactory<S>,
|
||||||
S::Future: Unpin,
|
S::Error: Into<Error> + 'static,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
S::Service: Unpin,
|
<S::Service as Service>::Future: 'static,
|
||||||
<S::Service as Service>::Future: Unpin + 'static,
|
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
let cfg = ServiceConfig::new(
|
let cfg = ServiceConfig::new(
|
||||||
self.keep_alive,
|
self.keep_alive,
|
||||||
@ -210,13 +195,10 @@ where
|
|||||||
where
|
where
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
F: IntoServiceFactory<S>,
|
F: IntoServiceFactory<S>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
S::Future: Unpin,
|
<S::Service as Service>::Future: 'static,
|
||||||
S::Service: Unpin,
|
|
||||||
<S::Service as Service>::Future: Unpin + 'static,
|
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
let cfg = ServiceConfig::new(
|
let cfg = ServiceConfig::new(
|
||||||
self.keep_alive,
|
self.keep_alive,
|
||||||
@ -231,13 +213,10 @@ where
|
|||||||
where
|
where
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
F: IntoServiceFactory<S>,
|
F: IntoServiceFactory<S>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
S::Future: Unpin,
|
<S::Service as Service>::Future: 'static,
|
||||||
S::Service: Unpin,
|
|
||||||
<S::Service as Service>::Future: Unpin + 'static,
|
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
let cfg = ServiceConfig::new(
|
let cfg = ServiceConfig::new(
|
||||||
self.keep_alive,
|
self.keep_alive,
|
||||||
|
@ -6,6 +6,7 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
|||||||
use bytes::{Buf, Bytes};
|
use bytes::{Buf, Bytes};
|
||||||
use futures::future::{err, Either, Future, FutureExt, LocalBoxFuture, Ready};
|
use futures::future::{err, Either, Future, FutureExt, LocalBoxFuture, Ready};
|
||||||
use h2::client::SendRequest;
|
use h2::client::SendRequest;
|
||||||
|
use pin_project::{pin_project, project};
|
||||||
|
|
||||||
use crate::body::MessageBody;
|
use crate::body::MessageBody;
|
||||||
use crate::h1::ClientCodec;
|
use crate::h1::ClientCodec;
|
||||||
@ -42,9 +43,7 @@ pub trait Connection {
|
|||||||
fn open_tunnel<H: Into<RequestHeadType>>(self, head: H) -> Self::TunnelFuture;
|
fn open_tunnel<H: Into<RequestHeadType>>(self, head: H) -> Self::TunnelFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait ConnectionLifetime:
|
pub(crate) trait ConnectionLifetime: AsyncRead + AsyncWrite + 'static {
|
||||||
AsyncRead + AsyncWrite + Unpin + 'static
|
|
||||||
{
|
|
||||||
/// Close connection
|
/// Close connection
|
||||||
fn close(&mut self);
|
fn close(&mut self);
|
||||||
|
|
||||||
@ -73,7 +72,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsyncRead + AsyncWrite> IoConnection<T> {
|
impl<T: AsyncRead + AsyncWrite + Unpin> IoConnection<T> {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
io: ConnectionType<T>,
|
io: ConnectionType<T>,
|
||||||
created: time::Instant,
|
created: time::Instant,
|
||||||
@ -205,24 +204,27 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pin_project]
|
||||||
pub enum EitherIo<A, B> {
|
pub enum EitherIo<A, B> {
|
||||||
A(A),
|
A(#[pin] A),
|
||||||
B(B),
|
B(#[pin] B),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A, B> AsyncRead for EitherIo<A, B>
|
impl<A, B> AsyncRead for EitherIo<A, B>
|
||||||
where
|
where
|
||||||
A: AsyncRead + Unpin,
|
A: AsyncRead,
|
||||||
B: AsyncRead + Unpin,
|
B: AsyncRead,
|
||||||
{
|
{
|
||||||
|
#[project]
|
||||||
fn poll_read(
|
fn poll_read(
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
buf: &mut [u8],
|
buf: &mut [u8],
|
||||||
) -> Poll<io::Result<usize>> {
|
) -> Poll<io::Result<usize>> {
|
||||||
match self.get_mut() {
|
#[project]
|
||||||
EitherIo::A(ref mut val) => Pin::new(val).poll_read(cx, buf),
|
match self.project() {
|
||||||
EitherIo::B(ref mut val) => Pin::new(val).poll_read(cx, buf),
|
EitherIo::A(val) => val.poll_read(cx, buf),
|
||||||
|
EitherIo::B(val) => val.poll_read(cx, buf),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,37 +238,44 @@ where
|
|||||||
|
|
||||||
impl<A, B> AsyncWrite for EitherIo<A, B>
|
impl<A, B> AsyncWrite for EitherIo<A, B>
|
||||||
where
|
where
|
||||||
A: AsyncWrite + Unpin,
|
A: AsyncWrite,
|
||||||
B: AsyncWrite + Unpin,
|
B: AsyncWrite,
|
||||||
{
|
{
|
||||||
|
#[project]
|
||||||
fn poll_write(
|
fn poll_write(
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
cx: &mut Context,
|
cx: &mut Context,
|
||||||
buf: &[u8],
|
buf: &[u8],
|
||||||
) -> Poll<io::Result<usize>> {
|
) -> Poll<io::Result<usize>> {
|
||||||
match self.get_mut() {
|
#[project]
|
||||||
EitherIo::A(ref mut val) => Pin::new(val).poll_write(cx, buf),
|
match self.project() {
|
||||||
EitherIo::B(ref mut val) => Pin::new(val).poll_write(cx, buf),
|
EitherIo::A(val) => val.poll_write(cx, buf),
|
||||||
|
EitherIo::B(val) => val.poll_write(cx, buf),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[project]
|
||||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
match self.get_mut() {
|
#[project]
|
||||||
EitherIo::A(ref mut val) => Pin::new(val).poll_flush(cx),
|
match self.project() {
|
||||||
EitherIo::B(ref mut val) => Pin::new(val).poll_flush(cx),
|
EitherIo::A(val) => val.poll_flush(cx),
|
||||||
|
EitherIo::B(val) => val.poll_flush(cx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[project]
|
||||||
fn poll_shutdown(
|
fn poll_shutdown(
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<io::Result<()>> {
|
) -> Poll<io::Result<()>> {
|
||||||
match self.get_mut() {
|
#[project]
|
||||||
EitherIo::A(ref mut val) => Pin::new(val).poll_shutdown(cx),
|
match self.project() {
|
||||||
EitherIo::B(ref mut val) => Pin::new(val).poll_shutdown(cx),
|
EitherIo::A(val) => val.poll_shutdown(cx),
|
||||||
|
EitherIo::B(val) => val.poll_shutdown(cx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[project]
|
||||||
fn poll_write_buf<U: Buf>(
|
fn poll_write_buf<U: Buf>(
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
@ -275,9 +284,10 @@ where
|
|||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
match self.get_mut() {
|
#[project]
|
||||||
EitherIo::A(ref mut val) => Pin::new(val).poll_write_buf(cx, buf),
|
match self.project() {
|
||||||
EitherIo::B(ref mut val) => Pin::new(val).poll_write_buf(cx, buf),
|
EitherIo::A(val) => val.poll_write_buf(cx, buf),
|
||||||
|
EitherIo::B(val) => val.poll_write_buf(cx, buf),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,8 +62,8 @@ pub struct Connector<T, U> {
|
|||||||
_t: PhantomData<U>,
|
_t: PhantomData<U>,
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Io: AsyncRead + AsyncWrite {}
|
trait Io: AsyncRead + AsyncWrite + Unpin {}
|
||||||
impl<T: AsyncRead + AsyncWrite> Io for T {}
|
impl<T: AsyncRead + AsyncWrite + Unpin> Io for T {}
|
||||||
|
|
||||||
impl Connector<(), ()> {
|
impl Connector<(), ()> {
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
@ -123,7 +123,6 @@ impl<T, U> Connector<T, U> {
|
|||||||
Response = TcpConnection<Uri, U1>,
|
Response = TcpConnection<Uri, U1>,
|
||||||
Error = actix_connect::ConnectError,
|
Error = actix_connect::ConnectError,
|
||||||
> + Clone,
|
> + Clone,
|
||||||
T1::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
Connector {
|
Connector {
|
||||||
connector,
|
connector,
|
||||||
@ -222,7 +221,7 @@ where
|
|||||||
{
|
{
|
||||||
let connector = TimeoutService::new(
|
let connector = TimeoutService::new(
|
||||||
self.timeout,
|
self.timeout,
|
||||||
apply_fn(UnpinWrapper(self.connector), |msg: Connect, srv| {
|
apply_fn(self.connector, |msg: Connect, srv| {
|
||||||
srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr))
|
srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr))
|
||||||
})
|
})
|
||||||
.map_err(ConnectError::from)
|
.map_err(ConnectError::from)
|
||||||
@ -257,35 +256,33 @@ where
|
|||||||
let ssl_service = TimeoutService::new(
|
let ssl_service = TimeoutService::new(
|
||||||
self.timeout,
|
self.timeout,
|
||||||
pipeline(
|
pipeline(
|
||||||
apply_fn(
|
apply_fn(self.connector.clone(), |msg: Connect, srv| {
|
||||||
UnpinWrapper(self.connector.clone()),
|
srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr))
|
||||||
|msg: Connect, srv| {
|
})
|
||||||
srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr))
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.map_err(ConnectError::from),
|
.map_err(ConnectError::from),
|
||||||
)
|
)
|
||||||
.and_then(match self.ssl {
|
.and_then(match self.ssl {
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
SslConnector::Openssl(ssl) => OpensslConnector::service(ssl)
|
SslConnector::Openssl(ssl) => service(
|
||||||
.map(|stream| {
|
OpensslConnector::service(ssl)
|
||||||
let sock = stream.into_parts().0;
|
.map(|stream| {
|
||||||
let h2 = sock
|
let sock = stream.into_parts().0;
|
||||||
.ssl()
|
let h2 = sock
|
||||||
.selected_alpn_protocol()
|
.ssl()
|
||||||
.map(|protos| protos.windows(2).any(|w| w == H2))
|
.selected_alpn_protocol()
|
||||||
.unwrap_or(false);
|
.map(|protos| protos.windows(2).any(|w| w == H2))
|
||||||
if h2 {
|
.unwrap_or(false);
|
||||||
(Box::new(sock) as Box<dyn Io + Unpin>, Protocol::Http2)
|
if h2 {
|
||||||
} else {
|
(Box::new(sock) as Box<dyn Io>, Protocol::Http2)
|
||||||
(Box::new(sock) as Box<dyn Io + Unpin>, Protocol::Http1)
|
} else {
|
||||||
}
|
(Box::new(sock) as Box<dyn Io>, Protocol::Http1)
|
||||||
})
|
}
|
||||||
.map_err(ConnectError::from),
|
})
|
||||||
|
.map_err(ConnectError::from),
|
||||||
|
),
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(feature = "rustls")]
|
||||||
SslConnector::Rustls(ssl) => service(
|
SslConnector::Rustls(ssl) => service(
|
||||||
UnpinWrapper(RustlsConnector::service(ssl))
|
RustlsConnector::service(ssl)
|
||||||
.map_err(ConnectError::from)
|
.map_err(ConnectError::from)
|
||||||
.map(|stream| {
|
.map(|stream| {
|
||||||
let sock = stream.into_parts().0;
|
let sock = stream.into_parts().0;
|
||||||
@ -296,15 +293,9 @@ where
|
|||||||
.map(|protos| protos.windows(2).any(|w| w == H2))
|
.map(|protos| protos.windows(2).any(|w| w == H2))
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
if h2 {
|
if h2 {
|
||||||
(
|
(Box::new(sock) as Box<dyn Io>, Protocol::Http2)
|
||||||
Box::new(sock) as Box<dyn Io + Unpin>,
|
|
||||||
Protocol::Http2,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
(
|
(Box::new(sock) as Box<dyn Io>, Protocol::Http1)
|
||||||
Box::new(sock) as Box<dyn Io + Unpin>,
|
|
||||||
Protocol::Http1,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@ -317,7 +308,7 @@ where
|
|||||||
|
|
||||||
let tcp_service = TimeoutService::new(
|
let tcp_service = TimeoutService::new(
|
||||||
self.timeout,
|
self.timeout,
|
||||||
apply_fn(UnpinWrapper(self.connector), |msg: Connect, srv| {
|
apply_fn(self.connector, |msg: Connect, srv| {
|
||||||
srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr))
|
srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr))
|
||||||
})
|
})
|
||||||
.map_err(ConnectError::from)
|
.map_err(ConnectError::from)
|
||||||
@ -348,42 +339,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct UnpinWrapper<T: Clone>(T);
|
|
||||||
|
|
||||||
impl<T: Clone> Unpin for UnpinWrapper<T> {}
|
|
||||||
|
|
||||||
impl<T: Service + Clone> Service for UnpinWrapper<T> {
|
|
||||||
type Request = T::Request;
|
|
||||||
type Response = T::Response;
|
|
||||||
type Error = T::Error;
|
|
||||||
type Future = UnpinWrapperFut<T>;
|
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), T::Error>> {
|
|
||||||
self.0.poll_ready(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self, req: T::Request) -> Self::Future {
|
|
||||||
UnpinWrapperFut {
|
|
||||||
fut: self.0.call(req),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UnpinWrapperFut<T: Service> {
|
|
||||||
fut: T::Future,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Service> Unpin for UnpinWrapperFut<T> {}
|
|
||||||
|
|
||||||
impl<T: Service> Future for UnpinWrapperFut<T> {
|
|
||||||
type Output = Result<T::Response, T::Error>;
|
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
|
||||||
unsafe { Pin::new_unchecked(&mut self.get_mut().fut) }.poll(cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(any(feature = "openssl", feature = "rustls")))]
|
#[cfg(not(any(feature = "openssl", feature = "rustls")))]
|
||||||
mod connect_impl {
|
mod connect_impl {
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
@ -396,9 +351,8 @@ mod connect_impl {
|
|||||||
|
|
||||||
pub(crate) struct InnerConnector<T, Io>
|
pub(crate) struct InnerConnector<T, Io>
|
||||||
where
|
where
|
||||||
Io: AsyncRead + AsyncWrite + 'static,
|
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
T: Service<Request = Connect, Response = (Io, Protocol), Error = ConnectError>
|
T: Service<Request = Connect, Response = (Io, Protocol), Error = ConnectError>
|
||||||
+ Unpin
|
|
||||||
+ 'static,
|
+ 'static,
|
||||||
{
|
{
|
||||||
pub(crate) tcp_pool: ConnectionPool<T, Io>,
|
pub(crate) tcp_pool: ConnectionPool<T, Io>,
|
||||||
@ -406,9 +360,8 @@ mod connect_impl {
|
|||||||
|
|
||||||
impl<T, Io> Clone for InnerConnector<T, Io>
|
impl<T, Io> Clone for InnerConnector<T, Io>
|
||||||
where
|
where
|
||||||
Io: AsyncRead + AsyncWrite + 'static,
|
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
T: Service<Request = Connect, Response = (Io, Protocol), Error = ConnectError>
|
T: Service<Request = Connect, Response = (Io, Protocol), Error = ConnectError>
|
||||||
+ Unpin
|
|
||||||
+ 'static,
|
+ 'static,
|
||||||
{
|
{
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
@ -422,9 +375,7 @@ mod connect_impl {
|
|||||||
where
|
where
|
||||||
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
T: Service<Request = Connect, Response = (Io, Protocol), Error = ConnectError>
|
T: Service<Request = Connect, Response = (Io, Protocol), Error = ConnectError>
|
||||||
+ Unpin
|
|
||||||
+ 'static,
|
+ 'static,
|
||||||
T::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
type Request = Connect;
|
type Request = Connect;
|
||||||
type Response = IoConnection<Io>;
|
type Response = IoConnection<Io>;
|
||||||
@ -465,8 +416,6 @@ mod connect_impl {
|
|||||||
Io2: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io2: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
T1: Service<Request = Connect, Response = (Io1, Protocol), Error = ConnectError>,
|
T1: Service<Request = Connect, Response = (Io1, Protocol), Error = ConnectError>,
|
||||||
T2: Service<Request = Connect, Response = (Io2, Protocol), Error = ConnectError>,
|
T2: Service<Request = Connect, Response = (Io2, Protocol), Error = ConnectError>,
|
||||||
T1::Future: Unpin,
|
|
||||||
T2::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
pub(crate) tcp_pool: ConnectionPool<T1, Io1>,
|
pub(crate) tcp_pool: ConnectionPool<T1, Io1>,
|
||||||
pub(crate) ssl_pool: ConnectionPool<T2, Io2>,
|
pub(crate) ssl_pool: ConnectionPool<T2, Io2>,
|
||||||
@ -477,13 +426,9 @@ mod connect_impl {
|
|||||||
Io1: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io1: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
Io2: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io2: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
T1: Service<Request = Connect, Response = (Io1, Protocol), Error = ConnectError>
|
T1: Service<Request = Connect, Response = (Io1, Protocol), Error = ConnectError>
|
||||||
+ Unpin
|
|
||||||
+ 'static,
|
+ 'static,
|
||||||
T2: Service<Request = Connect, Response = (Io2, Protocol), Error = ConnectError>
|
T2: Service<Request = Connect, Response = (Io2, Protocol), Error = ConnectError>
|
||||||
+ Unpin
|
|
||||||
+ 'static,
|
+ 'static,
|
||||||
T1::Future: Unpin,
|
|
||||||
T2::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
InnerConnector {
|
InnerConnector {
|
||||||
@ -498,13 +443,9 @@ mod connect_impl {
|
|||||||
Io1: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io1: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
Io2: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io2: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
T1: Service<Request = Connect, Response = (Io1, Protocol), Error = ConnectError>
|
T1: Service<Request = Connect, Response = (Io1, Protocol), Error = ConnectError>
|
||||||
+ Unpin
|
|
||||||
+ 'static,
|
+ 'static,
|
||||||
T2: Service<Request = Connect, Response = (Io2, Protocol), Error = ConnectError>
|
T2: Service<Request = Connect, Response = (Io2, Protocol), Error = ConnectError>
|
||||||
+ Unpin
|
|
||||||
+ 'static,
|
+ 'static,
|
||||||
T1::Future: Unpin,
|
|
||||||
T2::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
type Request = Connect;
|
type Request = Connect;
|
||||||
type Response = EitherConnection<Io1, Io2>;
|
type Response = EitherConnection<Io1, Io2>;
|
||||||
@ -532,14 +473,14 @@ mod connect_impl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pin_project::pin_project]
|
||||||
pub(crate) struct InnerConnectorResponseA<T, Io1, Io2>
|
pub(crate) struct InnerConnectorResponseA<T, Io1, Io2>
|
||||||
where
|
where
|
||||||
Io1: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io1: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
T: Service<Request = Connect, Response = (Io1, Protocol), Error = ConnectError>
|
T: Service<Request = Connect, Response = (Io1, Protocol), Error = ConnectError>
|
||||||
+ Unpin
|
|
||||||
+ 'static,
|
+ 'static,
|
||||||
T::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
|
#[pin]
|
||||||
fut: <ConnectionPool<T, Io1> as Service>::Future,
|
fut: <ConnectionPool<T, Io1> as Service>::Future,
|
||||||
_t: PhantomData<Io2>,
|
_t: PhantomData<Io2>,
|
||||||
}
|
}
|
||||||
@ -547,9 +488,7 @@ mod connect_impl {
|
|||||||
impl<T, Io1, Io2> Future for InnerConnectorResponseA<T, Io1, Io2>
|
impl<T, Io1, Io2> Future for InnerConnectorResponseA<T, Io1, Io2>
|
||||||
where
|
where
|
||||||
T: Service<Request = Connect, Response = (Io1, Protocol), Error = ConnectError>
|
T: Service<Request = Connect, Response = (Io1, Protocol), Error = ConnectError>
|
||||||
+ Unpin
|
|
||||||
+ 'static,
|
+ 'static,
|
||||||
T::Future: Unpin,
|
|
||||||
Io1: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io1: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
Io2: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io2: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
{
|
{
|
||||||
@ -563,14 +502,14 @@ mod connect_impl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pin_project::pin_project]
|
||||||
pub(crate) struct InnerConnectorResponseB<T, Io1, Io2>
|
pub(crate) struct InnerConnectorResponseB<T, Io1, Io2>
|
||||||
where
|
where
|
||||||
Io2: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io2: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
T: Service<Request = Connect, Response = (Io2, Protocol), Error = ConnectError>
|
T: Service<Request = Connect, Response = (Io2, Protocol), Error = ConnectError>
|
||||||
+ Unpin
|
|
||||||
+ 'static,
|
+ 'static,
|
||||||
T::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
|
#[pin]
|
||||||
fut: <ConnectionPool<T, Io2> as Service>::Future,
|
fut: <ConnectionPool<T, Io2> as Service>::Future,
|
||||||
_t: PhantomData<Io1>,
|
_t: PhantomData<Io1>,
|
||||||
}
|
}
|
||||||
@ -578,9 +517,7 @@ mod connect_impl {
|
|||||||
impl<T, Io1, Io2> Future for InnerConnectorResponseB<T, Io1, Io2>
|
impl<T, Io1, Io2> Future for InnerConnectorResponseB<T, Io1, Io2>
|
||||||
where
|
where
|
||||||
T: Service<Request = Connect, Response = (Io2, Protocol), Error = ConnectError>
|
T: Service<Request = Connect, Response = (Io2, Protocol), Error = ConnectError>
|
||||||
+ Unpin
|
|
||||||
+ 'static,
|
+ 'static,
|
||||||
T::Future: Unpin,
|
|
||||||
Io1: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io1: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
Io2: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io2: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
{
|
{
|
||||||
|
@ -3,8 +3,8 @@ use std::io;
|
|||||||
use derive_more::{Display, From};
|
use derive_more::{Display, From};
|
||||||
use trust_dns_resolver::error::ResolveError;
|
use trust_dns_resolver::error::ResolveError;
|
||||||
|
|
||||||
#[cfg(feature = "ssl")]
|
#[cfg(feature = "openssl")]
|
||||||
use openssl::ssl::{Error as SslError, HandshakeError};
|
use open_ssl::ssl::{Error as SslError, HandshakeError};
|
||||||
|
|
||||||
use crate::error::{Error, ParseError, ResponseError};
|
use crate::error::{Error, ParseError, ResponseError};
|
||||||
use crate::http::Error as HttpError;
|
use crate::http::Error as HttpError;
|
||||||
@ -18,7 +18,7 @@ pub enum ConnectError {
|
|||||||
SslIsNotSupported,
|
SslIsNotSupported,
|
||||||
|
|
||||||
/// SSL error
|
/// SSL error
|
||||||
#[cfg(feature = "ssl")]
|
#[cfg(feature = "openssl")]
|
||||||
#[display(fmt = "{}", _0)]
|
#[display(fmt = "{}", _0)]
|
||||||
SslError(SslError),
|
SslError(SslError),
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ impl From<actix_connect::ConnectError> for ConnectError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ssl")]
|
#[cfg(feature = "openssl")]
|
||||||
impl<T> From<HandshakeError<T>> for ConnectError {
|
impl<T> From<HandshakeError<T>> for ConnectError {
|
||||||
fn from(err: HandshakeError<T>) -> ConnectError {
|
fn from(err: HandshakeError<T>) -> ConnectError {
|
||||||
match err {
|
match err {
|
||||||
|
@ -46,11 +46,9 @@ pub(crate) struct ConnectionPool<T, Io: 'static>(Rc<RefCell<T>>, Rc<RefCell<Inne
|
|||||||
|
|
||||||
impl<T, Io> ConnectionPool<T, Io>
|
impl<T, Io> ConnectionPool<T, Io>
|
||||||
where
|
where
|
||||||
Io: AsyncRead + AsyncWrite + 'static,
|
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
T: Service<Request = Connect, Response = (Io, Protocol), Error = ConnectError>
|
T: Service<Request = Connect, Response = (Io, Protocol), Error = ConnectError>
|
||||||
+ Unpin
|
|
||||||
+ 'static,
|
+ 'static,
|
||||||
T::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
connector: T,
|
connector: T,
|
||||||
@ -89,9 +87,7 @@ impl<T, Io> Service for ConnectionPool<T, Io>
|
|||||||
where
|
where
|
||||||
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
T: Service<Request = Connect, Response = (Io, Protocol), Error = ConnectError>
|
T: Service<Request = Connect, Response = (Io, Protocol), Error = ConnectError>
|
||||||
+ Unpin
|
|
||||||
+ 'static,
|
+ 'static,
|
||||||
T::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
type Request = Connect;
|
type Request = Connect;
|
||||||
type Response = IoConnection<Io>;
|
type Response = IoConnection<Io>;
|
||||||
@ -400,7 +396,7 @@ struct CloseConnection<T> {
|
|||||||
|
|
||||||
impl<T> CloseConnection<T>
|
impl<T> CloseConnection<T>
|
||||||
where
|
where
|
||||||
T: AsyncWrite,
|
T: AsyncWrite + Unpin,
|
||||||
{
|
{
|
||||||
fn new(io: T, timeout: Duration) -> Self {
|
fn new(io: T, timeout: Duration) -> Self {
|
||||||
CloseConnection {
|
CloseConnection {
|
||||||
@ -416,10 +412,12 @@ where
|
|||||||
{
|
{
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> {
|
||||||
match Pin::new(&mut self.timeout).poll(cx) {
|
let this = self.get_mut();
|
||||||
|
|
||||||
|
match Pin::new(&mut this.timeout).poll(cx) {
|
||||||
Poll::Ready(_) => Poll::Ready(()),
|
Poll::Ready(_) => Poll::Ready(()),
|
||||||
Poll::Pending => match Pin::new(&mut self.io).poll_shutdown(cx) {
|
Poll::Pending => match Pin::new(&mut this.io).poll_shutdown(cx) {
|
||||||
Poll::Ready(_) => Poll::Ready(()),
|
Poll::Ready(_) => Poll::Ready(()),
|
||||||
Poll::Pending => Poll::Pending,
|
Poll::Pending => Poll::Pending,
|
||||||
},
|
},
|
||||||
@ -429,7 +427,7 @@ where
|
|||||||
|
|
||||||
struct ConnectorPoolSupport<T, Io>
|
struct ConnectorPoolSupport<T, Io>
|
||||||
where
|
where
|
||||||
Io: AsyncRead + AsyncWrite + 'static,
|
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
{
|
{
|
||||||
connector: T,
|
connector: T,
|
||||||
inner: Rc<RefCell<Inner<Io>>>,
|
inner: Rc<RefCell<Inner<Io>>>,
|
||||||
@ -438,14 +436,13 @@ where
|
|||||||
impl<T, Io> Future for ConnectorPoolSupport<T, Io>
|
impl<T, Io> Future for ConnectorPoolSupport<T, Io>
|
||||||
where
|
where
|
||||||
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
T: Service<Request = Connect, Response = (Io, Protocol), Error = ConnectError>
|
T: Service<Request = Connect, Response = (Io, Protocol), Error = ConnectError>,
|
||||||
+ Unpin,
|
T::Future: 'static,
|
||||||
T::Future: Unpin + 'static,
|
|
||||||
{
|
{
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||||
let this = self.get_mut();
|
let this = unsafe { self.get_unchecked_mut() };
|
||||||
|
|
||||||
let mut inner = this.inner.as_ref().borrow_mut();
|
let mut inner = this.inner.as_ref().borrow_mut();
|
||||||
inner.waker.register(cx.waker());
|
inner.waker.register(cx.waker());
|
||||||
@ -512,7 +509,7 @@ where
|
|||||||
|
|
||||||
impl<F, Io> OpenWaitingConnection<F, Io>
|
impl<F, Io> OpenWaitingConnection<F, Io>
|
||||||
where
|
where
|
||||||
F: Future<Output = Result<(Io, Protocol), ConnectError>> + Unpin + 'static,
|
F: Future<Output = Result<(Io, Protocol), ConnectError>> + 'static,
|
||||||
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
{
|
{
|
||||||
fn spawn(
|
fn spawn(
|
||||||
@ -546,13 +543,13 @@ where
|
|||||||
|
|
||||||
impl<F, Io> Future for OpenWaitingConnection<F, Io>
|
impl<F, Io> Future for OpenWaitingConnection<F, Io>
|
||||||
where
|
where
|
||||||
F: Future<Output = Result<(Io, Protocol), ConnectError>> + Unpin,
|
F: Future<Output = Result<(Io, Protocol), ConnectError>>,
|
||||||
Io: AsyncRead + AsyncWrite + Unpin,
|
Io: AsyncRead + AsyncWrite + Unpin,
|
||||||
{
|
{
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||||
let this = self.get_mut();
|
let this = unsafe { self.get_unchecked_mut() };
|
||||||
|
|
||||||
if let Some(ref mut h2) = this.h2 {
|
if let Some(ref mut h2) = this.h2 {
|
||||||
return match Pin::new(h2).poll(cx) {
|
return match Pin::new(h2).poll(cx) {
|
||||||
@ -577,7 +574,7 @@ where
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
match Pin::new(&mut this.fut).poll(cx) {
|
match unsafe { Pin::new_unchecked(&mut this.fut) }.poll(cx) {
|
||||||
Poll::Ready(Err(err)) => {
|
Poll::Ready(Err(err)) => {
|
||||||
let _ = this.inner.take();
|
let _ = this.inner.take();
|
||||||
if let Some(rx) = this.rx.take() {
|
if let Some(rx) = this.rx.take() {
|
||||||
@ -596,7 +593,7 @@ where
|
|||||||
Poll::Ready(())
|
Poll::Ready(())
|
||||||
} else {
|
} else {
|
||||||
this.h2 = Some(handshake(io).boxed_local());
|
this.h2 = Some(handshake(io).boxed_local());
|
||||||
Pin::new(this).poll(cx)
|
unsafe { Pin::new_unchecked(this) }.poll(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Poll::Pending => Poll::Pending,
|
Poll::Pending => Poll::Pending,
|
||||||
|
@ -277,7 +277,7 @@ mod tests {
|
|||||||
fn test_date() {
|
fn test_date() {
|
||||||
let mut rt = System::new("test");
|
let mut rt = System::new("test");
|
||||||
|
|
||||||
let _ = rt.block_on(future::lazy(|| {
|
let _ = rt.block_on(future::lazy(|_| {
|
||||||
let settings = ServiceConfig::new(KeepAlive::Os, 0, 0);
|
let settings = ServiceConfig::new(KeepAlive::Os, 0, 0);
|
||||||
let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
|
let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
|
||||||
settings.set_date(&mut buf1);
|
settings.set_date(&mut buf1);
|
||||||
|
@ -181,13 +181,13 @@ impl ResponseError for FormError {}
|
|||||||
/// `InternalServerError` for `TimerError`
|
/// `InternalServerError` for `TimerError`
|
||||||
impl ResponseError for TimerError {}
|
impl ResponseError for TimerError {}
|
||||||
|
|
||||||
#[cfg(feature = "ssl")]
|
#[cfg(feature = "openssl")]
|
||||||
/// `InternalServerError` for `openssl::ssl::Error`
|
/// `InternalServerError` for `openssl::ssl::Error`
|
||||||
impl ResponseError for openssl::ssl::Error {}
|
impl ResponseError for open_ssl::ssl::Error {}
|
||||||
|
|
||||||
#[cfg(feature = "ssl")]
|
#[cfg(feature = "openssl")]
|
||||||
/// `InternalServerError` for `openssl::ssl::HandshakeError`
|
/// `InternalServerError` for `openssl::ssl::HandshakeError`
|
||||||
impl ResponseError for openssl::ssl::HandshakeError<tokio_tcp::TcpStream> {}
|
impl<T: std::fmt::Debug> ResponseError for open_ssl::ssl::HandshakeError<T> {}
|
||||||
|
|
||||||
/// Return `BAD_REQUEST` for `de::value::Error`
|
/// Return `BAD_REQUEST` for `de::value::Error`
|
||||||
impl ResponseError for DeError {
|
impl ResponseError for DeError {
|
||||||
@ -383,12 +383,12 @@ impl ResponseError for PayloadError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /// Return `BadRequest` for `cookie::ParseError`
|
/// Return `BadRequest` for `cookie::ParseError`
|
||||||
// impl ResponseError for crate::cookie::ParseError {
|
impl ResponseError for crate::cookie::ParseError {
|
||||||
// fn error_response(&self) -> Response {
|
fn error_response(&self) -> Response {
|
||||||
// Response::new(StatusCode::BAD_REQUEST)
|
Response::new(StatusCode::BAD_REQUEST)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
#[derive(Debug, Display, From)]
|
#[derive(Debug, Display, From)]
|
||||||
/// A set of errors that can occur during dispatching http requests
|
/// A set of errors that can occur during dispatching http requests
|
||||||
|
@ -48,14 +48,11 @@ pub struct Dispatcher<T, S, B, X, U>
|
|||||||
where
|
where
|
||||||
S: Service<Request = Request>,
|
S: Service<Request = Request>,
|
||||||
S::Error: Into<Error>,
|
S::Error: Into<Error>,
|
||||||
S::Future: Unpin,
|
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
X: Service<Request = Request, Response = Request>,
|
X: Service<Request = Request, Response = Request>,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::Future: Unpin,
|
|
||||||
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
U::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
inner: DispatcherState<T, S, B, X, U>,
|
inner: DispatcherState<T, S, B, X, U>,
|
||||||
}
|
}
|
||||||
@ -64,14 +61,11 @@ enum DispatcherState<T, S, B, X, U>
|
|||||||
where
|
where
|
||||||
S: Service<Request = Request>,
|
S: Service<Request = Request>,
|
||||||
S::Error: Into<Error>,
|
S::Error: Into<Error>,
|
||||||
S::Future: Unpin,
|
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
X: Service<Request = Request, Response = Request>,
|
X: Service<Request = Request, Response = Request>,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::Future: Unpin,
|
|
||||||
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
U::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
Normal(InnerDispatcher<T, S, B, X, U>),
|
Normal(InnerDispatcher<T, S, B, X, U>),
|
||||||
Upgrade(U::Future),
|
Upgrade(U::Future),
|
||||||
@ -82,14 +76,11 @@ struct InnerDispatcher<T, S, B, X, U>
|
|||||||
where
|
where
|
||||||
S: Service<Request = Request>,
|
S: Service<Request = Request>,
|
||||||
S::Error: Into<Error>,
|
S::Error: Into<Error>,
|
||||||
S::Future: Unpin,
|
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
X: Service<Request = Request, Response = Request>,
|
X: Service<Request = Request, Response = Request>,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::Future: Unpin,
|
|
||||||
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
U::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
service: CloneableService<S>,
|
service: CloneableService<S>,
|
||||||
expect: CloneableService<X>,
|
expect: CloneableService<X>,
|
||||||
@ -181,14 +172,11 @@ where
|
|||||||
S: Service<Request = Request>,
|
S: Service<Request = Request>,
|
||||||
S::Error: Into<Error>,
|
S::Error: Into<Error>,
|
||||||
S::Response: Into<Response<B>>,
|
S::Response: Into<Response<B>>,
|
||||||
S::Future: Unpin,
|
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
X: Service<Request = Request, Response = Request>,
|
X: Service<Request = Request, Response = Request>,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::Future: Unpin,
|
|
||||||
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
U::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
/// Create http/1 dispatcher.
|
/// Create http/1 dispatcher.
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
@ -269,14 +257,11 @@ where
|
|||||||
S: Service<Request = Request>,
|
S: Service<Request = Request>,
|
||||||
S::Error: Into<Error>,
|
S::Error: Into<Error>,
|
||||||
S::Response: Into<Response<B>>,
|
S::Response: Into<Response<B>>,
|
||||||
S::Future: Unpin,
|
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
X: Service<Request = Request, Response = Request>,
|
X: Service<Request = Request, Response = Request>,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::Future: Unpin,
|
|
||||||
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
U::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
fn can_read(&self, cx: &mut Context) -> bool {
|
fn can_read(&self, cx: &mut Context) -> bool {
|
||||||
if self
|
if self
|
||||||
@ -312,7 +297,9 @@ where
|
|||||||
let len = self.write_buf.len();
|
let len = self.write_buf.len();
|
||||||
let mut written = 0;
|
let mut written = 0;
|
||||||
while written < len {
|
while written < len {
|
||||||
match Pin::new(&mut self.io).poll_write(cx, &self.write_buf[written..]) {
|
match unsafe { Pin::new_unchecked(&mut self.io) }
|
||||||
|
.poll_write(cx, &self.write_buf[written..])
|
||||||
|
{
|
||||||
Poll::Ready(Ok(0)) => {
|
Poll::Ready(Ok(0)) => {
|
||||||
return Err(DispatchError::Io(io::Error::new(
|
return Err(DispatchError::Io(io::Error::new(
|
||||||
io::ErrorKind::WriteZero,
|
io::ErrorKind::WriteZero,
|
||||||
@ -383,32 +370,36 @@ where
|
|||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
},
|
},
|
||||||
State::ExpectCall(ref mut fut) => match Pin::new(fut).poll(cx) {
|
State::ExpectCall(ref mut fut) => {
|
||||||
Poll::Ready(Ok(req)) => {
|
match unsafe { Pin::new_unchecked(fut) }.poll(cx) {
|
||||||
self.send_continue();
|
Poll::Ready(Ok(req)) => {
|
||||||
self.state = State::ServiceCall(self.service.call(req));
|
self.send_continue();
|
||||||
continue;
|
self.state = State::ServiceCall(self.service.call(req));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Poll::Ready(Err(e)) => {
|
||||||
|
let res: Response = e.into().into();
|
||||||
|
let (res, body) = res.replace_body(());
|
||||||
|
Some(self.send_response(res, body.into_body())?)
|
||||||
|
}
|
||||||
|
Poll::Pending => None,
|
||||||
}
|
}
|
||||||
Poll::Ready(Err(e)) => {
|
}
|
||||||
let res: Response = e.into().into();
|
State::ServiceCall(ref mut fut) => {
|
||||||
let (res, body) = res.replace_body(());
|
match unsafe { Pin::new_unchecked(fut) }.poll(cx) {
|
||||||
Some(self.send_response(res, body.into_body())?)
|
Poll::Ready(Ok(res)) => {
|
||||||
|
let (res, body) = res.into().replace_body(());
|
||||||
|
self.state = self.send_response(res, body)?;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Poll::Ready(Err(e)) => {
|
||||||
|
let res: Response = e.into().into();
|
||||||
|
let (res, body) = res.replace_body(());
|
||||||
|
Some(self.send_response(res, body.into_body())?)
|
||||||
|
}
|
||||||
|
Poll::Pending => None,
|
||||||
}
|
}
|
||||||
Poll::Pending => None,
|
}
|
||||||
},
|
|
||||||
State::ServiceCall(ref mut fut) => match Pin::new(fut).poll(cx) {
|
|
||||||
Poll::Ready(Ok(res)) => {
|
|
||||||
let (res, body) = res.into().replace_body(());
|
|
||||||
self.state = self.send_response(res, body)?;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Poll::Ready(Err(e)) => {
|
|
||||||
let res: Response = e.into().into();
|
|
||||||
let (res, body) = res.replace_body(());
|
|
||||||
Some(self.send_response(res, body.into_body())?)
|
|
||||||
}
|
|
||||||
Poll::Pending => None,
|
|
||||||
},
|
|
||||||
State::SendPayload(ref mut stream) => {
|
State::SendPayload(ref mut stream) => {
|
||||||
loop {
|
loop {
|
||||||
if self.write_buf.len() < HW_BUFFER_SIZE {
|
if self.write_buf.len() < HW_BUFFER_SIZE {
|
||||||
@ -472,7 +463,7 @@ where
|
|||||||
// Handle `EXPECT: 100-Continue` header
|
// Handle `EXPECT: 100-Continue` header
|
||||||
let req = if req.head().expect() {
|
let req = if req.head().expect() {
|
||||||
let mut task = self.expect.call(req);
|
let mut task = self.expect.call(req);
|
||||||
match Pin::new(&mut task).poll(cx) {
|
match unsafe { Pin::new_unchecked(&mut task) }.poll(cx) {
|
||||||
Poll::Ready(Ok(req)) => {
|
Poll::Ready(Ok(req)) => {
|
||||||
self.send_continue();
|
self.send_continue();
|
||||||
req
|
req
|
||||||
@ -491,7 +482,7 @@ where
|
|||||||
|
|
||||||
// Call service
|
// Call service
|
||||||
let mut task = self.service.call(req);
|
let mut task = self.service.call(req);
|
||||||
match Pin::new(&mut task).poll(cx) {
|
match unsafe { Pin::new_unchecked(&mut task) }.poll(cx) {
|
||||||
Poll::Ready(Ok(res)) => {
|
Poll::Ready(Ok(res)) => {
|
||||||
let (res, body) = res.into().replace_body(());
|
let (res, body) = res.into().replace_body(());
|
||||||
self.send_response(res, body)
|
self.send_response(res, body)
|
||||||
@ -689,26 +680,37 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T, S, B, X, U> Unpin for Dispatcher<T, S, B, X, U>
|
||||||
|
where
|
||||||
|
T: IoStream,
|
||||||
|
S: Service<Request = Request>,
|
||||||
|
S::Error: Into<Error>,
|
||||||
|
S::Response: Into<Response<B>>,
|
||||||
|
B: MessageBody,
|
||||||
|
X: Service<Request = Request, Response = Request>,
|
||||||
|
X::Error: Into<Error>,
|
||||||
|
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
||||||
|
U::Error: fmt::Display,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
impl<T, S, B, X, U> Future for Dispatcher<T, S, B, X, U>
|
impl<T, S, B, X, U> Future for Dispatcher<T, S, B, X, U>
|
||||||
where
|
where
|
||||||
T: IoStream,
|
T: IoStream,
|
||||||
S: Service<Request = Request>,
|
S: Service<Request = Request>,
|
||||||
S::Error: Into<Error>,
|
S::Error: Into<Error>,
|
||||||
S::Response: Into<Response<B>>,
|
S::Response: Into<Response<B>>,
|
||||||
S::Future: Unpin,
|
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
X: Service<Request = Request, Response = Request>,
|
X: Service<Request = Request, Response = Request>,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::Future: Unpin,
|
|
||||||
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
U::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
type Output = Result<(), DispatchError>;
|
type Output = Result<(), DispatchError>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||||
match self.inner {
|
match self.as_mut().inner {
|
||||||
DispatcherState::Normal(ref mut inner) => {
|
DispatcherState::Normal(ref mut inner) => {
|
||||||
inner.poll_keepalive(cx)?;
|
inner.poll_keepalive(cx)?;
|
||||||
|
|
||||||
@ -818,7 +820,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DispatcherState::Upgrade(ref mut fut) => {
|
DispatcherState::Upgrade(ref mut fut) => {
|
||||||
Pin::new(fut).poll(cx).map_err(|e| {
|
unsafe { Pin::new_unchecked(fut) }.poll(cx).map_err(|e| {
|
||||||
error!("Upgrade handler error: {}", e);
|
error!("Upgrade handler error: {}", e);
|
||||||
DispatchError::Upgrade
|
DispatchError::Upgrade
|
||||||
})
|
})
|
||||||
@ -894,7 +896,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_req_parse_err() {
|
fn test_req_parse_err() {
|
||||||
let mut sys = actix_rt::System::new("test");
|
let mut sys = actix_rt::System::new("test");
|
||||||
let _ = sys.block_on(lazy(|| {
|
let _ = sys.block_on(lazy(|cx| {
|
||||||
let buf = TestBuffer::new("GET /test HTTP/1\r\n\r\n");
|
let buf = TestBuffer::new("GET /test HTTP/1\r\n\r\n");
|
||||||
|
|
||||||
let mut h1 = Dispatcher::<_, _, _, _, UpgradeHandler<TestBuffer>>::new(
|
let mut h1 = Dispatcher::<_, _, _, _, UpgradeHandler<TestBuffer>>::new(
|
||||||
@ -907,7 +909,10 @@ mod tests {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
assert!(h1.poll().is_err());
|
match Pin::new(&mut h1).poll(cx) {
|
||||||
|
Poll::Pending => panic!(),
|
||||||
|
Poll::Ready(res) => assert!(res.is_err()),
|
||||||
|
}
|
||||||
|
|
||||||
if let DispatcherState::Normal(ref inner) = h1.inner {
|
if let DispatcherState::Normal(ref inner) = h1.inner {
|
||||||
assert!(inner.flags.contains(Flags::READ_DISCONNECT));
|
assert!(inner.flags.contains(Flags::READ_DISCONNECT));
|
||||||
|
@ -228,26 +228,23 @@ impl Inner {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use actix_rt::Runtime;
|
use actix_rt::Runtime;
|
||||||
use futures::future::{lazy, result};
|
use futures::future::{poll_fn, ready};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unread_data() {
|
fn test_unread_data() {
|
||||||
Runtime::new()
|
Runtime::new().unwrap().block_on(async {
|
||||||
.unwrap()
|
let (_, mut payload) = Payload::create(false);
|
||||||
.block_on(async {
|
|
||||||
let (_, mut payload) = Payload::create(false);
|
|
||||||
|
|
||||||
payload.unread_data(Bytes::from("data"));
|
payload.unread_data(Bytes::from("data"));
|
||||||
assert!(!payload.is_empty());
|
assert!(!payload.is_empty());
|
||||||
assert_eq!(payload.len(), 4);
|
assert_eq!(payload.len(), 4);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Poll::Ready(Some(Bytes::from("data"))),
|
Bytes::from("data"),
|
||||||
payload.next_item().await.ok().unwrap()
|
poll_fn(|cx| payload.readany(cx)).await.unwrap().unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
result(())
|
ready(())
|
||||||
})
|
});
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,11 +39,7 @@ where
|
|||||||
S::Error: Into<Error>,
|
S::Error: Into<Error>,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>>,
|
S::Response: Into<Response<B>>,
|
||||||
S::Future: Unpin,
|
|
||||||
S::Service: Unpin,
|
|
||||||
<S::Service as Service>::Future: Unpin,
|
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
/// Create new `HttpService` instance with default config.
|
/// Create new `HttpService` instance with default config.
|
||||||
pub fn new<F: IntoServiceFactory<S>>(service: F) -> Self {
|
pub fn new<F: IntoServiceFactory<S>>(service: F) -> Self {
|
||||||
@ -81,20 +77,13 @@ where
|
|||||||
S::Error: Into<Error>,
|
S::Error: Into<Error>,
|
||||||
S::Response: Into<Response<B>>,
|
S::Response: Into<Response<B>>,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Future: Unpin,
|
|
||||||
S::Service: Unpin,
|
|
||||||
<S::Service as Service>::Future: Unpin,
|
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
pub fn expect<X1>(self, expect: X1) -> H1Service<T, P, S, B, X1, U>
|
pub fn expect<X1>(self, expect: X1) -> H1Service<T, P, S, B, X1, U>
|
||||||
where
|
where
|
||||||
X1: ServiceFactory<Request = Request, Response = Request>,
|
X1: ServiceFactory<Request = Request, Response = Request>,
|
||||||
X1::Error: Into<Error>,
|
X1::Error: Into<Error>,
|
||||||
X1::InitError: fmt::Debug,
|
X1::InitError: fmt::Debug,
|
||||||
X1::Future: Unpin,
|
|
||||||
X1::Service: Unpin,
|
|
||||||
<X1::Service as Service>::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
H1Service {
|
H1Service {
|
||||||
expect,
|
expect,
|
||||||
@ -111,9 +100,6 @@ where
|
|||||||
U1: ServiceFactory<Request = (Request, Framed<T, Codec>), Response = ()>,
|
U1: ServiceFactory<Request = (Request, Framed<T, Codec>), Response = ()>,
|
||||||
U1::Error: fmt::Display,
|
U1::Error: fmt::Display,
|
||||||
U1::InitError: fmt::Debug,
|
U1::InitError: fmt::Debug,
|
||||||
U1::Future: Unpin,
|
|
||||||
U1::Service: Unpin,
|
|
||||||
<U1::Service as Service>::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
H1Service {
|
H1Service {
|
||||||
upgrade,
|
upgrade,
|
||||||
@ -139,20 +125,13 @@ impl<T, P, S, B, X, U> ServiceFactory for H1Service<T, P, S, B, X, U>
|
|||||||
where
|
where
|
||||||
T: IoStream,
|
T: IoStream,
|
||||||
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
||||||
S::Service: Unpin,
|
|
||||||
S::Error: Into<Error>,
|
S::Error: Into<Error>,
|
||||||
S::Response: Into<Response<B>>,
|
S::Response: Into<Response<B>>,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Future: Unpin,
|
|
||||||
S::Service: Unpin,
|
|
||||||
<S::Service as Service>::Future: Unpin,
|
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
X: ServiceFactory<Config = SrvConfig, Request = Request, Response = Request>,
|
X: ServiceFactory<Config = SrvConfig, Request = Request, Response = Request>,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::InitError: fmt::Debug,
|
X::InitError: fmt::Debug,
|
||||||
X::Future: Unpin,
|
|
||||||
X::Service: Unpin,
|
|
||||||
<X::Service as Service>::Future: Unpin,
|
|
||||||
U: ServiceFactory<
|
U: ServiceFactory<
|
||||||
Config = SrvConfig,
|
Config = SrvConfig,
|
||||||
Request = (Request, Framed<T, Codec>),
|
Request = (Request, Framed<T, Codec>),
|
||||||
@ -160,10 +139,6 @@ where
|
|||||||
>,
|
>,
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
U::InitError: fmt::Debug,
|
U::InitError: fmt::Debug,
|
||||||
U::Future: Unpin,
|
|
||||||
U::Service: Unpin,
|
|
||||||
<U::Service as Service>::Future: Unpin,
|
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
type Config = SrvConfig;
|
type Config = SrvConfig;
|
||||||
type Request = Io<T, P>;
|
type Request = Io<T, P>;
|
||||||
@ -188,30 +163,24 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
#[pin_project::pin_project]
|
||||||
pub struct H1ServiceResponse<T, P, S, B, X, U>
|
pub struct H1ServiceResponse<T, P, S, B, X, U>
|
||||||
where
|
where
|
||||||
S: ServiceFactory<Request = Request>,
|
S: ServiceFactory<Request = Request>,
|
||||||
S::Error: Into<Error>,
|
S::Error: Into<Error>,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Future: Unpin,
|
|
||||||
S::Service: Unpin,
|
|
||||||
<S::Service as Service>::Future: Unpin,
|
|
||||||
X: ServiceFactory<Request = Request, Response = Request>,
|
X: ServiceFactory<Request = Request, Response = Request>,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::InitError: fmt::Debug,
|
X::InitError: fmt::Debug,
|
||||||
X::Future: Unpin,
|
|
||||||
X::Service: Unpin,
|
|
||||||
<X::Service as Service>::Future: Unpin,
|
|
||||||
U: ServiceFactory<Request = (Request, Framed<T, Codec>), Response = ()>,
|
U: ServiceFactory<Request = (Request, Framed<T, Codec>), Response = ()>,
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
U::InitError: fmt::Debug,
|
U::InitError: fmt::Debug,
|
||||||
U::Future: Unpin,
|
|
||||||
U::Service: Unpin,
|
|
||||||
<U::Service as Service>::Future: Unpin,
|
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
|
#[pin]
|
||||||
fut: S::Future,
|
fut: S::Future,
|
||||||
|
#[pin]
|
||||||
fut_ex: Option<X::Future>,
|
fut_ex: Option<X::Future>,
|
||||||
|
#[pin]
|
||||||
fut_upg: Option<U::Future>,
|
fut_upg: Option<U::Future>,
|
||||||
expect: Option<X::Service>,
|
expect: Option<X::Service>,
|
||||||
upgrade: Option<U::Service>,
|
upgrade: Option<U::Service>,
|
||||||
@ -227,51 +196,45 @@ where
|
|||||||
S::Error: Into<Error>,
|
S::Error: Into<Error>,
|
||||||
S::Response: Into<Response<B>>,
|
S::Response: Into<Response<B>>,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Future: Unpin,
|
|
||||||
S::Service: Unpin,
|
|
||||||
<S::Service as Service>::Future: Unpin,
|
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
X: ServiceFactory<Request = Request, Response = Request>,
|
X: ServiceFactory<Request = Request, Response = Request>,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::InitError: fmt::Debug,
|
X::InitError: fmt::Debug,
|
||||||
X::Future: Unpin,
|
|
||||||
X::Service: Unpin,
|
|
||||||
<X::Service as Service>::Future: Unpin,
|
|
||||||
U: ServiceFactory<Request = (Request, Framed<T, Codec>), Response = ()>,
|
U: ServiceFactory<Request = (Request, Framed<T, Codec>), Response = ()>,
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
U::InitError: fmt::Debug,
|
U::InitError: fmt::Debug,
|
||||||
U::Future: Unpin,
|
|
||||||
U::Service: Unpin,
|
|
||||||
<U::Service as Service>::Future: Unpin,
|
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
type Output =
|
type Output =
|
||||||
Result<H1ServiceHandler<T, P, S::Service, B, X::Service, U::Service>, ()>;
|
Result<H1ServiceHandler<T, P, S::Service, B, X::Service, U::Service>, ()>;
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||||
let this = self.get_mut();
|
let mut this = self.as_mut().project();
|
||||||
|
|
||||||
if let Some(ref mut fut) = this.fut_ex {
|
if let Some(fut) = this.fut_ex.as_pin_mut() {
|
||||||
let expect = ready!(Pin::new(fut)
|
let expect = ready!(fut
|
||||||
.poll(cx)
|
.poll(cx)
|
||||||
.map_err(|e| log::error!("Init http service error: {:?}", e)))?;
|
.map_err(|e| log::error!("Init http service error: {:?}", e)))?;
|
||||||
this.expect = Some(expect);
|
this = self.as_mut().project();
|
||||||
this.fut_ex.take();
|
*this.expect = Some(expect);
|
||||||
|
this.fut_ex.set(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref mut fut) = this.fut_upg {
|
if let Some(fut) = this.fut_upg.as_pin_mut() {
|
||||||
let upgrade = ready!(Pin::new(fut)
|
let upgrade = ready!(fut
|
||||||
.poll(cx)
|
.poll(cx)
|
||||||
.map_err(|e| log::error!("Init http service error: {:?}", e)))?;
|
.map_err(|e| log::error!("Init http service error: {:?}", e)))?;
|
||||||
this.upgrade = Some(upgrade);
|
this = self.as_mut().project();
|
||||||
this.fut_ex.take();
|
*this.upgrade = Some(upgrade);
|
||||||
|
this.fut_ex.set(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = ready!(Pin::new(&mut this.fut)
|
let result = ready!(this
|
||||||
|
.fut
|
||||||
.poll(cx)
|
.poll(cx)
|
||||||
.map_err(|e| log::error!("Init http service error: {:?}", e)));
|
.map_err(|e| log::error!("Init http service error: {:?}", e)));
|
||||||
|
|
||||||
Poll::Ready(result.map(|service| {
|
Poll::Ready(result.map(|service| {
|
||||||
|
let this = self.as_mut().project();
|
||||||
H1ServiceHandler::new(
|
H1ServiceHandler::new(
|
||||||
this.cfg.take().unwrap(),
|
this.cfg.take().unwrap(),
|
||||||
service,
|
service,
|
||||||
@ -295,18 +258,14 @@ pub struct H1ServiceHandler<T, P, S, B, X, U> {
|
|||||||
|
|
||||||
impl<T, P, S, B, X, U> H1ServiceHandler<T, P, S, B, X, U>
|
impl<T, P, S, B, X, U> H1ServiceHandler<T, P, S, B, X, U>
|
||||||
where
|
where
|
||||||
S: Service<Request = Request> + Unpin,
|
S: Service<Request = Request>,
|
||||||
S::Error: Into<Error>,
|
S::Error: Into<Error>,
|
||||||
S::Response: Into<Response<B>>,
|
S::Response: Into<Response<B>>,
|
||||||
S::Future: Unpin,
|
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
X: Service<Request = Request, Response = Request> + Unpin,
|
X: Service<Request = Request, Response = Request>,
|
||||||
X::Future: Unpin,
|
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
U: Service<Request = (Request, Framed<T, Codec>), Response = ()> + Unpin,
|
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
||||||
U::Future: Unpin,
|
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
fn new(
|
fn new(
|
||||||
cfg: ServiceConfig,
|
cfg: ServiceConfig,
|
||||||
@ -329,18 +288,14 @@ where
|
|||||||
impl<T, P, S, B, X, U> Service for H1ServiceHandler<T, P, S, B, X, U>
|
impl<T, P, S, B, X, U> Service for H1ServiceHandler<T, P, S, B, X, U>
|
||||||
where
|
where
|
||||||
T: IoStream,
|
T: IoStream,
|
||||||
S: Service<Request = Request> + Unpin,
|
S: Service<Request = Request>,
|
||||||
S::Error: Into<Error>,
|
S::Error: Into<Error>,
|
||||||
S::Response: Into<Response<B>>,
|
S::Response: Into<Response<B>>,
|
||||||
S::Future: Unpin,
|
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
X: Service<Request = Request, Response = Request> + Unpin,
|
X: Service<Request = Request, Response = Request>,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::Future: Unpin,
|
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
||||||
U: Service<Request = (Request, Framed<T, Codec>), Response = ()> + Unpin,
|
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
U::Future: Unpin,
|
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
type Request = Io<T, P>;
|
type Request = Io<T, P>;
|
||||||
type Response = ();
|
type Response = ();
|
||||||
|
@ -11,6 +11,7 @@ use crate::h1::{Codec, Message};
|
|||||||
use crate::response::Response;
|
use crate::response::Response;
|
||||||
|
|
||||||
/// Send http/1 response
|
/// Send http/1 response
|
||||||
|
#[pin_project::pin_project]
|
||||||
pub struct SendResponse<T, B> {
|
pub struct SendResponse<T, B> {
|
||||||
res: Option<Message<(Response<()>, BodySize)>>,
|
res: Option<Message<(Response<()>, BodySize)>>,
|
||||||
body: Option<ResponseBody<B>>,
|
body: Option<ResponseBody<B>>,
|
||||||
@ -34,7 +35,7 @@ where
|
|||||||
|
|
||||||
impl<T, B> Future for SendResponse<T, B>
|
impl<T, B> Future for SendResponse<T, B>
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin,
|
T: AsyncRead + AsyncWrite,
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
{
|
{
|
||||||
type Output = Result<Framed<T, Codec>, Error>;
|
type Output = Result<Framed<T, Codec>, Error>;
|
||||||
|
@ -35,6 +35,7 @@ use crate::response::Response;
|
|||||||
const CHUNK_SIZE: usize = 16_384;
|
const CHUNK_SIZE: usize = 16_384;
|
||||||
|
|
||||||
/// Dispatcher for HTTP/2 protocol
|
/// Dispatcher for HTTP/2 protocol
|
||||||
|
#[pin_project::pin_project]
|
||||||
pub struct Dispatcher<T: IoStream, S: Service<Request = Request>, B: MessageBody> {
|
pub struct Dispatcher<T: IoStream, S: Service<Request = Request>, B: MessageBody> {
|
||||||
service: CloneableService<S>,
|
service: CloneableService<S>,
|
||||||
connection: Connection<T, Bytes>,
|
connection: Connection<T, Bytes>,
|
||||||
@ -46,24 +47,13 @@ pub struct Dispatcher<T: IoStream, S: Service<Request = Request>, B: MessageBody
|
|||||||
_t: PhantomData<B>,
|
_t: PhantomData<B>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, S, B> Unpin for Dispatcher<T, S, B>
|
|
||||||
where
|
|
||||||
T: IoStream,
|
|
||||||
S: Service<Request = Request>,
|
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
|
||||||
S::Future: Unpin + 'static,
|
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
|
||||||
B: MessageBody + 'static,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, S, B> Dispatcher<T, S, B>
|
impl<T, S, B> Dispatcher<T, S, B>
|
||||||
where
|
where
|
||||||
T: IoStream,
|
T: IoStream,
|
||||||
S: Service<Request = Request>,
|
S: Service<Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::Future: Unpin + 'static,
|
S::Future: 'static,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
{
|
{
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
@ -107,9 +97,9 @@ impl<T, S, B> Future for Dispatcher<T, S, B>
|
|||||||
where
|
where
|
||||||
T: IoStream,
|
T: IoStream,
|
||||||
S: Service<Request = Request>,
|
S: Service<Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::Future: Unpin + 'static,
|
S::Future: 'static,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
{
|
{
|
||||||
type Output = Result<(), DispatchError>;
|
type Output = Result<(), DispatchError>;
|
||||||
@ -122,7 +112,7 @@ where
|
|||||||
match Pin::new(&mut this.connection).poll_accept(cx) {
|
match Pin::new(&mut this.connection).poll_accept(cx) {
|
||||||
Poll::Ready(None) => return Poll::Ready(Ok(())),
|
Poll::Ready(None) => return Poll::Ready(Ok(())),
|
||||||
Poll::Ready(Some(Err(err))) => return Poll::Ready(Err(err.into())),
|
Poll::Ready(Some(Err(err))) => return Poll::Ready(Err(err.into())),
|
||||||
Poll::Ready(Some(Ok((req, _)))) => {
|
Poll::Ready(Some(Ok((req, res)))) => {
|
||||||
// update keep-alive expire
|
// update keep-alive expire
|
||||||
if this.ka_timer.is_some() {
|
if this.ka_timer.is_some() {
|
||||||
if let Some(expire) = this.config.keep_alive_expire() {
|
if let Some(expire) = this.config.keep_alive_expire() {
|
||||||
@ -131,7 +121,6 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (parts, body) = req.into_parts();
|
let (parts, body) = req.into_parts();
|
||||||
// let b: () = body;
|
|
||||||
let mut req = Request::with_payload(Payload::<
|
let mut req = Request::with_payload(Payload::<
|
||||||
crate::payload::PayloadStream,
|
crate::payload::PayloadStream,
|
||||||
>::H2(
|
>::H2(
|
||||||
@ -150,20 +139,20 @@ where
|
|||||||
on_connect.set(&mut req.extensions_mut());
|
on_connect.set(&mut req.extensions_mut());
|
||||||
}
|
}
|
||||||
|
|
||||||
// tokio_executor::current_thread::spawn(ServiceResponse::<
|
tokio_executor::current_thread::spawn(ServiceResponse::<
|
||||||
// S::Future,
|
S::Future,
|
||||||
// S::Response,
|
S::Response,
|
||||||
// S::Error,
|
S::Error,
|
||||||
// B,
|
B,
|
||||||
// > {
|
> {
|
||||||
// state: ServiceResponseState::ServiceCall(
|
state: ServiceResponseState::ServiceCall(
|
||||||
// this.service.call(req),
|
this.service.call(req),
|
||||||
// Some(res),
|
Some(res),
|
||||||
// ),
|
),
|
||||||
// config: this.config.clone(),
|
config: this.config.clone(),
|
||||||
// buffer: None,
|
buffer: None,
|
||||||
// _t: PhantomData,
|
_t: PhantomData,
|
||||||
// });
|
});
|
||||||
}
|
}
|
||||||
Poll::Pending => return Poll::Pending,
|
Poll::Pending => return Poll::Pending,
|
||||||
}
|
}
|
||||||
@ -171,6 +160,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pin_project::pin_project]
|
||||||
struct ServiceResponse<F, I, E, B> {
|
struct ServiceResponse<F, I, E, B> {
|
||||||
state: ServiceResponseState<F, B>,
|
state: ServiceResponseState<F, B>,
|
||||||
config: ServiceConfig,
|
config: ServiceConfig,
|
||||||
@ -185,9 +175,9 @@ enum ServiceResponseState<F, B> {
|
|||||||
|
|
||||||
impl<F, I, E, B> ServiceResponse<F, I, E, B>
|
impl<F, I, E, B> ServiceResponse<F, I, E, B>
|
||||||
where
|
where
|
||||||
F: Future<Output = Result<I, E>> + Unpin,
|
F: Future<Output = Result<I, E>>,
|
||||||
E: Into<Error> + Unpin + 'static,
|
E: Into<Error> + 'static,
|
||||||
I: Into<Response<B>> + Unpin + 'static,
|
I: Into<Response<B>> + 'static,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
{
|
{
|
||||||
fn prepare_response(
|
fn prepare_response(
|
||||||
@ -253,25 +243,27 @@ where
|
|||||||
|
|
||||||
impl<F, I, E, B> Future for ServiceResponse<F, I, E, B>
|
impl<F, I, E, B> Future for ServiceResponse<F, I, E, B>
|
||||||
where
|
where
|
||||||
F: Future<Output = Result<I, E>> + Unpin,
|
F: Future<Output = Result<I, E>>,
|
||||||
E: Into<Error> + Unpin + 'static,
|
E: Into<Error> + 'static,
|
||||||
I: Into<Response<B>> + Unpin + 'static,
|
I: Into<Response<B>> + 'static,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
{
|
{
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||||
let this = self.get_mut();
|
let mut this = self.as_mut().project();
|
||||||
|
|
||||||
match this.state {
|
match this.state {
|
||||||
ServiceResponseState::ServiceCall(ref mut call, ref mut send) => {
|
ServiceResponseState::ServiceCall(ref mut call, ref mut send) => {
|
||||||
match Pin::new(call).poll(cx) {
|
match unsafe { Pin::new_unchecked(call) }.poll(cx) {
|
||||||
Poll::Ready(Ok(res)) => {
|
Poll::Ready(Ok(res)) => {
|
||||||
let (res, body) = res.into().replace_body(());
|
let (res, body) = res.into().replace_body(());
|
||||||
|
|
||||||
let mut send = send.take().unwrap();
|
let mut send = send.take().unwrap();
|
||||||
let mut size = body.size();
|
let mut size = body.size();
|
||||||
let h2_res = this.prepare_response(res.head(), &mut size);
|
let h2_res =
|
||||||
|
self.as_mut().prepare_response(res.head(), &mut size);
|
||||||
|
this = self.as_mut().project();
|
||||||
|
|
||||||
let stream = match send.send_response(h2_res, size.is_eof()) {
|
let stream = match send.send_response(h2_res, size.is_eof()) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -284,8 +276,9 @@ where
|
|||||||
if size.is_eof() {
|
if size.is_eof() {
|
||||||
Poll::Ready(())
|
Poll::Ready(())
|
||||||
} else {
|
} else {
|
||||||
this.state = ServiceResponseState::SendPayload(stream, body);
|
*this.state =
|
||||||
Pin::new(this).poll(cx)
|
ServiceResponseState::SendPayload(stream, body);
|
||||||
|
self.poll(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Poll::Pending => Poll::Pending,
|
Poll::Pending => Poll::Pending,
|
||||||
@ -295,7 +288,9 @@ where
|
|||||||
|
|
||||||
let mut send = send.take().unwrap();
|
let mut send = send.take().unwrap();
|
||||||
let mut size = body.size();
|
let mut size = body.size();
|
||||||
let h2_res = this.prepare_response(res.head(), &mut size);
|
let h2_res =
|
||||||
|
self.as_mut().prepare_response(res.head(), &mut size);
|
||||||
|
this = self.as_mut().project();
|
||||||
|
|
||||||
let stream = match send.send_response(h2_res, size.is_eof()) {
|
let stream = match send.send_response(h2_res, size.is_eof()) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -308,11 +303,11 @@ where
|
|||||||
if size.is_eof() {
|
if size.is_eof() {
|
||||||
Poll::Ready(())
|
Poll::Ready(())
|
||||||
} else {
|
} else {
|
||||||
this.state = ServiceResponseState::SendPayload(
|
*this.state = ServiceResponseState::SendPayload(
|
||||||
stream,
|
stream,
|
||||||
body.into_body(),
|
body.into_body(),
|
||||||
);
|
);
|
||||||
Pin::new(this).poll(cx)
|
self.poll(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -356,7 +351,7 @@ where
|
|||||||
chunk.len(),
|
chunk.len(),
|
||||||
CHUNK_SIZE,
|
CHUNK_SIZE,
|
||||||
));
|
));
|
||||||
this.buffer = Some(chunk);
|
*this.buffer = Some(chunk);
|
||||||
}
|
}
|
||||||
Poll::Ready(Some(Err(e))) => {
|
Poll::Ready(Some(Err(e))) => {
|
||||||
error!("Response payload stream error: {:?}", e);
|
error!("Response payload stream error: {:?}", e);
|
||||||
|
@ -37,12 +37,10 @@ pub struct H2Service<T, P, S, B> {
|
|||||||
impl<T, P, S, B> H2Service<T, P, S, B>
|
impl<T, P, S, B> H2Service<T, P, S, B>
|
||||||
where
|
where
|
||||||
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
S::Future: Unpin,
|
<S::Service as Service>::Future: 'static,
|
||||||
<S::Service as Service>::Future: Unpin + 'static,
|
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
/// Create new `HttpService` instance.
|
/// Create new `HttpService` instance.
|
||||||
pub fn new<F: IntoServiceFactory<S>>(service: F) -> Self {
|
pub fn new<F: IntoServiceFactory<S>>(service: F) -> Self {
|
||||||
@ -83,12 +81,10 @@ impl<T, P, S, B> ServiceFactory for H2Service<T, P, S, B>
|
|||||||
where
|
where
|
||||||
T: IoStream,
|
T: IoStream,
|
||||||
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
S::Future: Unpin,
|
<S::Service as Service>::Future: 'static,
|
||||||
<S::Service as Service>::Future: Unpin + 'static,
|
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
type Config = SrvConfig;
|
type Config = SrvConfig;
|
||||||
type Request = Io<T, P>;
|
type Request = Io<T, P>;
|
||||||
@ -109,7 +105,9 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
#[pin_project::pin_project]
|
||||||
pub struct H2ServiceResponse<T, P, S: ServiceFactory, B> {
|
pub struct H2ServiceResponse<T, P, S: ServiceFactory, B> {
|
||||||
|
#[pin]
|
||||||
fut: S::Future,
|
fut: S::Future,
|
||||||
cfg: Option<ServiceConfig>,
|
cfg: Option<ServiceConfig>,
|
||||||
on_connect: Option<rc::Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>,
|
on_connect: Option<rc::Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>,
|
||||||
@ -120,19 +118,18 @@ impl<T, P, S, B> Future for H2ServiceResponse<T, P, S, B>
|
|||||||
where
|
where
|
||||||
T: IoStream,
|
T: IoStream,
|
||||||
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
S::Future: Unpin,
|
<S::Service as Service>::Future: 'static,
|
||||||
<S::Service as Service>::Future: Unpin + 'static,
|
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
type Output = Result<H2ServiceHandler<T, P, S::Service, B>, S::InitError>;
|
type Output = Result<H2ServiceHandler<T, P, S::Service, B>, S::InitError>;
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||||
let this = self.get_mut();
|
let this = self.as_mut().project();
|
||||||
|
|
||||||
Poll::Ready(ready!(Pin::new(&mut this.fut).poll(cx)).map(|service| {
|
Poll::Ready(ready!(this.fut.poll(cx)).map(|service| {
|
||||||
|
let this = self.as_mut().project();
|
||||||
H2ServiceHandler::new(
|
H2ServiceHandler::new(
|
||||||
this.cfg.take().unwrap(),
|
this.cfg.take().unwrap(),
|
||||||
this.on_connect.clone(),
|
this.on_connect.clone(),
|
||||||
@ -153,11 +150,10 @@ pub struct H2ServiceHandler<T, P, S, B> {
|
|||||||
impl<T, P, S, B> H2ServiceHandler<T, P, S, B>
|
impl<T, P, S, B> H2ServiceHandler<T, P, S, B>
|
||||||
where
|
where
|
||||||
S: Service<Request = Request>,
|
S: Service<Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::Future: Unpin + 'static,
|
S::Future: 'static,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
fn new(
|
fn new(
|
||||||
cfg: ServiceConfig,
|
cfg: ServiceConfig,
|
||||||
@ -177,11 +173,10 @@ impl<T, P, S, B> Service for H2ServiceHandler<T, P, S, B>
|
|||||||
where
|
where
|
||||||
T: IoStream,
|
T: IoStream,
|
||||||
S: Service<Request = Request>,
|
S: Service<Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::Future: Unpin + 'static,
|
S::Future: 'static,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
type Request = Io<T, P>;
|
type Request = Io<T, P>;
|
||||||
type Response = ();
|
type Response = ();
|
||||||
@ -235,9 +230,9 @@ pub struct H2ServiceHandlerResponse<T, S, B>
|
|||||||
where
|
where
|
||||||
T: IoStream,
|
T: IoStream,
|
||||||
S: Service<Request = Request>,
|
S: Service<Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::Future: Unpin + 'static,
|
S::Future: 'static,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
{
|
{
|
||||||
state: State<T, S, B>,
|
state: State<T, S, B>,
|
||||||
@ -247,9 +242,9 @@ impl<T, S, B> Future for H2ServiceHandlerResponse<T, S, B>
|
|||||||
where
|
where
|
||||||
T: IoStream,
|
T: IoStream,
|
||||||
S: Service<Request = Request>,
|
S: Service<Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::Future: Unpin + 'static,
|
S::Future: 'static,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
{
|
{
|
||||||
type Output = Result<(), DispatchError>;
|
type Output = Result<(), DispatchError>;
|
||||||
|
@ -80,6 +80,11 @@ impl<P> Request<P> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get request's payload
|
||||||
|
pub fn payload(&mut self) -> &mut Payload<P> {
|
||||||
|
&mut self.payload
|
||||||
|
}
|
||||||
|
|
||||||
/// Get request's payload
|
/// Get request's payload
|
||||||
pub fn take_payload(&mut self) -> Payload<P> {
|
pub fn take_payload(&mut self) -> Payload<P> {
|
||||||
std::mem::replace(&mut self.payload, Payload::None)
|
std::mem::replace(&mut self.payload, Payload::None)
|
||||||
|
@ -635,8 +635,8 @@ impl ResponseBuilder {
|
|||||||
/// `ResponseBuilder` can not be used after this call.
|
/// `ResponseBuilder` can not be used after this call.
|
||||||
pub fn streaming<S, E>(&mut self, stream: S) -> Response
|
pub fn streaming<S, E>(&mut self, stream: S) -> Response
|
||||||
where
|
where
|
||||||
S: Stream<Item = Result<Bytes, E>> + Unpin + 'static,
|
S: Stream<Item = Result<Bytes, E>> + 'static,
|
||||||
E: Into<Error> + Unpin + 'static,
|
E: Into<Error> + 'static,
|
||||||
{
|
{
|
||||||
self.body(Body::from_message(BodyStream::new(stream)))
|
self.body(Body::from_message(BodyStream::new(stream)))
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ use actix_service::{IntoServiceFactory, Service, ServiceFactory};
|
|||||||
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||||
use futures::{ready, Future};
|
use futures::{ready, Future};
|
||||||
use h2::server::{self, Handshake};
|
use h2::server::{self, Handshake};
|
||||||
|
use pin_project::{pin_project, project};
|
||||||
|
|
||||||
use crate::body::MessageBody;
|
use crate::body::MessageBody;
|
||||||
use crate::builder::HttpServiceBuilder;
|
use crate::builder::HttpServiceBuilder;
|
||||||
@ -35,12 +36,10 @@ pub struct HttpService<T, P, S, B, X = h1::ExpectHandler, U = h1::UpgradeHandler
|
|||||||
impl<T, S, B> HttpService<T, (), S, B>
|
impl<T, S, B> HttpService<T, (), S, B>
|
||||||
where
|
where
|
||||||
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
S::Future: Unpin,
|
<S::Service as Service>::Future: 'static,
|
||||||
S::Service: Unpin,
|
|
||||||
<S::Service as Service>::Future: Unpin + 'static,
|
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
{
|
{
|
||||||
/// Create builder for `HttpService` instance.
|
/// Create builder for `HttpService` instance.
|
||||||
@ -52,14 +51,11 @@ where
|
|||||||
impl<T, P, S, B> HttpService<T, P, S, B>
|
impl<T, P, S, B> HttpService<T, P, S, B>
|
||||||
where
|
where
|
||||||
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
S::Future: Unpin,
|
<S::Service as Service>::Future: 'static,
|
||||||
S::Service: Unpin,
|
|
||||||
<S::Service as Service>::Future: Unpin + 'static,
|
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
/// Create new `HttpService` instance.
|
/// Create new `HttpService` instance.
|
||||||
pub fn new<F: IntoServiceFactory<S>>(service: F) -> Self {
|
pub fn new<F: IntoServiceFactory<S>>(service: F) -> Self {
|
||||||
@ -94,14 +90,11 @@ where
|
|||||||
impl<T, P, S, B, X, U> HttpService<T, P, S, B, X, U>
|
impl<T, P, S, B, X, U> HttpService<T, P, S, B, X, U>
|
||||||
where
|
where
|
||||||
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
S::Future: Unpin,
|
<S::Service as Service>::Future: 'static,
|
||||||
S::Service: Unpin,
|
|
||||||
<S::Service as Service>::Future: Unpin + 'static,
|
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
/// Provide service for `EXPECT: 100-Continue` support.
|
/// Provide service for `EXPECT: 100-Continue` support.
|
||||||
///
|
///
|
||||||
@ -113,9 +106,7 @@ where
|
|||||||
X1: ServiceFactory<Config = SrvConfig, Request = Request, Response = Request>,
|
X1: ServiceFactory<Config = SrvConfig, Request = Request, Response = Request>,
|
||||||
X1::Error: Into<Error>,
|
X1::Error: Into<Error>,
|
||||||
X1::InitError: fmt::Debug,
|
X1::InitError: fmt::Debug,
|
||||||
X1::Future: Unpin,
|
<X1::Service as Service>::Future: 'static,
|
||||||
X1::Service: Unpin,
|
|
||||||
<X1::Service as Service>::Future: Unpin + 'static,
|
|
||||||
{
|
{
|
||||||
HttpService {
|
HttpService {
|
||||||
expect,
|
expect,
|
||||||
@ -140,9 +131,7 @@ where
|
|||||||
>,
|
>,
|
||||||
U1::Error: fmt::Display,
|
U1::Error: fmt::Display,
|
||||||
U1::InitError: fmt::Debug,
|
U1::InitError: fmt::Debug,
|
||||||
U1::Future: Unpin,
|
<U1::Service as Service>::Future: 'static,
|
||||||
U1::Service: Unpin,
|
|
||||||
<U1::Service as Service>::Future: Unpin + 'static,
|
|
||||||
{
|
{
|
||||||
HttpService {
|
HttpService {
|
||||||
upgrade,
|
upgrade,
|
||||||
@ -166,22 +155,17 @@ where
|
|||||||
|
|
||||||
impl<T, P, S, B, X, U> ServiceFactory for HttpService<T, P, S, B, X, U>
|
impl<T, P, S, B, X, U> ServiceFactory for HttpService<T, P, S, B, X, U>
|
||||||
where
|
where
|
||||||
T: IoStream + Unpin,
|
T: IoStream,
|
||||||
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
S: ServiceFactory<Config = SrvConfig, Request = Request>,
|
||||||
S::Service: Unpin,
|
S::Error: Into<Error> + 'static,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
S::Future: Unpin,
|
<S::Service as Service>::Future: 'static,
|
||||||
S::Service: Unpin,
|
|
||||||
<S::Service as Service>::Future: Unpin + 'static,
|
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
X: ServiceFactory<Config = SrvConfig, Request = Request, Response = Request>,
|
X: ServiceFactory<Config = SrvConfig, Request = Request, Response = Request>,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::InitError: fmt::Debug,
|
X::InitError: fmt::Debug,
|
||||||
X::Future: Unpin,
|
<X::Service as Service>::Future: 'static,
|
||||||
X::Service: Unpin,
|
|
||||||
<X::Service as Service>::Future: Unpin + 'static,
|
|
||||||
U: ServiceFactory<
|
U: ServiceFactory<
|
||||||
Config = SrvConfig,
|
Config = SrvConfig,
|
||||||
Request = (Request, Framed<T, h1::Codec>),
|
Request = (Request, Framed<T, h1::Codec>),
|
||||||
@ -189,10 +173,7 @@ where
|
|||||||
>,
|
>,
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
U::InitError: fmt::Debug,
|
U::InitError: fmt::Debug,
|
||||||
U::Future: Unpin,
|
<U::Service as Service>::Future: 'static,
|
||||||
U::Service: Unpin,
|
|
||||||
<U::Service as Service>::Future: Unpin + 'static,
|
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
type Config = SrvConfig;
|
type Config = SrvConfig;
|
||||||
type Request = ServerIo<T, P>;
|
type Request = ServerIo<T, P>;
|
||||||
@ -217,6 +198,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
#[pin_project]
|
||||||
pub struct HttpServiceResponse<
|
pub struct HttpServiceResponse<
|
||||||
T,
|
T,
|
||||||
P,
|
P,
|
||||||
@ -225,8 +207,11 @@ pub struct HttpServiceResponse<
|
|||||||
X: ServiceFactory,
|
X: ServiceFactory,
|
||||||
U: ServiceFactory,
|
U: ServiceFactory,
|
||||||
> {
|
> {
|
||||||
|
#[pin]
|
||||||
fut: S::Future,
|
fut: S::Future,
|
||||||
|
#[pin]
|
||||||
fut_ex: Option<X::Future>,
|
fut_ex: Option<X::Future>,
|
||||||
|
#[pin]
|
||||||
fut_upg: Option<U::Future>,
|
fut_upg: Option<U::Future>,
|
||||||
expect: Option<X::Service>,
|
expect: Option<X::Service>,
|
||||||
upgrade: Option<U::Service>,
|
upgrade: Option<U::Service>,
|
||||||
@ -239,53 +224,50 @@ impl<T, P, S, B, X, U> Future for HttpServiceResponse<T, P, S, B, X, U>
|
|||||||
where
|
where
|
||||||
T: IoStream,
|
T: IoStream,
|
||||||
S: ServiceFactory<Request = Request>,
|
S: ServiceFactory<Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
S::Future: Unpin,
|
<S::Service as Service>::Future: 'static,
|
||||||
S::Service: Unpin,
|
|
||||||
<S::Service as Service>::Future: Unpin + 'static,
|
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
X: ServiceFactory<Request = Request, Response = Request>,
|
X: ServiceFactory<Request = Request, Response = Request>,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::InitError: fmt::Debug,
|
X::InitError: fmt::Debug,
|
||||||
X::Future: Unpin,
|
<X::Service as Service>::Future: 'static,
|
||||||
X::Service: Unpin,
|
|
||||||
<X::Service as Service>::Future: Unpin + 'static,
|
|
||||||
U: ServiceFactory<Request = (Request, Framed<T, h1::Codec>), Response = ()>,
|
U: ServiceFactory<Request = (Request, Framed<T, h1::Codec>), Response = ()>,
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
U::InitError: fmt::Debug,
|
U::InitError: fmt::Debug,
|
||||||
U::Future: Unpin,
|
<U::Service as Service>::Future: 'static,
|
||||||
U::Service: Unpin,
|
|
||||||
<U::Service as Service>::Future: Unpin + 'static,
|
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
type Output =
|
type Output =
|
||||||
Result<HttpServiceHandler<T, P, S::Service, B, X::Service, U::Service>, ()>;
|
Result<HttpServiceHandler<T, P, S::Service, B, X::Service, U::Service>, ()>;
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||||
let this = self.get_mut();
|
let mut this = self.as_mut().project();
|
||||||
|
|
||||||
if let Some(ref mut fut) = this.fut_ex {
|
if let Some(fut) = this.fut_ex.as_pin_mut() {
|
||||||
let expect = ready!(Pin::new(fut)
|
let expect = ready!(fut
|
||||||
.poll(cx)
|
.poll(cx)
|
||||||
.map_err(|e| log::error!("Init http service error: {:?}", e)))?;
|
.map_err(|e| log::error!("Init http service error: {:?}", e)))?;
|
||||||
this.expect = Some(expect);
|
this = self.as_mut().project();
|
||||||
this.fut_ex.take();
|
*this.expect = Some(expect);
|
||||||
|
this.fut_ex.set(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref mut fut) = this.fut_upg {
|
if let Some(fut) = this.fut_upg.as_pin_mut() {
|
||||||
let upgrade = ready!(Pin::new(fut)
|
let upgrade = ready!(fut
|
||||||
.poll(cx)
|
.poll(cx)
|
||||||
.map_err(|e| log::error!("Init http service error: {:?}", e)))?;
|
.map_err(|e| log::error!("Init http service error: {:?}", e)))?;
|
||||||
this.upgrade = Some(upgrade);
|
this = self.as_mut().project();
|
||||||
this.fut_ex.take();
|
*this.upgrade = Some(upgrade);
|
||||||
|
this.fut_ex.set(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = ready!(Pin::new(&mut this.fut)
|
let result = ready!(this
|
||||||
|
.fut
|
||||||
.poll(cx)
|
.poll(cx)
|
||||||
.map_err(|e| log::error!("Init http service error: {:?}", e)));
|
.map_err(|e| log::error!("Init http service error: {:?}", e)));
|
||||||
Poll::Ready(result.map(|service| {
|
Poll::Ready(result.map(|service| {
|
||||||
|
let this = self.as_mut().project();
|
||||||
HttpServiceHandler::new(
|
HttpServiceHandler::new(
|
||||||
this.cfg.take().unwrap(),
|
this.cfg.take().unwrap(),
|
||||||
service,
|
service,
|
||||||
@ -309,19 +291,15 @@ pub struct HttpServiceHandler<T, P, S, B, X, U> {
|
|||||||
|
|
||||||
impl<T, P, S, B, X, U> HttpServiceHandler<T, P, S, B, X, U>
|
impl<T, P, S, B, X, U> HttpServiceHandler<T, P, S, B, X, U>
|
||||||
where
|
where
|
||||||
S: Service<Request = Request> + Unpin,
|
S: Service<Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::Future: 'static,
|
S::Future: 'static,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
S::Future: Unpin,
|
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
X: Service<Request = Request, Response = Request> + Unpin,
|
X: Service<Request = Request, Response = Request>,
|
||||||
X::Future: Unpin,
|
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
U: Service<Request = (Request, Framed<T, h1::Codec>), Response = ()> + Unpin,
|
U: Service<Request = (Request, Framed<T, h1::Codec>), Response = ()>,
|
||||||
U::Future: Unpin,
|
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
fn new(
|
fn new(
|
||||||
cfg: ServiceConfig,
|
cfg: ServiceConfig,
|
||||||
@ -343,19 +321,16 @@ where
|
|||||||
|
|
||||||
impl<T, P, S, B, X, U> Service for HttpServiceHandler<T, P, S, B, X, U>
|
impl<T, P, S, B, X, U> Service for HttpServiceHandler<T, P, S, B, X, U>
|
||||||
where
|
where
|
||||||
T: IoStream + Unpin,
|
T: IoStream,
|
||||||
S: Service<Request = Request> + Unpin,
|
S: Service<Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::Future: Unpin + 'static,
|
S::Future: 'static,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
X: Service<Request = Request, Response = Request> + Unpin,
|
X: Service<Request = Request, Response = Request>,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::Future: Unpin,
|
U: Service<Request = (Request, Framed<T, h1::Codec>), Response = ()>,
|
||||||
U: Service<Request = (Request, Framed<T, h1::Codec>), Response = ()> + Unpin,
|
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
U::Future: Unpin,
|
|
||||||
P: Unpin,
|
|
||||||
{
|
{
|
||||||
type Request = ServerIo<T, P>;
|
type Request = ServerIo<T, P>;
|
||||||
type Response = ();
|
type Response = ();
|
||||||
@ -442,22 +417,21 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pin_project]
|
||||||
enum State<T, S, B, X, U>
|
enum State<T, S, B, X, U>
|
||||||
where
|
where
|
||||||
S: Service<Request = Request> + Unpin,
|
S: Service<Request = Request>,
|
||||||
S::Future: Unpin + 'static,
|
S::Future: 'static,
|
||||||
S::Error: Into<Error>,
|
S::Error: Into<Error>,
|
||||||
T: IoStream + Unpin,
|
T: IoStream,
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
X: Service<Request = Request, Response = Request> + Unpin,
|
X: Service<Request = Request, Response = Request>,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::Future: Unpin,
|
U: Service<Request = (Request, Framed<T, h1::Codec>), Response = ()>,
|
||||||
U: Service<Request = (Request, Framed<T, h1::Codec>), Response = ()> + Unpin,
|
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
U::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
H1(h1::Dispatcher<T, S, B, X, U>),
|
H1(#[pin] h1::Dispatcher<T, S, B, X, U>),
|
||||||
H2(Dispatcher<Io<T>, S, B>),
|
H2(#[pin] Dispatcher<Io<T>, S, B>),
|
||||||
Unknown(
|
Unknown(
|
||||||
Option<(
|
Option<(
|
||||||
T,
|
T,
|
||||||
@ -480,21 +454,21 @@ where
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pin_project]
|
||||||
pub struct HttpServiceHandlerResponse<T, S, B, X, U>
|
pub struct HttpServiceHandlerResponse<T, S, B, X, U>
|
||||||
where
|
where
|
||||||
T: IoStream + Unpin,
|
T: IoStream,
|
||||||
S: Service<Request = Request> + Unpin,
|
S: Service<Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::Future: Unpin + 'static,
|
S::Future: 'static,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
X: Service<Request = Request, Response = Request> + Unpin,
|
X: Service<Request = Request, Response = Request>,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::Future: Unpin,
|
U: Service<Request = (Request, Framed<T, h1::Codec>), Response = ()>,
|
||||||
U: Service<Request = (Request, Framed<T, h1::Codec>), Response = ()> + Unpin,
|
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
U::Future: Unpin,
|
|
||||||
{
|
{
|
||||||
|
#[pin]
|
||||||
state: State<T, S, B, X, U>,
|
state: State<T, S, B, X, U>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,25 +476,45 @@ const HTTP2_PREFACE: [u8; 14] = *b"PRI * HTTP/2.0";
|
|||||||
|
|
||||||
impl<T, S, B, X, U> Future for HttpServiceHandlerResponse<T, S, B, X, U>
|
impl<T, S, B, X, U> Future for HttpServiceHandlerResponse<T, S, B, X, U>
|
||||||
where
|
where
|
||||||
T: IoStream + Unpin,
|
T: IoStream,
|
||||||
S: Service<Request = Request> + Unpin,
|
S: Service<Request = Request>,
|
||||||
S::Error: Into<Error> + Unpin + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::Future: Unpin + 'static,
|
S::Future: 'static,
|
||||||
S::Response: Into<Response<B>> + Unpin + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
X: Service<Request = Request, Response = Request> + Unpin,
|
X: Service<Request = Request, Response = Request>,
|
||||||
X::Future: Unpin,
|
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
U: Service<Request = (Request, Framed<T, h1::Codec>), Response = ()> + Unpin,
|
U: Service<Request = (Request, Framed<T, h1::Codec>), Response = ()>,
|
||||||
U::Future: Unpin,
|
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
{
|
{
|
||||||
type Output = Result<(), DispatchError>;
|
type Output = Result<(), DispatchError>;
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||||
match self.state {
|
self.project().state.poll(cx)
|
||||||
State::H1(ref mut disp) => Pin::new(disp).poll(cx),
|
}
|
||||||
State::H2(ref mut disp) => Pin::new(disp).poll(cx),
|
}
|
||||||
|
|
||||||
|
impl<T, S, B, X, U> State<T, S, B, X, U>
|
||||||
|
where
|
||||||
|
T: IoStream,
|
||||||
|
S: Service<Request = Request>,
|
||||||
|
S::Error: Into<Error> + 'static,
|
||||||
|
S::Response: Into<Response<B>> + 'static,
|
||||||
|
B: MessageBody + 'static,
|
||||||
|
X: Service<Request = Request, Response = Request>,
|
||||||
|
X::Error: Into<Error>,
|
||||||
|
U: Service<Request = (Request, Framed<T, h1::Codec>), Response = ()>,
|
||||||
|
U::Error: fmt::Display,
|
||||||
|
{
|
||||||
|
#[project]
|
||||||
|
fn poll(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context,
|
||||||
|
) -> Poll<Result<(), DispatchError>> {
|
||||||
|
#[project]
|
||||||
|
match self.as_mut().project() {
|
||||||
|
State::H1(disp) => disp.poll(cx),
|
||||||
|
State::H2(disp) => disp.poll(cx),
|
||||||
State::Unknown(ref mut data) => {
|
State::Unknown(ref mut data) => {
|
||||||
if let Some(ref mut item) = data {
|
if let Some(ref mut item) = data {
|
||||||
loop {
|
loop {
|
||||||
@ -549,15 +543,15 @@ where
|
|||||||
inner: io,
|
inner: io,
|
||||||
unread: Some(buf),
|
unread: Some(buf),
|
||||||
};
|
};
|
||||||
self.state = State::Handshake(Some((
|
self.set(State::Handshake(Some((
|
||||||
server::handshake(io),
|
server::handshake(io),
|
||||||
cfg,
|
cfg,
|
||||||
srv,
|
srv,
|
||||||
peer_addr,
|
peer_addr,
|
||||||
on_connect,
|
on_connect,
|
||||||
)));
|
))));
|
||||||
} else {
|
} else {
|
||||||
self.state = State::H1(h1::Dispatcher::with_timeout(
|
self.set(State::H1(h1::Dispatcher::with_timeout(
|
||||||
io,
|
io,
|
||||||
h1::Codec::new(cfg.clone()),
|
h1::Codec::new(cfg.clone()),
|
||||||
cfg,
|
cfg,
|
||||||
@ -567,7 +561,7 @@ where
|
|||||||
expect,
|
expect,
|
||||||
upgrade,
|
upgrade,
|
||||||
on_connect,
|
on_connect,
|
||||||
))
|
)))
|
||||||
}
|
}
|
||||||
self.poll(cx)
|
self.poll(cx)
|
||||||
}
|
}
|
||||||
@ -585,9 +579,9 @@ where
|
|||||||
panic!()
|
panic!()
|
||||||
};
|
};
|
||||||
let (_, cfg, srv, peer_addr, on_connect) = data.take().unwrap();
|
let (_, cfg, srv, peer_addr, on_connect) = data.take().unwrap();
|
||||||
self.state = State::H2(Dispatcher::new(
|
self.set(State::H2(Dispatcher::new(
|
||||||
srv, conn, on_connect, cfg, None, peer_addr,
|
srv, conn, on_connect, cfg, None, peer_addr,
|
||||||
));
|
)));
|
||||||
self.poll(cx)
|
self.poll(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -595,13 +589,13 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper for `AsyncRead + AsyncWrite` types
|
/// Wrapper for `AsyncRead + AsyncWrite` types
|
||||||
|
#[pin_project::pin_project]
|
||||||
struct Io<T> {
|
struct Io<T> {
|
||||||
unread: Option<BytesMut>,
|
unread: Option<BytesMut>,
|
||||||
|
#[pin]
|
||||||
inner: T,
|
inner: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Unpin for Io<T> {}
|
|
||||||
|
|
||||||
impl<T: io::Read> io::Read for Io<T> {
|
impl<T: io::Read> io::Read for Io<T> {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
if let Some(mut bytes) = self.unread.take() {
|
if let Some(mut bytes) = self.unread.take() {
|
||||||
@ -627,7 +621,7 @@ impl<T: io::Write> io::Write for Io<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsyncRead + Unpin> AsyncRead for Io<T> {
|
impl<T: AsyncRead> AsyncRead for Io<T> {
|
||||||
// unsafe fn initializer(&self) -> io::Initializer {
|
// unsafe fn initializer(&self) -> io::Initializer {
|
||||||
// self.get_mut().inner.initializer()
|
// self.get_mut().inner.initializer()
|
||||||
// }
|
// }
|
||||||
@ -641,7 +635,19 @@ impl<T: AsyncRead + Unpin> AsyncRead for Io<T> {
|
|||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
buf: &mut [u8],
|
buf: &mut [u8],
|
||||||
) -> Poll<io::Result<usize>> {
|
) -> Poll<io::Result<usize>> {
|
||||||
Pin::new(&mut self.get_mut().inner).poll_read(cx, buf)
|
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(
|
// fn poll_read_vectored(
|
||||||
@ -653,32 +659,24 @@ impl<T: AsyncRead + Unpin> AsyncRead for Io<T> {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsyncWrite + Unpin> tokio_io::AsyncWrite for Io<T> {
|
impl<T: AsyncWrite> tokio_io::AsyncWrite for Io<T> {
|
||||||
fn poll_write(
|
fn poll_write(
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
buf: &[u8],
|
buf: &[u8],
|
||||||
) -> Poll<io::Result<usize>> {
|
) -> Poll<io::Result<usize>> {
|
||||||
Pin::new(&mut self.get_mut().inner).poll_write(cx, buf)
|
self.project().inner.poll_write(cx, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn poll_write_vectored(
|
|
||||||
// self: Pin<&mut Self>,
|
|
||||||
// cx: &mut Context<'_>,
|
|
||||||
// bufs: &[io::IoSlice<'_>],
|
|
||||||
// ) -> Poll<io::Result<usize>> {
|
|
||||||
// self.get_mut().inner.poll_write_vectored(cx, bufs)
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
Pin::new(&mut self.get_mut().inner).poll_flush(cx)
|
self.project().inner.poll_flush(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_shutdown(
|
fn poll_shutdown(
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<io::Result<()>> {
|
) -> Poll<io::Result<()>> {
|
||||||
Pin::new(&mut self.get_mut().inner).poll_shutdown(cx)
|
self.project().inner.poll_shutdown(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,17 +11,17 @@ use super::{Codec, Frame, Message};
|
|||||||
pub struct Transport<S, T>
|
pub struct Transport<S, T>
|
||||||
where
|
where
|
||||||
S: Service<Request = Frame, Response = Message> + 'static,
|
S: Service<Request = Frame, Response = Message> + 'static,
|
||||||
T: AsyncRead + AsyncWrite + Unpin,
|
T: AsyncRead + AsyncWrite,
|
||||||
{
|
{
|
||||||
inner: FramedTransport<S, T, Codec>,
|
inner: FramedTransport<S, T, Codec>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, T> Transport<S, T>
|
impl<S, T> Transport<S, T>
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin,
|
T: AsyncRead + AsyncWrite,
|
||||||
S: Service<Request = Frame, Response = Message> + Unpin,
|
S: Service<Request = Frame, Response = Message>,
|
||||||
S::Future: 'static,
|
S::Future: 'static,
|
||||||
S::Error: Unpin + 'static,
|
S::Error: 'static,
|
||||||
{
|
{
|
||||||
pub fn new<F: IntoService<S>>(io: T, service: F) -> Self {
|
pub fn new<F: IntoService<S>>(io: T, service: F) -> Self {
|
||||||
Transport {
|
Transport {
|
||||||
@ -38,10 +38,10 @@ where
|
|||||||
|
|
||||||
impl<S, T> Future for Transport<S, T>
|
impl<S, T> Future for Transport<S, T>
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin,
|
T: AsyncRead + AsyncWrite,
|
||||||
S: Service<Request = Frame, Response = Message> + Unpin,
|
S: Service<Request = Frame, Response = Message>,
|
||||||
S::Future: 'static,
|
S::Future: 'static,
|
||||||
S::Error: Unpin + 'static,
|
S::Error: 'static,
|
||||||
{
|
{
|
||||||
type Output = Result<(), FramedTransportError<S::Error, Codec>>;
|
type Output = Result<(), FramedTransportError<S::Error, Codec>>;
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use actix_service::NewService;
|
use actix_service::ServiceFactory;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::future::{self, ok};
|
use futures::future::{self, ok};
|
||||||
|
|
||||||
use actix_http::{http, HttpService, Request, Response};
|
use actix_http::{http, HttpService, Request, Response};
|
||||||
use actix_http_test::TestServer;
|
use actix_http_test::{block_on, TestServer};
|
||||||
|
|
||||||
const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
@ -29,55 +29,63 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_h1_v2() {
|
fn test_h1_v2() {
|
||||||
env_logger::init();
|
block_on(async {
|
||||||
let mut srv = TestServer::new(move || {
|
let srv = TestServer::start(move || {
|
||||||
HttpService::build().finish(|_| future::ok::<_, ()>(Response::Ok().body(STR)))
|
HttpService::build()
|
||||||
});
|
.finish(|_| future::ok::<_, ()>(Response::Ok().body(STR)))
|
||||||
let response = srv.block_on(srv.get("/").send()).unwrap();
|
});
|
||||||
assert!(response.status().is_success());
|
|
||||||
|
|
||||||
let request = srv.get("/").header("x-test", "111").send();
|
let response = srv.get("/").send().await.unwrap();
|
||||||
let response = srv.block_on(request).unwrap();
|
assert!(response.status().is_success());
|
||||||
assert!(response.status().is_success());
|
|
||||||
|
|
||||||
// read response
|
let request = srv.get("/").header("x-test", "111").send();
|
||||||
let bytes = srv.load_body(response).unwrap();
|
let mut response = request.await.unwrap();
|
||||||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
let response = srv.block_on(srv.post("/").send()).unwrap();
|
// read response
|
||||||
assert!(response.status().is_success());
|
let bytes = response.body().await.unwrap();
|
||||||
|
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||||
|
|
||||||
// read response
|
let mut response = srv.post("/").send().await.unwrap();
|
||||||
let bytes = srv.load_body(response).unwrap();
|
assert!(response.status().is_success());
|
||||||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
|
||||||
|
// read response
|
||||||
|
let bytes = response.body().await.unwrap();
|
||||||
|
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_connection_close() {
|
fn test_connection_close() {
|
||||||
let mut srv = TestServer::new(move || {
|
block_on(async {
|
||||||
HttpService::build()
|
let srv = TestServer::start(move || {
|
||||||
.finish(|_| ok::<_, ()>(Response::Ok().body(STR)))
|
HttpService::build()
|
||||||
.map(|_| ())
|
.finish(|_| ok::<_, ()>(Response::Ok().body(STR)))
|
||||||
});
|
.map(|_| ())
|
||||||
let response = srv.block_on(srv.get("/").force_close().send()).unwrap();
|
});
|
||||||
assert!(response.status().is_success());
|
|
||||||
|
let response = srv.get("/").force_close().send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_with_query_parameter() {
|
fn test_with_query_parameter() {
|
||||||
let mut srv = TestServer::new(move || {
|
block_on(async {
|
||||||
HttpService::build()
|
let srv = TestServer::start(move || {
|
||||||
.finish(|req: Request| {
|
HttpService::build()
|
||||||
if req.uri().query().unwrap().contains("qp=") {
|
.finish(|req: Request| {
|
||||||
ok::<_, ()>(Response::Ok().finish())
|
if req.uri().query().unwrap().contains("qp=") {
|
||||||
} else {
|
ok::<_, ()>(Response::Ok().finish())
|
||||||
ok::<_, ()>(Response::BadRequest().finish())
|
} else {
|
||||||
}
|
ok::<_, ()>(Response::BadRequest().finish())
|
||||||
})
|
}
|
||||||
.map(|_| ())
|
})
|
||||||
});
|
.map(|_| ())
|
||||||
|
});
|
||||||
|
|
||||||
let request = srv.request(http::Method::GET, srv.url("/?qp=5")).send();
|
let request = srv.request(http::Method::GET, srv.url("/?qp=5"));
|
||||||
let response = srv.block_on(request).unwrap();
|
let response = request.send().await.unwrap();
|
||||||
assert!(response.status().is_success());
|
assert!(response.status().is_success());
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
545
actix-http/tests/test_openssl.rs
Normal file
545
actix-http/tests/test_openssl.rs
Normal file
@ -0,0 +1,545 @@
|
|||||||
|
#![cfg(feature = "openssl")]
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
use actix_codec::{AsyncRead, AsyncWrite};
|
||||||
|
use actix_http_test::{block_on, TestServer};
|
||||||
|
use actix_server::ssl::OpensslAcceptor;
|
||||||
|
use actix_server_config::ServerConfig;
|
||||||
|
use actix_service::{factory_fn_cfg, pipeline_factory, service_fn2, ServiceFactory};
|
||||||
|
|
||||||
|
use bytes::{Bytes, BytesMut};
|
||||||
|
use futures::future::{err, ok, ready};
|
||||||
|
use futures::stream::{once, Stream, StreamExt};
|
||||||
|
use open_ssl::ssl::{AlpnError, SslAcceptor, SslFiletype, SslMethod};
|
||||||
|
|
||||||
|
use actix_http::error::{ErrorBadRequest, PayloadError};
|
||||||
|
use actix_http::http::header::{self, HeaderName, HeaderValue};
|
||||||
|
use actix_http::http::{Method, StatusCode, Version};
|
||||||
|
use actix_http::httpmessage::HttpMessage;
|
||||||
|
use actix_http::{body, Error, HttpService, Request, Response};
|
||||||
|
|
||||||
|
async fn load_body<S>(stream: S) -> Result<BytesMut, PayloadError>
|
||||||
|
where
|
||||||
|
S: Stream<Item = Result<Bytes, PayloadError>>,
|
||||||
|
{
|
||||||
|
let body = stream
|
||||||
|
.map(|res| match res {
|
||||||
|
Ok(chunk) => chunk,
|
||||||
|
Err(_) => panic!(),
|
||||||
|
})
|
||||||
|
.fold(BytesMut::new(), move |mut body, chunk| {
|
||||||
|
body.extend_from_slice(&chunk);
|
||||||
|
ready(body)
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ssl_acceptor<T: AsyncRead + AsyncWrite>() -> io::Result<OpensslAcceptor<T, ()>> {
|
||||||
|
// load ssl keys
|
||||||
|
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
|
||||||
|
builder
|
||||||
|
.set_private_key_file("../tests/key.pem", SslFiletype::PEM)
|
||||||
|
.unwrap();
|
||||||
|
builder
|
||||||
|
.set_certificate_chain_file("../tests/cert.pem")
|
||||||
|
.unwrap();
|
||||||
|
builder.set_alpn_select_callback(|_, protos| {
|
||||||
|
const H2: &[u8] = b"\x02h2";
|
||||||
|
if protos.windows(3).any(|window| window == H2) {
|
||||||
|
Ok(b"h2")
|
||||||
|
} else {
|
||||||
|
Err(AlpnError::NOACK)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.set_alpn_protos(b"\x02h2")?;
|
||||||
|
Ok(OpensslAcceptor::new(builder.build()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2() -> io::Result<()> {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_1() -> io::Result<()> {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_body() -> io::Result<()> {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send_body(data.clone()).await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
let body = srv.load_body(response).await.unwrap();
|
||||||
|
assert_eq!(&body, data.as_bytes());
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_content_length() {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let header = HeaderName::from_static("content-length");
|
||||||
|
let value = HeaderValue::from_static("0");
|
||||||
|
|
||||||
|
{
|
||||||
|
for i in 0..4 {
|
||||||
|
let req = srv
|
||||||
|
.request(Method::GET, srv.surl(&format!("/{}", i)))
|
||||||
|
.send();
|
||||||
|
let response = req.await.unwrap();
|
||||||
|
assert_eq!(response.headers().get(&header), None);
|
||||||
|
|
||||||
|
let req = srv
|
||||||
|
.request(Method::HEAD, srv.surl(&format!("/{}", i)))
|
||||||
|
.send();
|
||||||
|
let response = req.await.unwrap();
|
||||||
|
assert_eq!(response.headers().get(&header), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 4..6 {
|
||||||
|
let req = srv
|
||||||
|
.request(Method::GET, srv.surl(&format!("/{}", i)))
|
||||||
|
.send();
|
||||||
|
let response = req.await.unwrap();
|
||||||
|
assert_eq!(response.headers().get(&header), Some(&value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_headers() {
|
||||||
|
block_on(async {
|
||||||
|
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 {
|
||||||
|
builder.header(
|
||||||
|
format!("X-TEST-{}", idx).as_str(),
|
||||||
|
"TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ok::<_, ()>(builder.body(data.clone()))
|
||||||
|
}).map_err(|_| ()))
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.load_body(response).await.unwrap();
|
||||||
|
assert_eq!(bytes, Bytes::from(data2));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_body2() {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.load_body(response).await.unwrap();
|
||||||
|
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_head_empty() {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.shead("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
assert_eq!(response.version(), Version::HTTP_2);
|
||||||
|
|
||||||
|
{
|
||||||
|
let len = response.headers().get(header::CONTENT_LENGTH).unwrap();
|
||||||
|
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.load_body(response).await.unwrap();
|
||||||
|
assert!(bytes.is_empty());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_head_binary() {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.shead("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
{
|
||||||
|
let len = response.headers().get(header::CONTENT_LENGTH).unwrap();
|
||||||
|
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.load_body(response).await.unwrap();
|
||||||
|
assert!(bytes.is_empty());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_head_binary2() {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.shead("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
{
|
||||||
|
let len = response.headers().get(header::CONTENT_LENGTH).unwrap();
|
||||||
|
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_body_length() {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.load_body(response).await.unwrap();
|
||||||
|
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_body_chunked_explicit() {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
assert!(!response.headers().contains_key(header::TRANSFER_ENCODING));
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.load_body(response).await.unwrap();
|
||||||
|
|
||||||
|
// decode
|
||||||
|
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_response_http_error_handling() {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.load_body(response).await.unwrap();
|
||||||
|
assert_eq!(bytes, Bytes::from_static(b"failed to parse header value"));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_service_error() {
|
||||||
|
block_on(async {
|
||||||
|
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::<Response, Error>(ErrorBadRequest("error")))
|
||||||
|
.map_err(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.load_body(response).await.unwrap();
|
||||||
|
assert_eq!(bytes, Bytes::from_static(b"error"));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_on_connect() {
|
||||||
|
block_on(async {
|
||||||
|
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::<usize>());
|
||||||
|
ok::<_, ()>(Response::Ok().finish())
|
||||||
|
})
|
||||||
|
.map_err(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
})
|
||||||
|
}
|
474
actix-http/tests/test_rustls.rs
Normal file
474
actix-http/tests/test_rustls.rs
Normal file
@ -0,0 +1,474 @@
|
|||||||
|
#![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::{block_on, TestServer};
|
||||||
|
use actix_server::ssl::RustlsAcceptor;
|
||||||
|
use actix_server_config::ServerConfig;
|
||||||
|
use actix_service::{factory_fn_cfg, pipeline_factory, service_fn2, ServiceFactory};
|
||||||
|
|
||||||
|
use bytes::{Bytes, BytesMut};
|
||||||
|
use futures::future::{self, err, ok};
|
||||||
|
use futures::stream::{once, Stream, StreamExt};
|
||||||
|
use rust_tls::{
|
||||||
|
internal::pemfile::{certs, pkcs8_private_keys},
|
||||||
|
NoClientAuth, ServerConfig as RustlsServerConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{self, BufReader};
|
||||||
|
|
||||||
|
async fn load_body<S>(mut stream: S) -> Result<BytesMut, PayloadError>
|
||||||
|
where
|
||||||
|
S: Stream<Item = Result<Bytes, PayloadError>> + Unpin,
|
||||||
|
{
|
||||||
|
let mut body = BytesMut::new();
|
||||||
|
while let Some(item) = stream.next().await {
|
||||||
|
body.extend_from_slice(&item?)
|
||||||
|
}
|
||||||
|
Ok(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ssl_acceptor<T: AsyncRead + AsyncWrite>() -> io::Result<RustlsAcceptor<T, ()>> {
|
||||||
|
// load ssl keys
|
||||||
|
let mut config = RustlsServerConfig::new(NoClientAuth::new());
|
||||||
|
let cert_file = &mut BufReader::new(File::open("../tests/cert.pem").unwrap());
|
||||||
|
let key_file = &mut BufReader::new(File::open("../tests/key.pem").unwrap());
|
||||||
|
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();
|
||||||
|
|
||||||
|
let protos = vec![b"h2".to_vec()];
|
||||||
|
config.set_protocols(&protos);
|
||||||
|
Ok(RustlsAcceptor::new(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2() -> io::Result<()> {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_1() -> io::Result<()> {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_body1() -> io::Result<()> {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send_body(data.clone()).await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
let body = srv.load_body(response).await.unwrap();
|
||||||
|
assert_eq!(&body, data.as_bytes());
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_content_length() {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let header = HeaderName::from_static("content-length");
|
||||||
|
let value = HeaderValue::from_static("0");
|
||||||
|
|
||||||
|
{
|
||||||
|
for i in 0..4 {
|
||||||
|
let req = srv
|
||||||
|
.request(Method::GET, srv.surl(&format!("/{}", i)))
|
||||||
|
.send();
|
||||||
|
let response = req.await.unwrap();
|
||||||
|
assert_eq!(response.headers().get(&header), None);
|
||||||
|
|
||||||
|
let req = srv
|
||||||
|
.request(Method::HEAD, srv.surl(&format!("/{}", i)))
|
||||||
|
.send();
|
||||||
|
let response = req.await.unwrap();
|
||||||
|
assert_eq!(response.headers().get(&header), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 4..6 {
|
||||||
|
let req = srv
|
||||||
|
.request(Method::GET, srv.surl(&format!("/{}", i)))
|
||||||
|
.send();
|
||||||
|
let response = req.await.unwrap();
|
||||||
|
assert_eq!(response.headers().get(&header), Some(&value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_headers() {
|
||||||
|
block_on(async {
|
||||||
|
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 {
|
||||||
|
config.header(
|
||||||
|
format!("X-TEST-{}", idx).as_str(),
|
||||||
|
"TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||||
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
future::ok::<_, ()>(config.body(data.clone()))
|
||||||
|
}).map_err(|_| ()))
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.load_body(response).await.unwrap();
|
||||||
|
assert_eq!(bytes, Bytes::from(data2));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
|
Hello World Hello World Hello World Hello World Hello World";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_body2() {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.load_body(response).await.unwrap();
|
||||||
|
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_head_empty() {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.shead("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
assert_eq!(response.version(), Version::HTTP_2);
|
||||||
|
|
||||||
|
{
|
||||||
|
let len = response
|
||||||
|
.headers()
|
||||||
|
.get(http::header::CONTENT_LENGTH)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.load_body(response).await.unwrap();
|
||||||
|
assert!(bytes.is_empty());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_head_binary() {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.shead("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
{
|
||||||
|
let len = response
|
||||||
|
.headers()
|
||||||
|
.get(http::header::CONTENT_LENGTH)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.load_body(response).await.unwrap();
|
||||||
|
assert!(bytes.is_empty());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_head_binary2() {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.shead("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
{
|
||||||
|
let len = response
|
||||||
|
.headers()
|
||||||
|
.get(http::header::CONTENT_LENGTH)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_body_length() {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.load_body(response).await.unwrap();
|
||||||
|
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_body_chunked_explicit() {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
assert!(!response.headers().contains_key(header::TRANSFER_ENCODING));
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.load_body(response).await.unwrap();
|
||||||
|
|
||||||
|
// decode
|
||||||
|
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_response_http_error_handling() {
|
||||||
|
block_on(async {
|
||||||
|
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(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.load_body(response).await.unwrap();
|
||||||
|
assert_eq!(bytes, Bytes::from_static(b"failed to parse header value"));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h2_service_error() {
|
||||||
|
block_on(async {
|
||||||
|
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::<Response, Error>(error::ErrorBadRequest("error")))
|
||||||
|
.map_err(|_| ()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert_eq!(response.status(), http::StatusCode::BAD_REQUEST);
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.load_body(response).await.unwrap();
|
||||||
|
assert_eq!(bytes, Bytes::from_static(b"error"));
|
||||||
|
})
|
||||||
|
}
|
@ -1,462 +0,0 @@
|
|||||||
#![cfg(feature = "rust-tls")]
|
|
||||||
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::{new_service_cfg, NewService};
|
|
||||||
|
|
||||||
use bytes::{Bytes, BytesMut};
|
|
||||||
use futures::future::{self, ok, Future};
|
|
||||||
use futures::stream::{once, Stream};
|
|
||||||
use rustls::{
|
|
||||||
internal::pemfile::{certs, pkcs8_private_keys},
|
|
||||||
NoClientAuth, ServerConfig as RustlsServerConfig,
|
|
||||||
};
|
|
||||||
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{BufReader, Result};
|
|
||||||
|
|
||||||
fn load_body<S>(stream: S) -> impl Future<Item = BytesMut, Error = PayloadError>
|
|
||||||
where
|
|
||||||
S: Stream<Item = Bytes, Error = PayloadError>,
|
|
||||||
{
|
|
||||||
stream.fold(BytesMut::new(), move |mut body, chunk| {
|
|
||||||
body.extend_from_slice(&chunk);
|
|
||||||
Ok::<_, PayloadError>(body)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ssl_acceptor<T: AsyncRead + AsyncWrite>() -> Result<RustlsAcceptor<T, ()>> {
|
|
||||||
// load ssl keys
|
|
||||||
let mut config = RustlsServerConfig::new(NoClientAuth::new());
|
|
||||||
let cert_file = &mut BufReader::new(File::open("../tests/cert.pem").unwrap());
|
|
||||||
let key_file = &mut BufReader::new(File::open("../tests/key.pem").unwrap());
|
|
||||||
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();
|
|
||||||
|
|
||||||
let protos = vec![b"h2".to_vec()];
|
|
||||||
config.set_protocols(&protos);
|
|
||||||
Ok(RustlsAcceptor::new(config))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2() -> Result<()> {
|
|
||||||
let rustls = ssl_acceptor()?;
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
rustls
|
|
||||||
.clone()
|
|
||||||
.map_err(|e| println!("Rustls error: {}", e))
|
|
||||||
.and_then(
|
|
||||||
HttpService::build()
|
|
||||||
.h2(|_| future::ok::<_, Error>(Response::Ok().finish()))
|
|
||||||
.map_err(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_1() -> Result<()> {
|
|
||||||
let rustls = ssl_acceptor()?;
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
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(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_body() -> Result<()> {
|
|
||||||
let data = "HELLOWORLD".to_owned().repeat(64 * 1024);
|
|
||||||
let rustls = ssl_acceptor()?;
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
rustls
|
|
||||||
.clone()
|
|
||||||
.map_err(|e| println!("Rustls error: {}", e))
|
|
||||||
.and_then(
|
|
||||||
HttpService::build()
|
|
||||||
.h2(|mut req: Request<_>| {
|
|
||||||
load_body(req.take_payload())
|
|
||||||
.and_then(|body| Ok(Response::Ok().body(body)))
|
|
||||||
})
|
|
||||||
.map_err(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send_body(data.clone())).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
|
|
||||||
let body = srv.load_body(response).unwrap();
|
|
||||||
assert_eq!(&body, data.as_bytes());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_content_length() {
|
|
||||||
let rustls = ssl_acceptor().unwrap();
|
|
||||||
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
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(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let header = HeaderName::from_static("content-length");
|
|
||||||
let value = HeaderValue::from_static("0");
|
|
||||||
|
|
||||||
{
|
|
||||||
for i in 0..4 {
|
|
||||||
let req = srv
|
|
||||||
.request(Method::GET, srv.surl(&format!("/{}", i)))
|
|
||||||
.send();
|
|
||||||
let response = srv.block_on(req).unwrap();
|
|
||||||
assert_eq!(response.headers().get(&header), None);
|
|
||||||
|
|
||||||
let req = srv
|
|
||||||
.request(Method::HEAD, srv.surl(&format!("/{}", i)))
|
|
||||||
.send();
|
|
||||||
let response = srv.block_on(req).unwrap();
|
|
||||||
assert_eq!(response.headers().get(&header), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in 4..6 {
|
|
||||||
let req = srv
|
|
||||||
.request(Method::GET, srv.surl(&format!("/{}", i)))
|
|
||||||
.send();
|
|
||||||
let response = srv.block_on(req).unwrap();
|
|
||||||
assert_eq!(response.headers().get(&header), Some(&value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_headers() {
|
|
||||||
let data = STR.repeat(10);
|
|
||||||
let data2 = data.clone();
|
|
||||||
let rustls = ssl_acceptor().unwrap();
|
|
||||||
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
let data = data.clone();
|
|
||||||
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 {
|
|
||||||
config.header(
|
|
||||||
format!("X-TEST-{}", idx).as_str(),
|
|
||||||
"TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
future::ok::<_, ()>(config.body(data.clone()))
|
|
||||||
}).map_err(|_| ()))
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
|
|
||||||
// read response
|
|
||||||
let bytes = srv.load_body(response).unwrap();
|
|
||||||
assert_eq!(bytes, Bytes::from(data2));
|
|
||||||
}
|
|
||||||
|
|
||||||
const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_body2() {
|
|
||||||
let rustls = ssl_acceptor().unwrap();
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
rustls
|
|
||||||
.clone()
|
|
||||||
.map_err(|e| println!("Rustls error: {}", e))
|
|
||||||
.and_then(
|
|
||||||
HttpService::build()
|
|
||||||
.h2(|_| future::ok::<_, ()>(Response::Ok().body(STR)))
|
|
||||||
.map_err(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
|
|
||||||
// read response
|
|
||||||
let bytes = srv.load_body(response).unwrap();
|
|
||||||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_head_empty() {
|
|
||||||
let rustls = ssl_acceptor().unwrap();
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
rustls
|
|
||||||
.clone()
|
|
||||||
.map_err(|e| println!("Rustls error: {}", e))
|
|
||||||
.and_then(
|
|
||||||
HttpService::build()
|
|
||||||
.finish(|_| ok::<_, ()>(Response::Ok().body(STR)))
|
|
||||||
.map_err(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.shead("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
assert_eq!(response.version(), Version::HTTP_2);
|
|
||||||
|
|
||||||
{
|
|
||||||
let len = response
|
|
||||||
.headers()
|
|
||||||
.get(http::header::CONTENT_LENGTH)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
// read response
|
|
||||||
let bytes = srv.load_body(response).unwrap();
|
|
||||||
assert!(bytes.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_head_binary() {
|
|
||||||
let rustls = ssl_acceptor().unwrap();
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
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(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.shead("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
|
|
||||||
{
|
|
||||||
let len = response
|
|
||||||
.headers()
|
|
||||||
.get(http::header::CONTENT_LENGTH)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
// read response
|
|
||||||
let bytes = srv.load_body(response).unwrap();
|
|
||||||
assert!(bytes.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_head_binary2() {
|
|
||||||
let rustls = ssl_acceptor().unwrap();
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
rustls
|
|
||||||
.clone()
|
|
||||||
.map_err(|e| println!("Rustls error: {}", e))
|
|
||||||
.and_then(
|
|
||||||
HttpService::build()
|
|
||||||
.h2(|_| ok::<_, ()>(Response::Ok().body(STR)))
|
|
||||||
.map_err(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.shead("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
|
|
||||||
{
|
|
||||||
let len = response
|
|
||||||
.headers()
|
|
||||||
.get(http::header::CONTENT_LENGTH)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_body_length() {
|
|
||||||
let rustls = ssl_acceptor().unwrap();
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
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(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
|
|
||||||
// read response
|
|
||||||
let bytes = srv.load_body(response).unwrap();
|
|
||||||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_body_chunked_explicit() {
|
|
||||||
let rustls = ssl_acceptor().unwrap();
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
rustls
|
|
||||||
.clone()
|
|
||||||
.map_err(|e| println!("Rustls error: {}", e))
|
|
||||||
.and_then(
|
|
||||||
HttpService::build()
|
|
||||||
.h2(|_| {
|
|
||||||
let body =
|
|
||||||
once::<_, Error>(Ok(Bytes::from_static(STR.as_ref())));
|
|
||||||
ok::<_, ()>(
|
|
||||||
Response::Ok()
|
|
||||||
.header(header::TRANSFER_ENCODING, "chunked")
|
|
||||||
.streaming(body),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.map_err(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
assert!(!response.headers().contains_key(header::TRANSFER_ENCODING));
|
|
||||||
|
|
||||||
// read response
|
|
||||||
let bytes = srv.load_body(response).unwrap();
|
|
||||||
|
|
||||||
// decode
|
|
||||||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_response_http_error_handling() {
|
|
||||||
let rustls = ssl_acceptor().unwrap();
|
|
||||||
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
rustls
|
|
||||||
.clone()
|
|
||||||
.map_err(|e| println!("Rustls error: {}", e))
|
|
||||||
.and_then(
|
|
||||||
HttpService::build()
|
|
||||||
.h2(new_service_cfg(|_: &ServerConfig| {
|
|
||||||
Ok::<_, ()>(|_| {
|
|
||||||
let broken_header = Bytes::from_static(b"\0\0\0");
|
|
||||||
ok::<_, ()>(
|
|
||||||
Response::Ok()
|
|
||||||
.header(http::header::CONTENT_TYPE, broken_header)
|
|
||||||
.body(STR),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
.map_err(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
|
||||||
assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR);
|
|
||||||
|
|
||||||
// read response
|
|
||||||
let bytes = srv.load_body(response).unwrap();
|
|
||||||
assert_eq!(bytes, Bytes::from_static(b"failed to parse header value"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_service_error() {
|
|
||||||
let rustls = ssl_acceptor().unwrap();
|
|
||||||
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
rustls
|
|
||||||
.clone()
|
|
||||||
.map_err(|e| println!("Rustls error: {}", e))
|
|
||||||
.and_then(
|
|
||||||
HttpService::build()
|
|
||||||
.h2(|_| Err::<Response, Error>(error::ErrorBadRequest("error")))
|
|
||||||
.map_err(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
|
||||||
assert_eq!(response.status(), http::StatusCode::BAD_REQUEST);
|
|
||||||
|
|
||||||
// read response
|
|
||||||
let bytes = srv.load_body(response).unwrap();
|
|
||||||
assert_eq!(bytes, Bytes::from_static(b"error"));
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,480 +0,0 @@
|
|||||||
#![cfg(feature = "ssl")]
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite};
|
|
||||||
use actix_http_test::TestServer;
|
|
||||||
use actix_server::ssl::OpensslAcceptor;
|
|
||||||
use actix_server_config::ServerConfig;
|
|
||||||
use actix_service::{new_service_cfg, NewService};
|
|
||||||
|
|
||||||
use bytes::{Bytes, BytesMut};
|
|
||||||
use futures::future::{ok, Future};
|
|
||||||
use futures::stream::{once, Stream};
|
|
||||||
use openssl::ssl::{AlpnError, SslAcceptor, SslFiletype, SslMethod};
|
|
||||||
use std::io::Result;
|
|
||||||
|
|
||||||
use actix_http::error::{ErrorBadRequest, PayloadError};
|
|
||||||
use actix_http::http::header::{self, HeaderName, HeaderValue};
|
|
||||||
use actix_http::http::{Method, StatusCode, Version};
|
|
||||||
use actix_http::httpmessage::HttpMessage;
|
|
||||||
use actix_http::{body, Error, HttpService, Request, Response};
|
|
||||||
|
|
||||||
fn load_body<S>(stream: S) -> impl Future<Item = BytesMut, Error = PayloadError>
|
|
||||||
where
|
|
||||||
S: Stream<Item = Bytes, Error = PayloadError>,
|
|
||||||
{
|
|
||||||
stream.fold(BytesMut::new(), move |mut body, chunk| {
|
|
||||||
body.extend_from_slice(&chunk);
|
|
||||||
Ok::<_, PayloadError>(body)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ssl_acceptor<T: AsyncRead + AsyncWrite>() -> Result<OpensslAcceptor<T, ()>> {
|
|
||||||
// load ssl keys
|
|
||||||
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
|
|
||||||
builder
|
|
||||||
.set_private_key_file("../tests/key.pem", SslFiletype::PEM)
|
|
||||||
.unwrap();
|
|
||||||
builder
|
|
||||||
.set_certificate_chain_file("../tests/cert.pem")
|
|
||||||
.unwrap();
|
|
||||||
builder.set_alpn_select_callback(|_, protos| {
|
|
||||||
const H2: &[u8] = b"\x02h2";
|
|
||||||
if protos.windows(3).any(|window| window == H2) {
|
|
||||||
Ok(b"h2")
|
|
||||||
} else {
|
|
||||||
Err(AlpnError::NOACK)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.set_alpn_protos(b"\x02h2")?;
|
|
||||||
Ok(OpensslAcceptor::new(builder.build()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2() -> Result<()> {
|
|
||||||
let openssl = ssl_acceptor()?;
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
openssl
|
|
||||||
.clone()
|
|
||||||
.map_err(|e| println!("Openssl error: {}", e))
|
|
||||||
.and_then(
|
|
||||||
HttpService::build()
|
|
||||||
.h2(|_| ok::<_, Error>(Response::Ok().finish()))
|
|
||||||
.map_err(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_1() -> Result<()> {
|
|
||||||
let openssl = ssl_acceptor()?;
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
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(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_body() -> Result<()> {
|
|
||||||
let data = "HELLOWORLD".to_owned().repeat(64 * 1024);
|
|
||||||
let openssl = ssl_acceptor()?;
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
openssl
|
|
||||||
.clone()
|
|
||||||
.map_err(|e| println!("Openssl error: {}", e))
|
|
||||||
.and_then(
|
|
||||||
HttpService::build()
|
|
||||||
.h2(|mut req: Request<_>| {
|
|
||||||
load_body(req.take_payload())
|
|
||||||
.and_then(|body| Ok(Response::Ok().body(body)))
|
|
||||||
})
|
|
||||||
.map_err(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send_body(data.clone())).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
|
|
||||||
let body = srv.load_body(response).unwrap();
|
|
||||||
assert_eq!(&body, data.as_bytes());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_content_length() {
|
|
||||||
let openssl = ssl_acceptor().unwrap();
|
|
||||||
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
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(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let header = HeaderName::from_static("content-length");
|
|
||||||
let value = HeaderValue::from_static("0");
|
|
||||||
|
|
||||||
{
|
|
||||||
for i in 0..4 {
|
|
||||||
let req = srv
|
|
||||||
.request(Method::GET, srv.surl(&format!("/{}", i)))
|
|
||||||
.send();
|
|
||||||
let response = srv.block_on(req).unwrap();
|
|
||||||
assert_eq!(response.headers().get(&header), None);
|
|
||||||
|
|
||||||
let req = srv
|
|
||||||
.request(Method::HEAD, srv.surl(&format!("/{}", i)))
|
|
||||||
.send();
|
|
||||||
let response = srv.block_on(req).unwrap();
|
|
||||||
assert_eq!(response.headers().get(&header), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in 4..6 {
|
|
||||||
let req = srv
|
|
||||||
.request(Method::GET, srv.surl(&format!("/{}", i)))
|
|
||||||
.send();
|
|
||||||
let response = srv.block_on(req).unwrap();
|
|
||||||
assert_eq!(response.headers().get(&header), Some(&value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_headers() {
|
|
||||||
let data = STR.repeat(10);
|
|
||||||
let data2 = data.clone();
|
|
||||||
let openssl = ssl_acceptor().unwrap();
|
|
||||||
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
let data = data.clone();
|
|
||||||
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 {
|
|
||||||
builder.header(
|
|
||||||
format!("X-TEST-{}", idx).as_str(),
|
|
||||||
"TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ok::<_, ()>(builder.body(data.clone()))
|
|
||||||
}).map_err(|_| ()))
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
|
|
||||||
// read response
|
|
||||||
let bytes = srv.load_body(response).unwrap();
|
|
||||||
assert_eq!(bytes, Bytes::from(data2));
|
|
||||||
}
|
|
||||||
|
|
||||||
const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
|
||||||
Hello World Hello World Hello World Hello World Hello World";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_body2() {
|
|
||||||
let openssl = ssl_acceptor().unwrap();
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
openssl
|
|
||||||
.clone()
|
|
||||||
.map_err(|e| println!("Openssl error: {}", e))
|
|
||||||
.and_then(
|
|
||||||
HttpService::build()
|
|
||||||
.h2(|_| ok::<_, ()>(Response::Ok().body(STR)))
|
|
||||||
.map_err(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
|
|
||||||
// read response
|
|
||||||
let bytes = srv.load_body(response).unwrap();
|
|
||||||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_head_empty() {
|
|
||||||
let openssl = ssl_acceptor().unwrap();
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
openssl
|
|
||||||
.clone()
|
|
||||||
.map_err(|e| println!("Openssl error: {}", e))
|
|
||||||
.and_then(
|
|
||||||
HttpService::build()
|
|
||||||
.finish(|_| ok::<_, ()>(Response::Ok().body(STR)))
|
|
||||||
.map_err(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.shead("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
assert_eq!(response.version(), Version::HTTP_2);
|
|
||||||
|
|
||||||
{
|
|
||||||
let len = response.headers().get(header::CONTENT_LENGTH).unwrap();
|
|
||||||
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
// read response
|
|
||||||
let bytes = srv.load_body(response).unwrap();
|
|
||||||
assert!(bytes.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_head_binary() {
|
|
||||||
let openssl = ssl_acceptor().unwrap();
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
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(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.shead("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
|
|
||||||
{
|
|
||||||
let len = response.headers().get(header::CONTENT_LENGTH).unwrap();
|
|
||||||
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
// read response
|
|
||||||
let bytes = srv.load_body(response).unwrap();
|
|
||||||
assert!(bytes.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_head_binary2() {
|
|
||||||
let openssl = ssl_acceptor().unwrap();
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
openssl
|
|
||||||
.clone()
|
|
||||||
.map_err(|e| println!("Openssl error: {}", e))
|
|
||||||
.and_then(
|
|
||||||
HttpService::build()
|
|
||||||
.h2(|_| ok::<_, ()>(Response::Ok().body(STR)))
|
|
||||||
.map_err(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.shead("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
|
|
||||||
{
|
|
||||||
let len = response.headers().get(header::CONTENT_LENGTH).unwrap();
|
|
||||||
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_body_length() {
|
|
||||||
let openssl = ssl_acceptor().unwrap();
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
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(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
|
|
||||||
// read response
|
|
||||||
let bytes = srv.load_body(response).unwrap();
|
|
||||||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_body_chunked_explicit() {
|
|
||||||
let openssl = ssl_acceptor().unwrap();
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
openssl
|
|
||||||
.clone()
|
|
||||||
.map_err(|e| println!("Openssl error: {}", e))
|
|
||||||
.and_then(
|
|
||||||
HttpService::build()
|
|
||||||
.h2(|_| {
|
|
||||||
let body =
|
|
||||||
once::<_, Error>(Ok(Bytes::from_static(STR.as_ref())));
|
|
||||||
ok::<_, ()>(
|
|
||||||
Response::Ok()
|
|
||||||
.header(header::TRANSFER_ENCODING, "chunked")
|
|
||||||
.streaming(body),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.map_err(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
assert!(!response.headers().contains_key(header::TRANSFER_ENCODING));
|
|
||||||
|
|
||||||
// read response
|
|
||||||
let bytes = srv.load_body(response).unwrap();
|
|
||||||
|
|
||||||
// decode
|
|
||||||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_response_http_error_handling() {
|
|
||||||
let openssl = ssl_acceptor().unwrap();
|
|
||||||
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
openssl
|
|
||||||
.clone()
|
|
||||||
.map_err(|e| println!("Openssl error: {}", e))
|
|
||||||
.and_then(
|
|
||||||
HttpService::build()
|
|
||||||
.h2(new_service_cfg(|_: &ServerConfig| {
|
|
||||||
Ok::<_, ()>(|_| {
|
|
||||||
let broken_header = Bytes::from_static(b"\0\0\0");
|
|
||||||
ok::<_, ()>(
|
|
||||||
Response::Ok()
|
|
||||||
.header(header::CONTENT_TYPE, broken_header)
|
|
||||||
.body(STR),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
.map_err(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
|
||||||
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
||||||
|
|
||||||
// read response
|
|
||||||
let bytes = srv.load_body(response).unwrap();
|
|
||||||
assert_eq!(bytes, Bytes::from_static(b"failed to parse header value"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_service_error() {
|
|
||||||
let openssl = ssl_acceptor().unwrap();
|
|
||||||
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
openssl
|
|
||||||
.clone()
|
|
||||||
.map_err(|e| println!("Openssl error: {}", e))
|
|
||||||
.and_then(
|
|
||||||
HttpService::build()
|
|
||||||
.h2(|_| Err::<Response, Error>(ErrorBadRequest("error")))
|
|
||||||
.map_err(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
|
||||||
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
|
||||||
|
|
||||||
// read response
|
|
||||||
let bytes = srv.load_body(response).unwrap();
|
|
||||||
assert_eq!(bytes, Bytes::from_static(b"error"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_h2_on_connect() {
|
|
||||||
let openssl = ssl_acceptor().unwrap();
|
|
||||||
|
|
||||||
let mut srv = TestServer::new(move || {
|
|
||||||
openssl
|
|
||||||
.clone()
|
|
||||||
.map_err(|e| println!("Openssl error: {}", e))
|
|
||||||
.and_then(
|
|
||||||
HttpService::build()
|
|
||||||
.on_connect(|_| 10usize)
|
|
||||||
.h2(|req: Request| {
|
|
||||||
assert!(req.extensions().contains::<usize>());
|
|
||||||
ok::<_, ()>(Response::Ok().finish())
|
|
||||||
})
|
|
||||||
.map_err(|_| ()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
}
|
|
@ -1,26 +1,27 @@
|
|||||||
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
||||||
use actix_http::{body, h1, ws, Error, HttpService, Request, Response};
|
use actix_http::{body, h1, ws, Error, HttpService, Request, Response};
|
||||||
use actix_http_test::TestServer;
|
use actix_http_test::{block_on, TestServer};
|
||||||
use actix_utils::framed::FramedTransport;
|
use actix_utils::framed::FramedTransport;
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use futures::future::{self, ok};
|
use futures::future;
|
||||||
use futures::{Future, Sink, Stream};
|
use futures::{SinkExt, StreamExt};
|
||||||
|
|
||||||
fn ws_service<T: AsyncRead + AsyncWrite>(
|
async fn ws_service<T: AsyncRead + AsyncWrite + Unpin>(
|
||||||
(req, framed): (Request, Framed<T, h1::Codec>),
|
(req, mut framed): (Request, Framed<T, h1::Codec>),
|
||||||
) -> impl Future<Item = (), Error = Error> {
|
) -> Result<(), Error> {
|
||||||
let res = ws::handshake(req.head()).unwrap().message_body(());
|
let res = ws::handshake(req.head()).unwrap().message_body(());
|
||||||
|
|
||||||
framed
|
framed
|
||||||
.send((res, body::BodySize::None).into())
|
.send((res, body::BodySize::None).into())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
FramedTransport::new(framed.into_framed(ws::Codec::new()), service)
|
||||||
|
.await
|
||||||
.map_err(|_| panic!())
|
.map_err(|_| panic!())
|
||||||
.and_then(|framed| {
|
|
||||||
FramedTransport::new(framed.into_framed(ws::Codec::new()), service)
|
|
||||||
.map_err(|_| panic!())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn service(msg: ws::Frame) -> impl Future<Item = ws::Message, Error = Error> {
|
async fn service(msg: ws::Frame) -> Result<ws::Message, Error> {
|
||||||
let msg = match msg {
|
let msg = match msg {
|
||||||
ws::Frame::Ping(msg) => ws::Message::Pong(msg),
|
ws::Frame::Ping(msg) => ws::Message::Pong(msg),
|
||||||
ws::Frame::Text(text) => {
|
ws::Frame::Text(text) => {
|
||||||
@ -30,47 +31,56 @@ fn service(msg: ws::Frame) -> impl Future<Item = ws::Message, Error = Error> {
|
|||||||
ws::Frame::Close(reason) => ws::Message::Close(reason),
|
ws::Frame::Close(reason) => ws::Message::Close(reason),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
ok(msg)
|
Ok(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
fn test_simple() {
|
||||||
let mut srv = TestServer::new(|| {
|
block_on(async {
|
||||||
HttpService::build()
|
let mut srv = TestServer::start(|| {
|
||||||
.upgrade(ws_service)
|
HttpService::build()
|
||||||
.finish(|_| future::ok::<_, ()>(Response::NotFound()))
|
.upgrade(actix_service::service_fn(ws_service))
|
||||||
});
|
.finish(|_| future::ok::<_, ()>(Response::NotFound()))
|
||||||
|
});
|
||||||
|
|
||||||
// client service
|
// client service
|
||||||
let framed = srv.ws().unwrap();
|
let mut framed = srv.ws().await.unwrap();
|
||||||
let framed = srv
|
framed
|
||||||
.block_on(framed.send(ws::Message::Text("text".to_string())))
|
.send(ws::Message::Text("text".to_string()))
|
||||||
.unwrap();
|
.await
|
||||||
let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap();
|
.unwrap();
|
||||||
assert_eq!(item, Some(ws::Frame::Text(Some(BytesMut::from("text")))));
|
let (item, mut framed) = framed.into_future().await;
|
||||||
|
assert_eq!(
|
||||||
|
item.unwrap().unwrap(),
|
||||||
|
ws::Frame::Text(Some(BytesMut::from("text")))
|
||||||
|
);
|
||||||
|
|
||||||
let framed = srv
|
framed
|
||||||
.block_on(framed.send(ws::Message::Binary("text".into())))
|
.send(ws::Message::Binary("text".into()))
|
||||||
.unwrap();
|
.await
|
||||||
let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
let (item, mut framed) = framed.into_future().await;
|
||||||
item,
|
assert_eq!(
|
||||||
Some(ws::Frame::Binary(Some(Bytes::from_static(b"text").into())))
|
item.unwrap().unwrap(),
|
||||||
);
|
ws::Frame::Binary(Some(Bytes::from_static(b"text").into()))
|
||||||
|
);
|
||||||
|
|
||||||
let framed = srv
|
framed.send(ws::Message::Ping("text".into())).await.unwrap();
|
||||||
.block_on(framed.send(ws::Message::Ping("text".into())))
|
let (item, mut framed) = framed.into_future().await;
|
||||||
.unwrap();
|
assert_eq!(
|
||||||
let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap();
|
item.unwrap().unwrap(),
|
||||||
assert_eq!(item, Some(ws::Frame::Pong("text".to_string().into())));
|
ws::Frame::Pong("text".to_string().into())
|
||||||
|
);
|
||||||
|
|
||||||
let framed = srv
|
framed
|
||||||
.block_on(framed.send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))))
|
.send(ws::Message::Close(Some(ws::CloseCode::Normal.into())))
|
||||||
.unwrap();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (item, _framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap();
|
let (item, _framed) = framed.into_future().await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
item,
|
item.unwrap().unwrap(),
|
||||||
Some(ws::Frame::Close(Some(ws::CloseCode::Normal.into())))
|
ws::Frame::Close(Some(ws::CloseCode::Normal.into()))
|
||||||
);
|
);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use actix_rt::{System};
|
|||||||
use actix_server::{Server, ServiceFactory};
|
use actix_server::{Server, ServiceFactory};
|
||||||
use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connector};
|
use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connector};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::{Stream, future::lazy};
|
use futures::Stream;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
use net2::TcpBuilder;
|
use net2::TcpBuilder;
|
||||||
use tokio_net::tcp::TcpStream;
|
use tokio_net::tcp::TcpStream;
|
||||||
@ -55,7 +55,7 @@ pub struct TestServerRuntime {
|
|||||||
impl TestServer {
|
impl TestServer {
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
/// Start new test server with application factory
|
/// Start new test server with application factory
|
||||||
pub fn new<F: ServiceFactory<TcpStream>>(factory: F) -> TestServerRuntime {
|
pub fn start<F: ServiceFactory<TcpStream>>(factory: F) -> TestServerRuntime {
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
// run server in separate thread
|
// run server in separate thread
|
||||||
@ -76,11 +76,11 @@ impl TestServer {
|
|||||||
|
|
||||||
let (system, addr) = rx.recv().unwrap();
|
let (system, addr) = rx.recv().unwrap();
|
||||||
|
|
||||||
let client = block_on(lazy(move |_| {
|
let client = {
|
||||||
let connector = {
|
let connector = {
|
||||||
#[cfg(feature = "ssl")]
|
#[cfg(feature = "openssl")]
|
||||||
{
|
{
|
||||||
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
|
use open_ssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
|
||||||
|
|
||||||
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||||
builder.set_verify(SslVerifyMode::NONE);
|
builder.set_verify(SslVerifyMode::NONE);
|
||||||
@ -93,7 +93,7 @@ impl TestServer {
|
|||||||
.ssl(builder.build())
|
.ssl(builder.build())
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "ssl"))]
|
#[cfg(not(feature = "openssl"))]
|
||||||
{
|
{
|
||||||
Connector::new()
|
Connector::new()
|
||||||
.conn_lifetime(time::Duration::from_secs(0))
|
.conn_lifetime(time::Duration::from_secs(0))
|
||||||
@ -102,14 +102,9 @@ impl TestServer {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok::<Client, ()>(Client::build().connector(connector).finish())
|
Client::build().connector(connector).finish()
|
||||||
}))
|
};
|
||||||
.unwrap();
|
actix_connect::start_default_resolver();
|
||||||
|
|
||||||
block_on(lazy(|_| {
|
|
||||||
Ok::<_, ()>(actix_connect::start_default_resolver())
|
|
||||||
}))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
TestServerRuntime {
|
TestServerRuntime {
|
||||||
addr,
|
addr,
|
||||||
@ -228,35 +223,33 @@ impl TestServerRuntime {
|
|||||||
self.client.request(method, path.as_ref())
|
self.client.request(method, path.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_body<S>(
|
pub async fn load_body<S>(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut response: ClientResponse<S>,
|
mut response: ClientResponse<S>,
|
||||||
) -> Result<Bytes, PayloadError>
|
) -> Result<Bytes, PayloadError>
|
||||||
where
|
where
|
||||||
S: Stream<Item = Result<Bytes, PayloadError>> + Unpin + 'static,
|
S: Stream<Item = Result<Bytes, PayloadError>> + Unpin + 'static,
|
||||||
{
|
{
|
||||||
block_on(response.body().limit(10_485_760))
|
response.body().limit(10_485_760).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Connect to websocket server at a given path
|
/// Connect to websocket server at a given path
|
||||||
pub fn ws_at(
|
pub async fn ws_at(
|
||||||
&mut self,
|
&mut self,
|
||||||
path: &str,
|
path: &str,
|
||||||
) -> Result<Framed<impl AsyncRead + AsyncWrite, ws::Codec>, awc::error::WsClientError>
|
) -> Result<Framed<impl AsyncRead + AsyncWrite, ws::Codec>, awc::error::WsClientError>
|
||||||
{
|
{
|
||||||
let url = self.url(path);
|
let url = self.url(path);
|
||||||
let connect = self.client.ws(url).connect();
|
let connect = self.client.ws(url).connect();
|
||||||
block_on(async move {
|
connect.await.map(|(_, framed)| framed)
|
||||||
connect.await.map(|(_, framed)| framed)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Connect to a websocket server
|
/// Connect to a websocket server
|
||||||
pub fn ws(
|
pub async fn ws(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> Result<Framed<impl AsyncRead + AsyncWrite, ws::Codec>, awc::error::WsClientError>
|
) -> Result<Framed<impl AsyncRead + AsyncWrite, ws::Codec>, awc::error::WsClientError>
|
||||||
{
|
{
|
||||||
self.ws_at("/")
|
self.ws_at("/").await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stop http server
|
/// Stop http server
|
||||||
|
@ -1,32 +1,19 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIFfjCCA2agAwIBAgIJAOIBvp/w68KrMA0GCSqGSIb3DQEBCwUAMGsxCzAJBgNV
|
MIIDEDCCAfgCCQCQdmIZc/Ib/jANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQGEwJ1
|
||||||
BAYTAlJVMRkwFwYDVQQIDBBTYWludC1QZXRlcnNidXJnMRkwFwYDVQQHDBBTYWlu
|
czELMAkGA1UECAwCY2ExCzAJBgNVBAcMAnNmMSEwHwYJKoZIhvcNAQkBFhJmYWZo
|
||||||
dC1QZXRlcnNidXJnMRIwEAYDVQQKDAlLdXBpYmlsZXQxEjAQBgNVBAMMCWxvY2Fs
|
cmQ5MUBnbWFpbC5jb20wHhcNMTkxMTE5MTEwNjU1WhcNMjkxMTE2MTEwNjU1WjBK
|
||||||
aG9zdDAgFw0xOTA3MjcxODIzMTJaGA8zMDE5MDcyNzE4MjMxMlowazELMAkGA1UE
|
MQswCQYDVQQGEwJ1czELMAkGA1UECAwCY2ExCzAJBgNVBAcMAnNmMSEwHwYJKoZI
|
||||||
BhMCUlUxGTAXBgNVBAgMEFNhaW50LVBldGVyc2J1cmcxGTAXBgNVBAcMEFNhaW50
|
hvcNAQkBFhJmYWZocmQ5MUBnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||||
LVBldGVyc2J1cmcxEjAQBgNVBAoMCUt1cGliaWxldDESMBAGA1UEAwwJbG9jYWxo
|
DwAwggEKAoIBAQDcnaz12CKzUL7248V7Axhms/O9UQXfAdw0yolEfC3P5jADa/1C
|
||||||
b3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuiQZzTO3gRRPr6ZH
|
+kLWKjAc2coqDSbGsrsR6KiH2g06Kunx+tSGqUO+Sct7HEehmxndiSwx/hfMWezy
|
||||||
wcmKqkoXig9taCCqx72Qvb9tvCLhQLE1dDPZV8I/r8bx+mM4Yz3r0Hm5LxTIhCM9
|
XRe/olcHFTeCk/Tllz4xGEplhPua6GLhJygLOhAMiV8cwCYrgyPqsDduExLDFCqc
|
||||||
p3/abuiJAZENC/VkxgFzBGg7KGLSFmzU+A8Ft+2mrKmj5MpIPBCxDeVg80TCQOJy
|
K2xntIPreumXpiE3QY4+MWyteiJko4IWDFf/UwwsdCY5MlFfw1F/Uv9vz7FfOfvu
|
||||||
hj+NU3PpBo9nxTgxWNWO6X+ZovZohdp78fYLLtns8rxjug3FVzdPrrLnBvihkGlq
|
GccHd/ex8cOwotUqd6emZb+0bVE24Sv8U+yLnHIVx/tOkxgMAnJEpAnf2G3Wp3zU
|
||||||
gfImkh+vZxMTj1OgtxyCOhdbO4Ol4jCbn7a5yIw+iixHOEgBQfTQopRP7z1PEUV2
|
b2GJosbmfGaf+xTfnGGhTLLL7kCtva+NvZr5AgMBAAEwDQYJKoZIhvcNAQELBQAD
|
||||||
WIy2VEGzvQDlj2OyzH86T1IOFV5rz5MjdZuW0qNzeS0w3Jzgp/olSbIZLhGAaIk0
|
ggEBANftoL8zDGrjCwWvct8kOOqset2ukK8vjIGwfm88CKsy0IfSochNz2qeIu9R
|
||||||
gN7y9XvSHqs7rO0wW+467ico7+uP1ScGgPgJA5fGu7ahp7F7G3ZSoAqAcS60wYsX
|
ZuO7c0pfjmRkir9ZQdq9vXgG3ccL9UstFsferPH9W3YJ83kgXg3fa0EmCiN/0hwz
|
||||||
kStoA3RWAuqste6aChv1tlgTt+Rhk8qjGhuh0ng2qVnTGyo2T3OCHB/c47Bcsp6L
|
6Ij1ZBiN1j3+d6+PJPgyYFNu2nGwox5mJ9+aRAGe0/9c63PEOY8P2TI4HsiPmYSl
|
||||||
xiyTCnQIPH3fh2iO/SC7gPw3jihPMCAQZYlkC3MhMk974rn2zs9cKtJ8ubnG2m5F
|
fFR8k/03vr6e+rTKW85BgctjvYKe/TnFxeCQ7dZ+na7vlEtch4tNmy6O/vEk2kCt
|
||||||
VFVYmduRqS/YQS/O802jVCFdc8KDmoeAYNuHzgRZjQv9018UUeW3jtWKnopJnWs5
|
5jW0DUxhmRsv2wGmfFRI0+LotHjoXQQZi6nN5aGL3odaGF3gYwIVlZNd3AdkwDQz
|
||||||
ae9pbtmYeOtc7OovOxT7J2AaVfUkHRhmlqWZMzEJTcZws0fRPTZDifFJ5LFWbZsC
|
BzG0ZwXuDDV9bSs3MfWEWcy4xuU=
|
||||||
zW4tCKBKvYM9eAnbb+abiHXlY1MCAwEAAaMjMCEwHwYDVR0RBBgwFoIJbG9jYWxo
|
|
||||||
b3N0ggkxMjcuMC4wLjEwDQYJKoZIhvcNAQELBQADggIBAC1EU4SVCfUKc7JbhYRf
|
|
||||||
P87F+8e13bBTLxevJdnTCH3Xw2AN8UPmwQ2vv9Mv2FMulMBQ7yLnQLGtgGUua2OE
|
|
||||||
XO+EdBBEKnglo9cwXGzU6qHhaiCeXZDM8s53qOOrD42XsDsY0nOoFYqDLW3WixP9
|
|
||||||
f1fWbcEf6+ktlvqi/1/3R6QtQR+6LS43enbsYHq8aAP60NrpXxdXxEoUwW6Z/sje
|
|
||||||
XAQluH8jzledwJcY8bXRskAHZlE4kGlOVuGgnyI3BXyLiwB4g9smFzYIs98iAGmV
|
|
||||||
7ZBaR5IIiRCtoKBG+SngM7Log0bHphvFPjDDvgqWYiWaOHboYM60Y2Z/gRbcjuMU
|
|
||||||
WZX64jw29fa8UPFdtGTupt+iuO7iXnHnm0lBBK36rVdOvsZup76p6L4BXmFsRmFK
|
|
||||||
qJ2Zd8uWNPDq80Am0mYaAqENuIANHHJXX38SesC+QO+G2JZt6vCwkGk/Qune4GIg
|
|
||||||
1GwhvsDRfTQopSxg1rdPwPM7HWeTfUGHZ34B5p/iILA3o6PfYQU8fNAWIsCDkRX2
|
|
||||||
MrgDgCnLZxKb6pjR4DYNAdPwkxyMFACZ2T46z6WvLWFlnkK5nbZoqsOsp+GJHole
|
|
||||||
llafhrelXEzt3zFR0q4zGcqheJDI+Wy+fBy3XawgAc4eN0T2UCzL/jKxKgzlzSU3
|
|
||||||
+xh1SDNjFLRd6sGzZHPMgXN0
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
@ -1,52 +1,28 @@
|
|||||||
-----BEGIN PRIVATE KEY-----
|
-----BEGIN PRIVATE KEY-----
|
||||||
MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQC6JBnNM7eBFE+v
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDcnaz12CKzUL72
|
||||||
pkfByYqqSheKD21oIKrHvZC9v228IuFAsTV0M9lXwj+vxvH6YzhjPevQebkvFMiE
|
48V7Axhms/O9UQXfAdw0yolEfC3P5jADa/1C+kLWKjAc2coqDSbGsrsR6KiH2g06
|
||||||
Iz2nf9pu6IkBkQ0L9WTGAXMEaDsoYtIWbNT4DwW37aasqaPkykg8ELEN5WDzRMJA
|
Kunx+tSGqUO+Sct7HEehmxndiSwx/hfMWezyXRe/olcHFTeCk/Tllz4xGEplhPua
|
||||||
4nKGP41Tc+kGj2fFODFY1Y7pf5mi9miF2nvx9gsu2ezyvGO6DcVXN0+usucG+KGQ
|
6GLhJygLOhAMiV8cwCYrgyPqsDduExLDFCqcK2xntIPreumXpiE3QY4+MWyteiJk
|
||||||
aWqB8iaSH69nExOPU6C3HII6F1s7g6XiMJuftrnIjD6KLEc4SAFB9NCilE/vPU8R
|
o4IWDFf/UwwsdCY5MlFfw1F/Uv9vz7FfOfvuGccHd/ex8cOwotUqd6emZb+0bVE2
|
||||||
RXZYjLZUQbO9AOWPY7LMfzpPUg4VXmvPkyN1m5bSo3N5LTDcnOCn+iVJshkuEYBo
|
4Sv8U+yLnHIVx/tOkxgMAnJEpAnf2G3Wp3zUb2GJosbmfGaf+xTfnGGhTLLL7kCt
|
||||||
iTSA3vL1e9Ieqzus7TBb7jruJyjv64/VJwaA+AkDl8a7tqGnsXsbdlKgCoBxLrTB
|
va+NvZr5AgMBAAECggEBAKoU0UwzVgVCQgca8Jt2dnBvWYDhnxIfYAI/BvaKedMm
|
||||||
ixeRK2gDdFYC6qy17poKG/W2WBO35GGTyqMaG6HSeDapWdMbKjZPc4IcH9zjsFyy
|
1ms87OKfB7oOiksjyI0E2JklH72dzZf2jm4CuZt5UjGC+xwPzlTaJ4s6hQVbBHyC
|
||||||
novGLJMKdAg8fd+HaI79ILuA/DeOKE8wIBBliWQLcyEyT3viufbOz1wq0ny5ucba
|
NRyxU1BCXtW5tThbrhD4OjxqjmLRJEIB9OunLtwAEQoeuFLB8Va7+HFhR+Zd9k3f
|
||||||
bkVUVViZ25GpL9hBL87zTaNUIV1zwoOah4Bg24fOBFmNC/3TXxRR5beO1Yqeikmd
|
7aVA93pC5A50NRbZlke4miJ3Q8n7ZF0+UmxkBfm3fbqLk7aMWkoEKwLLTadjRlu1
|
||||||
azlp72lu2Zh461zs6i87FPsnYBpV9SQdGGaWpZkzMQlNxnCzR9E9NkOJ8UnksVZt
|
bBp0YDStX66I/p1kujqBOdh6VpPvxFOa1sV9pq0jeiGc9YfSkzRSKzIn8GoyviFB
|
||||||
mwLNbi0IoEq9gz14Cdtv5puIdeVjUwIDAQABAoICAQCZVVezw+BsAjFKPi1qIv2J
|
fHeszQdNlcnrSDSNnMABAw+ZpxUO7SCaftjwejEmKZUCgYEA+TY43VpmV95eY7eo
|
||||||
HZOadO7pEc/czflHdUON8SWgxtmDqZpmQmt3/ugiHE284qs4hqzXbcVnpCgLrLRh
|
WKwGepiHE0fwQLuKGELmZdZI80tFi73oZMuiB5WzwmkaKGcJmm7KGE9KEvHQCo9j
|
||||||
HEiP887NhQ3IVjVK8hmZQR5SvsAIv0c0ph3gqbWKqF8sq4tOKR/eBUwHawJwODXR
|
xvmktBR0VEZH8pmVfun+4h6+0H7m/NKMBBeOyv/IK8jBgHjkkB6e6nmeR7CqTxCw
|
||||||
AvB4KPWQbqOny/P3wNbseRLNAJeNT+MSaw5XPnzgLKvdFoEbJeBNy847Sbsk5DaF
|
tf9tbajl1QN8gNzXZSjBDT/lanMCgYEA4qANOKOSiEARtgwyXQeeSJcM2uPv6zF3
|
||||||
tHgm7n30WS1Q6bkU5VyP//hMBUKNJFaSL4TtCWB5qkbu8B5VbtsR9m0FizTb6L3h
|
ffM7vjSedtuEOHUSVeyBP/W8KDt7zyPppO/WNbURHS+HV0maS9yyj6zpVS2HGmbs
|
||||||
VmYbUXvIzJXjAwMjiDJ1w9wHl+tj3BE33tEmhuVzNf+SH+tLc9xuKJighDWt2vpD
|
3fetswsQ+zYVdokW89x4oc2z4XOGHd1LcSlyhRwPt0u2g1E9L0irwTQLWU0npFmG
|
||||||
eTpZ1qest26ANLOmNXWVCVTGpcWvOu5yhG/P7En10EzjFruMfHAFdwLm1gMx1rlR
|
PRf7sN9+LeMCgYAGkDUDL2ROoB6gRa/7Vdx90hKMoXJkYgwLA4gJ2pDlR3A3c/Lw
|
||||||
9fyNAk/0ROJ+5BUtuWgDiyytS5f2T9KGiOHni7UbBIkv0CV2H6VL39Twxf+3OHnx
|
5KQJyxmG3zm/IqeQF6be6QesZA30mT4peV2rGHbP2WH/s6fKReNelSy1VQJEWk8x
|
||||||
JJ7OWZ8DRuLM/EJfN3C1+3eDsXOvcdvbo2TFBmCCl4Pa2pm4k3g2NBfxy/zSYWIh
|
tGUgV4gwDwN5nLV4TjYlOrq+bJqvpmLhCC8bmj0jVQosYqSRl3cuICasnQKBgGlV
|
||||||
ccGPZorFKNMUi29U0Ool6fxeVflbll570pWVBLAB31HdkLSESv9h+2j/IiEJcJXj
|
VO/Xb1su1EyWPK5qxRIeSxZOTYw2sMB01nbgxCqge0M2fvA6/hQ5ZlwY0cIEgits
|
||||||
nzl2RtYB0Uxzk6SjO0z4WXjz/SXg5tQQkm/dx8kM8TvHICFq68AEnw8t9Hagsdxs
|
YlcSMsMq/TAAANxz1vbaupUhlSMbZcsBvNV0Nk9c4vr2Wxm7hsJF9u66IEMvQUp2
|
||||||
v5jNyOEeI1I5gPgZmKuboQKCAQEA7Hw6s8Xc3UtNaywMcK1Eb1O+kwRdztgtm0uE
|
pkjiMxfR9CHzF4orr9EcHI5EQ0Grbq5kwFKEfoRbAoGAcWoFPILeJOlp2yW/Ds3E
|
||||||
uqsHWmGqbBxXN4jiVLh3dILIXFuVbvDSsSZtPLhOj1wqxgsTHg93b4BtvowyNBgo
|
g2fQdI9BAamtEZEaslJmZMmsDTg5ACPcDkOSFEQIaJ7wLPXeZy74FVk/NrY5F8Gz
|
||||||
X4tErMu7/6NRael/hfOEdtpfv2gV+0eQa+8KKqYJPbqpMz/r5L/3RaxS3iXkj3QM
|
bjX9OD/xzwp852yW5L9r62vYJakAlXef5jI6CFdYKDDCcarU0S7W5k6kq9n+wrBR
|
||||||
6oC4+cRuwy/flPMIpxhDriH5yjfiMOdDsi3ZfMTJu/58DTrKV7WkJxQZmha4EoZn
|
i1NklYmUAMr2q59uJA5zsic=
|
||||||
IiXeRhzo+2ukMDWrr3GGPyDfjd/NB7rmY8QBdmhB5NSk+6B66JCNTIbKka/pichS
|
|
||||||
36bwSYFNji4NaHUUlYDUjfKoTNuQMEZknMGhc/433ADO7s17iQKCAQEAyYBYVG7/
|
|
||||||
LE2IkvQy9Nwly5tRCNlSvSkloz7PUwRbzG5uF5mweWEa8YECJe9/vrFXvyBW+NR8
|
|
||||||
XABFn4eG0POTR9nyb4n2nUlqiGugDIPgkrKCkJws5InifITZ/+Viocd4YZL5UwCU
|
|
||||||
R1/kMf0UjK2iJjWEeTPS6RmwRI2Iu7kym9BzphDyNYBQSbUE/f+4hNP6nUT/h09c
|
|
||||||
VH4/sUhubSgVKeK4onOci8bKitAkwVBYCYSyhuBCeCu8fTk2hVRWviRaJPVq2PMB
|
|
||||||
LHw1FCcfJLIPJG6MZpFAPkMQxpiewdggXIgi46ZlZcsNXEJ81ocT4GU2j+ArQXCf
|
|
||||||
lgEycyD3mx4k+wKCAQBGneohmKoVYtEheavVUcgnvkggOqOQirlDsE9YNo4hjRyI
|
|
||||||
4AWjTbrYNaVmI0+VVLvQvxULVUA1a4v5/zm+nbv9s/ykTSN4TQEI0VXtAfdl6gif
|
|
||||||
k7NR/ynXZBpgK2GAFKLLwFj+Agl1JtOHnV+9MA9O5Yv/QDAWqhYQSEU7GWkjHGc+
|
|
||||||
3eLT5ablzrcXHooqunlOxSBP6qURPupGuv1sLewSOOllyfjDLJmW3o+ZgNlY8nUX
|
|
||||||
7tK+mqhD4ZCG9VgMU5I0BrmZfQQ6yXMz09PYV9mb7N5kxbNjwbXpMOqeYolKSdRQ
|
|
||||||
6quST7Pv2OKf6KAdI0txPvP4Y1HFA1rG1W71nGKRAoIBAHlDU+T8J3Rx9I77hu70
|
|
||||||
zYoKnmnE35YW/R+Q3RQIu3X7vyVUyG9DkQNlr/VEfIw2Dahnve9hcLWtNDkdRnTZ
|
|
||||||
IPlMoCmfzVo6pHIU0uy1MKEX7Js6YYnnsPVevhLR6NmTQU73NDRPVOzfOGUc+RDw
|
|
||||||
LXTxIBgQqAy/+ORIiNDwUxSSDgcSi7DG14qD9c0l59WH/HpI276Cc/4lPA9kl4/5
|
|
||||||
X0MlvheFm+BCcgG34Wa1A0Y3JXkl3NqU94oktDro1or3NYioaPTGyR4MYaUPJh7f
|
|
||||||
SV2TacsP/ql5ks7xahkeB9un0ddOfBcWa6PqH1a7U6rnPj63mVB4hpGvhrziSiB/
|
|
||||||
s6ECggEAOp2P4Yd9Vm9/CptxS50HFF4adyLscAtsDd3S2hIAXhDovcPbvRek4pLQ
|
|
||||||
idPhHlRAfqrEztnhaVAmCK9HlhgthtiQGQX62YI4CS4QL2IhzDFo3M1a2snjFEdl
|
|
||||||
QuFk3XI7kQ0Yp8BLLG7T436JUrUkCXc4gQX2uRNut+ff34RIR2CjcQQjChxuHVeG
|
|
||||||
sP/3xFFj8OSs7ZoSPbmDBLrMOl64YHwezQUNAZiRYiaGbFiY0QUV6dHq8qX/qE1h
|
|
||||||
a/0Rq+gTqObDST0TqhMzI8V/i7R8SwVcD5ODHaZp5I2N2P/hV5OWY7ghQXhh89WM
|
|
||||||
o21xtGh0nP2Fq1TC6jFO+9cpbK8jNA==
|
|
||||||
-----END PRIVATE KEY-----
|
-----END PRIVATE KEY-----
|
||||||
|
Loading…
Reference in New Issue
Block a user