mirror of https://github.com/fafhrd91/actix-web
Merge remote-tracking branch 'origin/master' into header-from-request
This commit is contained in:
commit
a6964eed9b
|
@ -0,0 +1,3 @@
|
||||||
|
[alias]
|
||||||
|
chk = "hack check --workspace --tests --examples"
|
||||||
|
lint = "hack --clean-per-run clippy --workspace --tests --examples"
|
|
@ -3,6 +3,7 @@
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
### Added
|
### Added
|
||||||
* `Header` extractor for extracting common HTTP headers in handlers. [#2094]
|
* `Header` extractor for extracting common HTTP headers in handlers. [#2094]
|
||||||
|
* Added `TestServer::client_headers` method. [#2097]
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
* Double ampersand in Logger format is escaped correctly. [#2067]
|
* Double ampersand in Logger format is escaped correctly. [#2067]
|
||||||
|
@ -16,8 +17,9 @@
|
||||||
[871ca5e4](https://github.com/actix/actix-web/commit/871ca5e4ae2bdc22d1ea02701c2992fa8d04aed7)
|
[871ca5e4](https://github.com/actix/actix-web/commit/871ca5e4ae2bdc22d1ea02701c2992fa8d04aed7)
|
||||||
|
|
||||||
[#2067]: https://github.com/actix/actix-web/pull/2067
|
[#2067]: https://github.com/actix/actix-web/pull/2067
|
||||||
[#2094]: https://github.com/actix/actix-web/pull/2094
|
|
||||||
[#2093]: https://github.com/actix/actix-web/pull/2093
|
[#2093]: https://github.com/actix/actix-web/pull/2093
|
||||||
|
[#2094]: https://github.com/actix/actix-web/pull/2094
|
||||||
|
[#2097]: https://github.com/actix/actix-web/pull/2097
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.4 - 2021-03-09
|
## 4.0.0-beta.4 - 2021-03-09
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
### Added
|
||||||
|
* Added `TestServer::client_headers` method. [#2097]
|
||||||
|
|
||||||
## 3.0.0-beta.3 - 2021-03-09
|
## 3.0.0-beta.3 - 2021-03-09
|
||||||
* No notable changes.
|
* No notable changes.
|
||||||
|
|
|
@ -13,7 +13,9 @@ use std::{net, thread, time};
|
||||||
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
||||||
use actix_rt::{net::TcpStream, System};
|
use actix_rt::{net::TcpStream, System};
|
||||||
use actix_server::{Server, ServiceFactory};
|
use actix_server::{Server, ServiceFactory};
|
||||||
use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connector};
|
use awc::{
|
||||||
|
error::PayloadError, http::HeaderMap, ws, Client, ClientRequest, ClientResponse, Connector,
|
||||||
|
};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_core::stream::Stream;
|
use futures_core::stream::Stream;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
@ -258,6 +260,14 @@ impl TestServer {
|
||||||
self.ws_at("/").await
|
self.ws_at("/").await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get default HeaderMap of Client.
|
||||||
|
///
|
||||||
|
/// Returns Some(&mut HeaderMap) when Client object is unique
|
||||||
|
/// (No other clone of client exists at the same time).
|
||||||
|
pub fn client_headers(&mut self) -> Option<&mut HeaderMap> {
|
||||||
|
self.client.headers()
|
||||||
|
}
|
||||||
|
|
||||||
/// Stop HTTP server
|
/// Stop HTTP server
|
||||||
fn stop(&mut self) {
|
fn stop(&mut self) {
|
||||||
self.system.stop();
|
self.system.stop();
|
||||||
|
|
|
@ -63,11 +63,9 @@ where
|
||||||
X: ServiceFactory<Request, Config = (), Response = Request>,
|
X: ServiceFactory<Request, Config = (), Response = Request>,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::InitError: fmt::Debug,
|
X::InitError: fmt::Debug,
|
||||||
<X::Service as Service<Request>>::Future: 'static,
|
|
||||||
U: ServiceFactory<(Request, Framed<T, Codec>), Config = (), Response = ()>,
|
U: ServiceFactory<(Request, Framed<T, Codec>), Config = (), Response = ()>,
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
U::InitError: fmt::Debug,
|
U::InitError: fmt::Debug,
|
||||||
<U::Service as Service<(Request, Framed<T, Codec>)>>::Future: 'static,
|
|
||||||
{
|
{
|
||||||
/// Set server keep-alive setting.
|
/// Set server keep-alive setting.
|
||||||
///
|
///
|
||||||
|
@ -127,7 +125,6 @@ where
|
||||||
X1: ServiceFactory<Request, Config = (), Response = Request>,
|
X1: ServiceFactory<Request, Config = (), Response = Request>,
|
||||||
X1::Error: Into<Error>,
|
X1::Error: Into<Error>,
|
||||||
X1::InitError: fmt::Debug,
|
X1::InitError: fmt::Debug,
|
||||||
<X1::Service as Service<Request>>::Future: 'static,
|
|
||||||
{
|
{
|
||||||
HttpServiceBuilder {
|
HttpServiceBuilder {
|
||||||
keep_alive: self.keep_alive,
|
keep_alive: self.keep_alive,
|
||||||
|
@ -152,7 +149,6 @@ where
|
||||||
U1: ServiceFactory<(Request, Framed<T, Codec>), Config = (), Response = ()>,
|
U1: ServiceFactory<(Request, Framed<T, Codec>), Config = (), Response = ()>,
|
||||||
U1::Error: fmt::Display,
|
U1::Error: fmt::Display,
|
||||||
U1::InitError: fmt::Debug,
|
U1::InitError: fmt::Debug,
|
||||||
<U1::Service as Service<(Request, Framed<T, Codec>)>>::Future: 'static,
|
|
||||||
{
|
{
|
||||||
HttpServiceBuilder {
|
HttpServiceBuilder {
|
||||||
keep_alive: self.keep_alive,
|
keep_alive: self.keep_alive,
|
||||||
|
@ -211,7 +207,6 @@ where
|
||||||
S::Error: Into<Error> + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
<S::Service as Service<Request>>::Future: 'static,
|
|
||||||
{
|
{
|
||||||
let cfg = ServiceConfig::new(
|
let cfg = ServiceConfig::new(
|
||||||
self.keep_alive,
|
self.keep_alive,
|
||||||
|
@ -233,7 +228,6 @@ where
|
||||||
S::Error: Into<Error> + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
<S::Service as Service<Request>>::Future: 'static,
|
|
||||||
{
|
{
|
||||||
let cfg = ServiceConfig::new(
|
let cfg = ServiceConfig::new(
|
||||||
self.keep_alive,
|
self.keep_alive,
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::{
|
||||||
use actix_codec::Framed;
|
use actix_codec::Framed;
|
||||||
use bytes::buf::BufMut;
|
use bytes::buf::BufMut;
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use futures_core::Stream;
|
use futures_core::{ready, Stream};
|
||||||
use futures_util::{future::poll_fn, SinkExt as _};
|
use futures_util::{future::poll_fn, SinkExt as _};
|
||||||
|
|
||||||
use crate::error::PayloadError;
|
use crate::error::PayloadError;
|
||||||
|
@ -17,7 +17,7 @@ use crate::http::{
|
||||||
StatusCode,
|
StatusCode,
|
||||||
};
|
};
|
||||||
use crate::message::{RequestHeadType, ResponseHead};
|
use crate::message::{RequestHeadType, ResponseHead};
|
||||||
use crate::payload::{Payload, PayloadStream};
|
use crate::payload::Payload;
|
||||||
|
|
||||||
use super::connection::{ConnectionIo, H1Connection};
|
use super::connection::{ConnectionIo, H1Connection};
|
||||||
use super::error::{ConnectError, SendRequestError};
|
use super::error::{ConnectError, SendRequestError};
|
||||||
|
@ -122,10 +122,7 @@ where
|
||||||
|
|
||||||
Ok((head, Payload::None))
|
Ok((head, Payload::None))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => Ok((head, Payload::Stream(Box::pin(PlStream::new(framed))))),
|
||||||
let pl: PayloadStream = Box::pin(PlStream::new(framed));
|
|
||||||
Ok((head, pl.into()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,21 +191,16 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pin_project::pin_project]
|
#[pin_project::pin_project]
|
||||||
pub(crate) struct PlStream<Io: ConnectionIo>
|
pub(crate) struct PlStream<Io: ConnectionIo> {
|
||||||
where
|
|
||||||
Io: ConnectionIo,
|
|
||||||
{
|
|
||||||
#[pin]
|
#[pin]
|
||||||
framed: Option<Framed<H1Connection<Io>, h1::ClientPayloadCodec>>,
|
framed: Framed<H1Connection<Io>, h1::ClientPayloadCodec>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Io: ConnectionIo> PlStream<Io> {
|
impl<Io: ConnectionIo> PlStream<Io> {
|
||||||
fn new(framed: Framed<H1Connection<Io>, h1::ClientCodec>) -> Self {
|
fn new(framed: Framed<H1Connection<Io>, h1::ClientCodec>) -> Self {
|
||||||
let framed = framed.into_map_codec(|codec| codec.into_payload_codec());
|
let framed = framed.into_map_codec(|codec| codec.into_payload_codec());
|
||||||
|
|
||||||
PlStream {
|
PlStream { framed }
|
||||||
framed: Some(framed),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,20 +211,16 @@ impl<Io: ConnectionIo> Stream for PlStream<Io> {
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<Self::Item>> {
|
) -> Poll<Option<Self::Item>> {
|
||||||
let mut framed = self.project().framed.as_pin_mut().unwrap();
|
let mut this = self.project();
|
||||||
|
|
||||||
match framed.as_mut().next_item(cx)? {
|
match ready!(this.framed.as_mut().next_item(cx)?) {
|
||||||
Poll::Pending => Poll::Pending,
|
Some(Some(chunk)) => Poll::Ready(Some(Ok(chunk))),
|
||||||
Poll::Ready(Some(chunk)) => {
|
Some(None) => {
|
||||||
if let Some(chunk) = chunk {
|
let keep_alive = this.framed.codec_ref().keepalive();
|
||||||
Poll::Ready(Some(Ok(chunk)))
|
this.framed.io_mut().on_release(keep_alive);
|
||||||
} else {
|
Poll::Ready(None)
|
||||||
let keep_alive = framed.codec_ref().keepalive();
|
|
||||||
framed.io_mut().on_release(keep_alive);
|
|
||||||
Poll::Ready(None)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Poll::Ready(None) => Poll::Ready(None),
|
None => Poll::Ready(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::{fmt, net};
|
||||||
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
||||||
use actix_rt::net::TcpStream;
|
use actix_rt::net::TcpStream;
|
||||||
use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory};
|
use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory};
|
||||||
use futures_core::{future::LocalBoxFuture, ready};
|
use futures_core::future::LocalBoxFuture;
|
||||||
use futures_util::future::ready;
|
use futures_util::future::ready;
|
||||||
|
|
||||||
use crate::body::MessageBody;
|
use crate::body::MessageBody;
|
||||||
|
@ -14,7 +14,7 @@ use crate::config::ServiceConfig;
|
||||||
use crate::error::{DispatchError, Error};
|
use crate::error::{DispatchError, Error};
|
||||||
use crate::request::Request;
|
use crate::request::Request;
|
||||||
use crate::response::Response;
|
use crate::response::Response;
|
||||||
use crate::service::HttpFlow;
|
use crate::service::HttpServiceHandler;
|
||||||
use crate::{ConnectCallback, OnConnectData};
|
use crate::{ConnectCallback, OnConnectData};
|
||||||
|
|
||||||
use super::codec::Codec;
|
use super::codec::Codec;
|
||||||
|
@ -315,47 +315,10 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Service` implementation for HTTP/1 transport
|
/// `Service` implementation for HTTP/1 transport
|
||||||
pub struct H1ServiceHandler<T, S, B, X, U>
|
pub type H1ServiceHandler<T, S, B, X, U> = HttpServiceHandler<T, S, B, X, U>;
|
||||||
where
|
|
||||||
S: Service<Request>,
|
|
||||||
X: Service<Request>,
|
|
||||||
U: Service<(Request, Framed<T, Codec>)>,
|
|
||||||
{
|
|
||||||
flow: Rc<HttpFlow<S, X, U>>,
|
|
||||||
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
|
|
||||||
cfg: ServiceConfig,
|
|
||||||
_phantom: PhantomData<B>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, S, B, X, U> H1ServiceHandler<T, S, B, X, U>
|
|
||||||
where
|
|
||||||
S: Service<Request>,
|
|
||||||
S::Error: Into<Error>,
|
|
||||||
S::Response: Into<Response<B>>,
|
|
||||||
B: MessageBody,
|
|
||||||
X: Service<Request, Response = Request>,
|
|
||||||
X::Error: Into<Error>,
|
|
||||||
U: Service<(Request, Framed<T, Codec>), Response = ()>,
|
|
||||||
U::Error: fmt::Display,
|
|
||||||
{
|
|
||||||
fn new(
|
|
||||||
cfg: ServiceConfig,
|
|
||||||
service: S,
|
|
||||||
expect: X,
|
|
||||||
upgrade: Option<U>,
|
|
||||||
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
|
|
||||||
) -> H1ServiceHandler<T, S, B, X, U> {
|
|
||||||
H1ServiceHandler {
|
|
||||||
flow: HttpFlow::new(service, expect, upgrade),
|
|
||||||
cfg,
|
|
||||||
on_connect_ext,
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, S, B, X, U> Service<(T, Option<net::SocketAddr>)>
|
impl<T, S, B, X, U> Service<(T, Option<net::SocketAddr>)>
|
||||||
for H1ServiceHandler<T, S, B, X, U>
|
for HttpServiceHandler<T, S, B, X, U>
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin,
|
T: AsyncRead + AsyncWrite + Unpin,
|
||||||
S: Service<Request>,
|
S: Service<Request>,
|
||||||
|
@ -372,27 +335,10 @@ where
|
||||||
type Future = Dispatcher<T, S, B, X, U>;
|
type Future = Dispatcher<T, S, B, X, U>;
|
||||||
|
|
||||||
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
ready!(self.flow.expect.poll_ready(cx)).map_err(|e| {
|
self._poll_ready(cx).map_err(|e| {
|
||||||
let e = e.into();
|
log::error!("HTTP/1 service readiness error: {:?}", e);
|
||||||
log::error!("Http expect service readiness error: {:?}", e);
|
|
||||||
DispatchError::Service(e)
|
DispatchError::Service(e)
|
||||||
})?;
|
})
|
||||||
|
|
||||||
if let Some(ref upg) = self.flow.upgrade {
|
|
||||||
ready!(upg.poll_ready(cx)).map_err(|e| {
|
|
||||||
let e = e.into();
|
|
||||||
log::error!("Http upgrade service readiness error: {:?}", e);
|
|
||||||
DispatchError::Service(e)
|
|
||||||
})?;
|
|
||||||
};
|
|
||||||
|
|
||||||
ready!(self.flow.service.poll_ready(cx)).map_err(|e| {
|
|
||||||
let e = e.into();
|
|
||||||
log::error!("Http service readiness error: {:?}", e);
|
|
||||||
DispatchError::Service(e)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&self, (io, addr): (T, Option<net::SocketAddr>)) -> Self::Future {
|
fn call(&self, (io, addr): (T, Option<net::SocketAddr>)) -> Self::Future {
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
use std::fmt::{self, Display};
|
use std::{
|
||||||
use std::io::Write;
|
fmt,
|
||||||
use std::str::FromStr;
|
io::Write,
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
str::FromStr,
|
||||||
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
|
};
|
||||||
|
|
||||||
use bytes::buf::BufMut;
|
use bytes::buf::BufMut;
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use http::header::{HeaderValue, InvalidHeaderValue};
|
use http::header::{HeaderValue, InvalidHeaderValue};
|
||||||
use time::{offset, OffsetDateTime, PrimitiveDateTime};
|
use time::{OffsetDateTime, PrimitiveDateTime, UtcOffset};
|
||||||
|
|
||||||
use crate::error::ParseError;
|
use crate::error::ParseError;
|
||||||
use crate::header::IntoHeaderValue;
|
use crate::header::IntoHeaderValue;
|
||||||
use crate::time_parser;
|
use crate::time_parser;
|
||||||
|
|
||||||
/// A timestamp with HTTP formatting and parsing
|
/// A timestamp with HTTP formatting and parsing.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct HttpDate(OffsetDateTime);
|
pub struct HttpDate(OffsetDateTime);
|
||||||
|
|
||||||
|
@ -27,18 +29,12 @@ impl FromStr for HttpDate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for HttpDate {
|
impl fmt::Display for HttpDate {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::Display::fmt(&self.0.format("%a, %d %b %Y %H:%M:%S GMT"), f)
|
fmt::Display::fmt(&self.0.format("%a, %d %b %Y %H:%M:%S GMT"), f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<OffsetDateTime> for HttpDate {
|
|
||||||
fn from(dt: OffsetDateTime) -> HttpDate {
|
|
||||||
HttpDate(dt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SystemTime> for HttpDate {
|
impl From<SystemTime> for HttpDate {
|
||||||
fn from(sys: SystemTime) -> HttpDate {
|
fn from(sys: SystemTime) -> HttpDate {
|
||||||
HttpDate(PrimitiveDateTime::from(sys).assume_utc())
|
HttpDate(PrimitiveDateTime::from(sys).assume_utc())
|
||||||
|
@ -54,7 +50,7 @@ impl IntoHeaderValue for HttpDate {
|
||||||
wrt,
|
wrt,
|
||||||
"{}",
|
"{}",
|
||||||
self.0
|
self.0
|
||||||
.to_offset(offset!(UTC))
|
.to_offset(UtcOffset::UTC)
|
||||||
.format("%a, %d %b %Y %H:%M:%S GMT")
|
.format("%a, %d %b %Y %H:%M:%S GMT")
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -12,7 +12,7 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
||||||
use actix_rt::net::TcpStream;
|
use actix_rt::net::TcpStream;
|
||||||
use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory};
|
use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_core::ready;
|
use futures_core::{future::LocalBoxFuture, ready};
|
||||||
use h2::server::{handshake, Handshake};
|
use h2::server::{handshake, Handshake};
|
||||||
use pin_project::pin_project;
|
use pin_project::pin_project;
|
||||||
|
|
||||||
|
@ -107,7 +107,6 @@ where
|
||||||
X1: ServiceFactory<Request, Config = (), Response = Request>,
|
X1: ServiceFactory<Request, Config = (), Response = Request>,
|
||||||
X1::Error: Into<Error>,
|
X1::Error: Into<Error>,
|
||||||
X1::InitError: fmt::Debug,
|
X1::InitError: fmt::Debug,
|
||||||
<X1::Service as Service<Request>>::Future: 'static,
|
|
||||||
{
|
{
|
||||||
HttpService {
|
HttpService {
|
||||||
expect,
|
expect,
|
||||||
|
@ -128,7 +127,6 @@ where
|
||||||
U1: ServiceFactory<(Request, Framed<T, h1::Codec>), Config = (), Response = ()>,
|
U1: ServiceFactory<(Request, Framed<T, h1::Codec>), Config = (), Response = ()>,
|
||||||
U1::Error: fmt::Display,
|
U1::Error: fmt::Display,
|
||||||
U1::InitError: fmt::Debug,
|
U1::InitError: fmt::Debug,
|
||||||
<U1::Service as Service<(Request, Framed<T, h1::Codec>)>>::Future: 'static,
|
|
||||||
{
|
{
|
||||||
HttpService {
|
HttpService {
|
||||||
upgrade,
|
upgrade,
|
||||||
|
@ -150,23 +148,24 @@ where
|
||||||
impl<S, B, X, U> HttpService<TcpStream, S, B, X, U>
|
impl<S, B, X, U> HttpService<TcpStream, S, B, X, U>
|
||||||
where
|
where
|
||||||
S: ServiceFactory<Request, Config = ()>,
|
S: ServiceFactory<Request, Config = ()>,
|
||||||
|
S::Future: 'static,
|
||||||
S::Error: Into<Error> + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
<S::Service as Service<Request>>::Future: 'static,
|
<S::Service as Service<Request>>::Future: 'static,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
X: ServiceFactory<Request, Config = (), Response = Request>,
|
X: ServiceFactory<Request, Config = (), Response = Request>,
|
||||||
|
X::Future: 'static,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::InitError: fmt::Debug,
|
X::InitError: fmt::Debug,
|
||||||
<X::Service as Service<Request>>::Future: 'static,
|
|
||||||
U: ServiceFactory<
|
U: ServiceFactory<
|
||||||
(Request, Framed<TcpStream, h1::Codec>),
|
(Request, Framed<TcpStream, h1::Codec>),
|
||||||
Config = (),
|
Config = (),
|
||||||
Response = (),
|
Response = (),
|
||||||
>,
|
>,
|
||||||
|
U::Future: 'static,
|
||||||
U::Error: fmt::Display + Into<Error>,
|
U::Error: fmt::Display + Into<Error>,
|
||||||
U::InitError: fmt::Debug,
|
U::InitError: fmt::Debug,
|
||||||
<U::Service as Service<(Request, Framed<TcpStream, h1::Codec>)>>::Future: 'static,
|
|
||||||
{
|
{
|
||||||
/// Create simple tcp stream service
|
/// Create simple tcp stream service
|
||||||
pub fn tcp(
|
pub fn tcp(
|
||||||
|
@ -196,23 +195,24 @@ mod openssl {
|
||||||
impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>
|
impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>
|
||||||
where
|
where
|
||||||
S: ServiceFactory<Request, Config = ()>,
|
S: ServiceFactory<Request, Config = ()>,
|
||||||
|
S::Future: 'static,
|
||||||
S::Error: Into<Error> + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
<S::Service as Service<Request>>::Future: 'static,
|
<S::Service as Service<Request>>::Future: 'static,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
X: ServiceFactory<Request, Config = (), Response = Request>,
|
X: ServiceFactory<Request, Config = (), Response = Request>,
|
||||||
|
X::Future: 'static,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::InitError: fmt::Debug,
|
X::InitError: fmt::Debug,
|
||||||
<X::Service as Service<Request>>::Future: 'static,
|
|
||||||
U: ServiceFactory<
|
U: ServiceFactory<
|
||||||
(Request, Framed<TlsStream<TcpStream>, h1::Codec>),
|
(Request, Framed<TlsStream<TcpStream>, h1::Codec>),
|
||||||
Config = (),
|
Config = (),
|
||||||
Response = (),
|
Response = (),
|
||||||
>,
|
>,
|
||||||
|
U::Future: 'static,
|
||||||
U::Error: fmt::Display + Into<Error>,
|
U::Error: fmt::Display + Into<Error>,
|
||||||
U::InitError: fmt::Debug,
|
U::InitError: fmt::Debug,
|
||||||
<U::Service as Service<(Request, Framed<TlsStream<TcpStream>, h1::Codec>)>>::Future: 'static,
|
|
||||||
{
|
{
|
||||||
/// Create openssl based service
|
/// Create openssl based service
|
||||||
pub fn openssl(
|
pub fn openssl(
|
||||||
|
@ -261,23 +261,24 @@ mod rustls {
|
||||||
impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>
|
impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>
|
||||||
where
|
where
|
||||||
S: ServiceFactory<Request, Config = ()>,
|
S: ServiceFactory<Request, Config = ()>,
|
||||||
|
S::Future: 'static,
|
||||||
S::Error: Into<Error> + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
<S::Service as Service<Request>>::Future: 'static,
|
<S::Service as Service<Request>>::Future: 'static,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
X: ServiceFactory<Request, Config = (), Response = Request>,
|
X: ServiceFactory<Request, Config = (), Response = Request>,
|
||||||
|
X::Future: 'static,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::InitError: fmt::Debug,
|
X::InitError: fmt::Debug,
|
||||||
<X::Service as Service<Request>>::Future: 'static,
|
|
||||||
U: ServiceFactory<
|
U: ServiceFactory<
|
||||||
(Request, Framed<TlsStream<TcpStream>, h1::Codec>),
|
(Request, Framed<TlsStream<TcpStream>, h1::Codec>),
|
||||||
Config = (),
|
Config = (),
|
||||||
Response = (),
|
Response = (),
|
||||||
>,
|
>,
|
||||||
|
U::Future: 'static,
|
||||||
U::Error: fmt::Display + Into<Error>,
|
U::Error: fmt::Display + Into<Error>,
|
||||||
U::InitError: fmt::Debug,
|
U::InitError: fmt::Debug,
|
||||||
<U::Service as Service<(Request, Framed<TlsStream<TcpStream>, h1::Codec>)>>::Future: 'static,
|
|
||||||
{
|
{
|
||||||
/// Create openssl based service
|
/// Create openssl based service
|
||||||
pub fn rustls(
|
pub fn rustls(
|
||||||
|
@ -319,137 +320,117 @@ mod rustls {
|
||||||
impl<T, S, B, X, U> ServiceFactory<(T, Protocol, Option<net::SocketAddr>)>
|
impl<T, S, B, X, U> ServiceFactory<(T, Protocol, Option<net::SocketAddr>)>
|
||||||
for HttpService<T, S, B, X, U>
|
for HttpService<T, S, B, X, U>
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin,
|
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
S: ServiceFactory<Request, Config = ()>,
|
S: ServiceFactory<Request, Config = ()>,
|
||||||
|
S::Future: 'static,
|
||||||
S::Error: Into<Error> + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
<S::Service as Service<Request>>::Future: 'static,
|
<S::Service as Service<Request>>::Future: 'static,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
X: ServiceFactory<Request, Config = (), Response = Request>,
|
X: ServiceFactory<Request, Config = (), Response = Request>,
|
||||||
|
X::Future: 'static,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::InitError: fmt::Debug,
|
X::InitError: fmt::Debug,
|
||||||
<X::Service as Service<Request>>::Future: 'static,
|
|
||||||
U: ServiceFactory<(Request, Framed<T, h1::Codec>), Config = (), Response = ()>,
|
U: ServiceFactory<(Request, Framed<T, h1::Codec>), Config = (), Response = ()>,
|
||||||
|
U::Future: 'static,
|
||||||
U::Error: fmt::Display + Into<Error>,
|
U::Error: fmt::Display + Into<Error>,
|
||||||
U::InitError: fmt::Debug,
|
U::InitError: fmt::Debug,
|
||||||
<U::Service as Service<(Request, Framed<T, h1::Codec>)>>::Future: 'static,
|
|
||||||
{
|
{
|
||||||
type Response = ();
|
type Response = ();
|
||||||
type Error = DispatchError;
|
type Error = DispatchError;
|
||||||
type Config = ();
|
type Config = ();
|
||||||
type Service = HttpServiceHandler<T, S::Service, B, X::Service, U::Service>;
|
type Service = HttpServiceHandler<T, S::Service, B, X::Service, U::Service>;
|
||||||
type InitError = ();
|
type InitError = ();
|
||||||
type Future = HttpServiceResponse<T, S, B, X, U>;
|
type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
|
||||||
|
|
||||||
fn new_service(&self, _: ()) -> Self::Future {
|
fn new_service(&self, _: ()) -> Self::Future {
|
||||||
HttpServiceResponse {
|
let service = self.srv.new_service(());
|
||||||
fut: self.srv.new_service(()),
|
let expect = self.expect.new_service(());
|
||||||
fut_ex: Some(self.expect.new_service(())),
|
let upgrade = self.upgrade.as_ref().map(|s| s.new_service(()));
|
||||||
fut_upg: self.upgrade.as_ref().map(|f| f.new_service(())),
|
let on_connect_ext = self.on_connect_ext.clone();
|
||||||
expect: None,
|
let cfg = self.cfg.clone();
|
||||||
upgrade: None,
|
|
||||||
on_connect_ext: self.on_connect_ext.clone(),
|
|
||||||
cfg: self.cfg.clone(),
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
Box::pin(async move {
|
||||||
#[pin_project]
|
let expect = expect
|
||||||
pub struct HttpServiceResponse<T, S, B, X, U>
|
.await
|
||||||
where
|
.map_err(|e| log::error!("Init http expect service error: {:?}", e))?;
|
||||||
S: ServiceFactory<Request>,
|
|
||||||
X: ServiceFactory<Request>,
|
|
||||||
U: ServiceFactory<(Request, Framed<T, h1::Codec>)>,
|
|
||||||
{
|
|
||||||
#[pin]
|
|
||||||
fut: S::Future,
|
|
||||||
#[pin]
|
|
||||||
fut_ex: Option<X::Future>,
|
|
||||||
#[pin]
|
|
||||||
fut_upg: Option<U::Future>,
|
|
||||||
expect: Option<X::Service>,
|
|
||||||
upgrade: Option<U::Service>,
|
|
||||||
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
|
|
||||||
cfg: ServiceConfig,
|
|
||||||
_phantom: PhantomData<B>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, S, B, X, U> Future for HttpServiceResponse<T, S, B, X, U>
|
let upgrade = match upgrade {
|
||||||
where
|
Some(upgrade) => {
|
||||||
T: AsyncRead + AsyncWrite + Unpin,
|
let upgrade = upgrade.await.map_err(|e| {
|
||||||
S: ServiceFactory<Request>,
|
log::error!("Init http upgrade service error: {:?}", e)
|
||||||
S::Error: Into<Error> + 'static,
|
})?;
|
||||||
S::InitError: fmt::Debug,
|
Some(upgrade)
|
||||||
S::Response: Into<Response<B>> + 'static,
|
}
|
||||||
<S::Service as Service<Request>>::Future: 'static,
|
None => None,
|
||||||
B: MessageBody + 'static,
|
};
|
||||||
X: ServiceFactory<Request, Response = Request>,
|
|
||||||
X::Error: Into<Error>,
|
|
||||||
X::InitError: fmt::Debug,
|
|
||||||
<X::Service as Service<Request>>::Future: 'static,
|
|
||||||
U: ServiceFactory<(Request, Framed<T, h1::Codec>), Response = ()>,
|
|
||||||
U::Error: fmt::Display,
|
|
||||||
U::InitError: fmt::Debug,
|
|
||||||
<U::Service as Service<(Request, Framed<T, h1::Codec>)>>::Future: 'static,
|
|
||||||
{
|
|
||||||
type Output =
|
|
||||||
Result<HttpServiceHandler<T, S::Service, B, X::Service, U::Service>, ()>;
|
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
let service = service
|
||||||
let mut this = self.as_mut().project();
|
.await
|
||||||
|
.map_err(|e| log::error!("Init http service error: {:?}", e))?;
|
||||||
|
|
||||||
if let Some(fut) = this.fut_ex.as_pin_mut() {
|
Ok(HttpServiceHandler::new(
|
||||||
let expect = ready!(fut
|
cfg,
|
||||||
.poll(cx)
|
|
||||||
.map_err(|e| log::error!("Init http service error: {:?}", e)))?;
|
|
||||||
this = self.as_mut().project();
|
|
||||||
*this.expect = Some(expect);
|
|
||||||
this.fut_ex.set(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(fut) = this.fut_upg.as_pin_mut() {
|
|
||||||
let upgrade = ready!(fut
|
|
||||||
.poll(cx)
|
|
||||||
.map_err(|e| log::error!("Init http service error: {:?}", e)))?;
|
|
||||||
this = self.as_mut().project();
|
|
||||||
*this.upgrade = Some(upgrade);
|
|
||||||
this.fut_upg.set(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = ready!(this
|
|
||||||
.fut
|
|
||||||
.poll(cx)
|
|
||||||
.map_err(|e| log::error!("Init http service error: {:?}", e)));
|
|
||||||
|
|
||||||
Poll::Ready(result.map(|service| {
|
|
||||||
let this = self.as_mut().project();
|
|
||||||
HttpServiceHandler::new(
|
|
||||||
this.cfg.clone(),
|
|
||||||
service,
|
service,
|
||||||
this.expect.take().unwrap(),
|
expect,
|
||||||
this.upgrade.take(),
|
upgrade,
|
||||||
this.on_connect_ext.clone(),
|
on_connect_ext,
|
||||||
)
|
))
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Service` implementation for HTTP transport
|
/// `Service` implementation for HTTP/1 and HTTP/2 transport
|
||||||
pub struct HttpServiceHandler<T, S, B, X, U>
|
pub struct HttpServiceHandler<T, S, B, X, U>
|
||||||
where
|
where
|
||||||
S: Service<Request>,
|
S: Service<Request>,
|
||||||
X: Service<Request>,
|
X: Service<Request>,
|
||||||
U: Service<(Request, Framed<T, h1::Codec>)>,
|
U: Service<(Request, Framed<T, h1::Codec>)>,
|
||||||
{
|
{
|
||||||
flow: Rc<HttpFlow<S, X, U>>,
|
pub(super) flow: Rc<HttpFlow<S, X, U>>,
|
||||||
cfg: ServiceConfig,
|
pub(super) cfg: ServiceConfig,
|
||||||
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
|
pub(super) on_connect_ext: Option<Rc<ConnectCallback<T>>>,
|
||||||
_phantom: PhantomData<B>,
|
_phantom: PhantomData<B>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T, S, B, X, U> HttpServiceHandler<T, S, B, X, U>
|
||||||
|
where
|
||||||
|
S: Service<Request>,
|
||||||
|
S::Error: Into<Error>,
|
||||||
|
X: Service<Request>,
|
||||||
|
X::Error: Into<Error>,
|
||||||
|
U: Service<(Request, Framed<T, h1::Codec>)>,
|
||||||
|
U::Error: Into<Error>,
|
||||||
|
{
|
||||||
|
pub(super) fn new(
|
||||||
|
cfg: ServiceConfig,
|
||||||
|
service: S,
|
||||||
|
expect: X,
|
||||||
|
upgrade: Option<U>,
|
||||||
|
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
|
||||||
|
) -> HttpServiceHandler<T, S, B, X, U> {
|
||||||
|
HttpServiceHandler {
|
||||||
|
cfg,
|
||||||
|
on_connect_ext,
|
||||||
|
flow: HttpFlow::new(service, expect, upgrade),
|
||||||
|
_phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn _poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
|
||||||
|
ready!(self.flow.expect.poll_ready(cx).map_err(Into::into))?;
|
||||||
|
|
||||||
|
ready!(self.flow.service.poll_ready(cx).map_err(Into::into))?;
|
||||||
|
|
||||||
|
if let Some(ref upg) = self.flow.upgrade {
|
||||||
|
ready!(upg.poll_ready(cx).map_err(Into::into))?;
|
||||||
|
};
|
||||||
|
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A collection of services that describe an HTTP request flow.
|
/// A collection of services that describe an HTTP request flow.
|
||||||
pub(super) struct HttpFlow<S, X, U> {
|
pub(super) struct HttpFlow<S, X, U> {
|
||||||
pub(super) service: S,
|
pub(super) service: S,
|
||||||
|
@ -467,34 +448,6 @@ impl<S, X, U> HttpFlow<S, X, U> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, S, B, X, U> HttpServiceHandler<T, S, B, X, U>
|
|
||||||
where
|
|
||||||
S: Service<Request>,
|
|
||||||
S::Error: Into<Error> + 'static,
|
|
||||||
S::Future: 'static,
|
|
||||||
S::Response: Into<Response<B>> + 'static,
|
|
||||||
B: MessageBody + 'static,
|
|
||||||
X: Service<Request, Response = Request>,
|
|
||||||
X::Error: Into<Error>,
|
|
||||||
U: Service<(Request, Framed<T, h1::Codec>), Response = ()>,
|
|
||||||
U::Error: fmt::Display,
|
|
||||||
{
|
|
||||||
fn new(
|
|
||||||
cfg: ServiceConfig,
|
|
||||||
service: S,
|
|
||||||
expect: X,
|
|
||||||
upgrade: Option<U>,
|
|
||||||
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
|
|
||||||
) -> HttpServiceHandler<T, S, B, X, U> {
|
|
||||||
HttpServiceHandler {
|
|
||||||
cfg,
|
|
||||||
on_connect_ext,
|
|
||||||
flow: HttpFlow::new(service, expect, upgrade),
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, S, B, X, U> Service<(T, Protocol, Option<net::SocketAddr>)>
|
impl<T, S, B, X, U> Service<(T, Protocol, Option<net::SocketAddr>)>
|
||||||
for HttpServiceHandler<T, S, B, X, U>
|
for HttpServiceHandler<T, S, B, X, U>
|
||||||
where
|
where
|
||||||
|
@ -514,47 +467,10 @@ where
|
||||||
type Future = HttpServiceHandlerResponse<T, S, B, X, U>;
|
type Future = HttpServiceHandlerResponse<T, S, B, X, U>;
|
||||||
|
|
||||||
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
let ready = self
|
self._poll_ready(cx).map_err(|e| {
|
||||||
.flow
|
log::error!("HTTP service readiness error: {:?}", e);
|
||||||
.expect
|
DispatchError::Service(e)
|
||||||
.poll_ready(cx)
|
})
|
||||||
.map_err(|e| {
|
|
||||||
let e = e.into();
|
|
||||||
log::error!("Http service readiness error: {:?}", e);
|
|
||||||
DispatchError::Service(e)
|
|
||||||
})?
|
|
||||||
.is_ready();
|
|
||||||
|
|
||||||
let ready = self
|
|
||||||
.flow
|
|
||||||
.service
|
|
||||||
.poll_ready(cx)
|
|
||||||
.map_err(|e| {
|
|
||||||
let e = e.into();
|
|
||||||
log::error!("Http service readiness error: {:?}", e);
|
|
||||||
DispatchError::Service(e)
|
|
||||||
})?
|
|
||||||
.is_ready()
|
|
||||||
&& ready;
|
|
||||||
|
|
||||||
let ready = if let Some(ref upg) = self.flow.upgrade {
|
|
||||||
upg.poll_ready(cx)
|
|
||||||
.map_err(|e| {
|
|
||||||
let e = e.into();
|
|
||||||
log::error!("Http service readiness error: {:?}", e);
|
|
||||||
DispatchError::Service(e)
|
|
||||||
})?
|
|
||||||
.is_ready()
|
|
||||||
&& ready
|
|
||||||
} else {
|
|
||||||
ready
|
|
||||||
};
|
|
||||||
|
|
||||||
if ready {
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
} else {
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(
|
fn call(
|
||||||
|
|
|
@ -29,5 +29,6 @@ tokio = { version = "1", features = ["sync"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
|
awc = { version = "3.0.0-beta.3", default-features = false }
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
futures-util = { version = "0.3.7", default-features = false }
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use actix_web::{test, web, App, HttpRequest};
|
use actix_web::{
|
||||||
|
http::{header, StatusCode},
|
||||||
|
test, web, App, HttpRequest, HttpResponse,
|
||||||
|
};
|
||||||
use actix_web_actors::*;
|
use actix_web_actors::*;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_util::{SinkExt, StreamExt};
|
use futures_util::{SinkExt, StreamExt};
|
||||||
|
@ -56,3 +59,51 @@ async fn test_simple() {
|
||||||
let item = framed.next().await.unwrap().unwrap();
|
let item = framed.next().await.unwrap().unwrap();
|
||||||
assert_eq!(item, ws::Frame::Close(Some(ws::CloseCode::Normal.into())));
|
assert_eq!(item, ws::Frame::Close(Some(ws::CloseCode::Normal.into())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_with_credentials() {
|
||||||
|
let mut srv = test::start(|| {
|
||||||
|
App::new().service(web::resource("/").to(
|
||||||
|
|req: HttpRequest, stream: web::Payload| async move {
|
||||||
|
if req.headers().contains_key("Authorization") {
|
||||||
|
ws::start(Ws, &req, stream)
|
||||||
|
} else {
|
||||||
|
Ok(HttpResponse::new(StatusCode::UNAUTHORIZED))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
))
|
||||||
|
});
|
||||||
|
|
||||||
|
// client service without credentials
|
||||||
|
match srv.ws().await {
|
||||||
|
Ok(_) => panic!("WebSocket client without credentials should panic"),
|
||||||
|
Err(awc::error::WsClientError::InvalidResponseStatus(status)) => {
|
||||||
|
assert_eq!(status, StatusCode::UNAUTHORIZED)
|
||||||
|
}
|
||||||
|
Err(e) => panic!("Invalid error from WebSocket client: {}", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
let headers = srv.client_headers().unwrap();
|
||||||
|
headers.insert(
|
||||||
|
header::AUTHORIZATION,
|
||||||
|
header::HeaderValue::from_static("Bearer Something"),
|
||||||
|
);
|
||||||
|
|
||||||
|
// client service with credentials
|
||||||
|
let client = srv.ws();
|
||||||
|
|
||||||
|
let mut framed = client.await.unwrap();
|
||||||
|
|
||||||
|
framed.send(ws::Message::Text("text".into())).await.unwrap();
|
||||||
|
|
||||||
|
let item = framed.next().await.unwrap().unwrap();
|
||||||
|
assert_eq!(item, ws::Frame::Text(Bytes::from_static(b"text")));
|
||||||
|
|
||||||
|
framed
|
||||||
|
.send(ws::Message::Close(Some(ws::CloseCode::Normal.into())))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let item = framed.next().await.unwrap().unwrap();
|
||||||
|
assert_eq!(item, ws::Frame::Close(Some(ws::CloseCode::Normal.into())));
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
### Added
|
||||||
|
* Add `Client::headers` to get default mut reference of `HeaderMap` of client object. [#2114]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* `ConnectorService` type is renamed to `BoxConnectorService`. [#2081]
|
* `ConnectorService` type is renamed to `BoxConnectorService`. [#2081]
|
||||||
* Fix http/https encoding when enabling `compress` feature. [#2116]
|
* Fix http/https encoding when enabling `compress` feature. [#2116]
|
||||||
|
|
||||||
[#2081]: https://github.com/actix/actix-web/pull/2081
|
[#2081]: https://github.com/actix/actix-web/pull/2081
|
||||||
|
[#2114]: https://github.com/actix/actix-web/pull/2114
|
||||||
[#2116]: https://github.com/actix/actix-web/pull/2116
|
[#2116]: https://github.com/actix/actix-web/pull/2116
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -286,4 +286,12 @@ impl Client {
|
||||||
}
|
}
|
||||||
req
|
req
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get default HeaderMap of Client.
|
||||||
|
///
|
||||||
|
/// Returns Some(&mut HeaderMap) when Client object is unique
|
||||||
|
/// (No other clone of client exists at the same time).
|
||||||
|
pub fn headers(&mut self) -> Option<&mut HeaderMap> {
|
||||||
|
Rc::get_mut(&mut self.0.headers)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ digraph {
|
||||||
}
|
}
|
||||||
|
|
||||||
"actix-codec" -> { "actix-rt" "actix-service" "local-channel" "tokio" }
|
"actix-codec" -> { "actix-rt" "actix-service" "local-channel" "tokio" }
|
||||||
"actix-utils" -> { "actix-rt" "actix-service" "local-waker" }
|
"actix-utils" -> { "local-waker" }
|
||||||
"actix-tracing" -> { "actix-service" }
|
"actix-tracing" -> { "actix-service" }
|
||||||
"actix-tls" -> { "actix-service" "actix-codec" "actix-utils" "actix-rt" }
|
"actix-tls" -> { "actix-service" "actix-codec" "actix-utils" "actix-rt" }
|
||||||
"actix-server" -> { "actix-service" "actix-rt" "actix-codec" "actix-utils" "tokio" }
|
"actix-server" -> { "actix-service" "actix-rt" "actix-codec" "actix-utils" "tokio" }
|
||||||
|
|
|
@ -970,6 +970,14 @@ impl TestServer {
|
||||||
self.ws_at("/").await
|
self.ws_at("/").await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get default HeaderMap of Client.
|
||||||
|
///
|
||||||
|
/// Returns Some(&mut HeaderMap) when Client object is unique
|
||||||
|
/// (No other clone of client exists at the same time).
|
||||||
|
pub fn client_headers(&mut self) -> Option<&mut HeaderMap> {
|
||||||
|
self.client.headers()
|
||||||
|
}
|
||||||
|
|
||||||
/// Gracefully stop HTTP server
|
/// Gracefully stop HTTP server
|
||||||
pub async fn stop(self) {
|
pub async fn stop(self) {
|
||||||
self.server.stop(true).await;
|
self.server.stop(true).await;
|
||||||
|
|
Loading…
Reference in New Issue