mirror of https://github.com/fafhrd91/actix-web
Merge branch 'master' into default_error_handler
This commit is contained in:
commit
2781d70849
|
@ -481,6 +481,7 @@ mod tests {
|
||||||
assert_poll_next_none!(pl);
|
assert_poll_next_none!(pl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::let_unit_value)]
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_unit() {
|
async fn test_unit() {
|
||||||
let pl = ();
|
let pl = ();
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
)]
|
)]
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
|
|
||||||
pub use ::http::{uri, uri::Uri};
|
pub use ::http::{uri, uri::Uri};
|
||||||
pub use ::http::{Method, StatusCode, Version};
|
pub use ::http::{Method, StatusCode, Version};
|
||||||
|
@ -69,6 +70,8 @@ pub use self::payload::{BoxedPayloadStream, Payload, PayloadStream};
|
||||||
pub use self::requests::{Request, RequestHead, RequestHeadType};
|
pub use self::requests::{Request, RequestHead, RequestHeadType};
|
||||||
pub use self::responses::{Response, ResponseBuilder, ResponseHead};
|
pub use self::responses::{Response, ResponseBuilder, ResponseHead};
|
||||||
pub use self::service::HttpService;
|
pub use self::service::HttpService;
|
||||||
|
#[cfg(any(feature = "openssl", feature = "rustls"))]
|
||||||
|
pub use self::service::TlsAcceptorConfig;
|
||||||
|
|
||||||
/// A major HTTP protocol version.
|
/// A major HTTP protocol version.
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
|
|
@ -237,7 +237,7 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
|
let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
|
||||||
let _ = stream
|
stream
|
||||||
.write_all(b"GET /camel HTTP/1.1\r\nConnection: Close\r\n\r\n")
|
.write_all(b"GET /camel HTTP/1.1\r\nConnection: Close\r\n\r\n")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut data = vec![];
|
let mut data = vec![];
|
||||||
|
@ -251,7 +251,7 @@ mod tests {
|
||||||
assert!(memmem::find(&data, b"content-length").is_none());
|
assert!(memmem::find(&data, b"content-length").is_none());
|
||||||
|
|
||||||
let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
|
let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
|
||||||
let _ = stream
|
stream
|
||||||
.write_all(b"GET /lower HTTP/1.1\r\nConnection: Close\r\n\r\n")
|
.write_all(b"GET /lower HTTP/1.1\r\nConnection: Close\r\n\r\n")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut data = vec![];
|
let mut data = vec![];
|
||||||
|
|
|
@ -181,6 +181,25 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Configuration options used when accepting TLS connection.
|
||||||
|
#[cfg(any(feature = "openssl", feature = "rustls"))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "openssl", feature = "rustls"))))]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TlsAcceptorConfig {
|
||||||
|
pub(crate) handshake_timeout: Option<std::time::Duration>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "openssl", feature = "rustls"))]
|
||||||
|
impl TlsAcceptorConfig {
|
||||||
|
/// Set TLS handshake timeout duration.
|
||||||
|
pub fn handshake_timeout(self, dur: std::time::Duration) -> Self {
|
||||||
|
Self {
|
||||||
|
handshake_timeout: Some(dur),
|
||||||
|
// ..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
mod openssl {
|
mod openssl {
|
||||||
use actix_service::ServiceFactoryExt as _;
|
use actix_service::ServiceFactoryExt as _;
|
||||||
|
@ -230,7 +249,28 @@ mod openssl {
|
||||||
Error = TlsError<SslError, DispatchError>,
|
Error = TlsError<SslError, DispatchError>,
|
||||||
InitError = (),
|
InitError = (),
|
||||||
> {
|
> {
|
||||||
Acceptor::new(acceptor)
|
self.openssl_with_config(acceptor, TlsAcceptorConfig::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create OpenSSL based service with custom TLS acceptor configuration.
|
||||||
|
pub fn openssl_with_config(
|
||||||
|
self,
|
||||||
|
acceptor: SslAcceptor,
|
||||||
|
tls_acceptor_config: TlsAcceptorConfig,
|
||||||
|
) -> impl ServiceFactory<
|
||||||
|
TcpStream,
|
||||||
|
Config = (),
|
||||||
|
Response = (),
|
||||||
|
Error = TlsError<SslError, DispatchError>,
|
||||||
|
InitError = (),
|
||||||
|
> {
|
||||||
|
let mut acceptor = Acceptor::new(acceptor);
|
||||||
|
|
||||||
|
if let Some(handshake_timeout) = tls_acceptor_config.handshake_timeout {
|
||||||
|
acceptor.set_handshake_timeout(handshake_timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
acceptor
|
||||||
.map_init_err(|_| {
|
.map_init_err(|_| {
|
||||||
unreachable!("TLS acceptor service factory does not error on init")
|
unreachable!("TLS acceptor service factory does not error on init")
|
||||||
})
|
})
|
||||||
|
@ -293,8 +333,23 @@ mod rustls {
|
||||||
{
|
{
|
||||||
/// Create Rustls based service.
|
/// Create Rustls based service.
|
||||||
pub fn rustls(
|
pub fn rustls(
|
||||||
|
self,
|
||||||
|
config: ServerConfig,
|
||||||
|
) -> impl ServiceFactory<
|
||||||
|
TcpStream,
|
||||||
|
Config = (),
|
||||||
|
Response = (),
|
||||||
|
Error = TlsError<io::Error, DispatchError>,
|
||||||
|
InitError = (),
|
||||||
|
> {
|
||||||
|
self.rustls_with_config(config, TlsAcceptorConfig::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create Rustls based service with custom TLS acceptor configuration.
|
||||||
|
pub fn rustls_with_config(
|
||||||
self,
|
self,
|
||||||
mut config: ServerConfig,
|
mut config: ServerConfig,
|
||||||
|
tls_acceptor_config: TlsAcceptorConfig,
|
||||||
) -> impl ServiceFactory<
|
) -> impl ServiceFactory<
|
||||||
TcpStream,
|
TcpStream,
|
||||||
Config = (),
|
Config = (),
|
||||||
|
@ -306,7 +361,13 @@ mod rustls {
|
||||||
protos.extend_from_slice(&config.alpn_protocols);
|
protos.extend_from_slice(&config.alpn_protocols);
|
||||||
config.alpn_protocols = protos;
|
config.alpn_protocols = protos;
|
||||||
|
|
||||||
Acceptor::new(config)
|
let mut acceptor = Acceptor::new(config);
|
||||||
|
|
||||||
|
if let Some(handshake_timeout) = tls_acceptor_config.handshake_timeout {
|
||||||
|
acceptor.set_handshake_timeout(handshake_timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
acceptor
|
||||||
.map_init_err(|_| {
|
.map_init_err(|_| {
|
||||||
unreachable!("TLS acceptor service factory does not error on init")
|
unreachable!("TLS acceptor service factory does not error on init")
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
extern crate tls_openssl as openssl;
|
extern crate tls_openssl as openssl;
|
||||||
|
|
||||||
use std::{convert::Infallible, io};
|
use std::{convert::Infallible, io, time::Duration};
|
||||||
|
|
||||||
use actix_http::{
|
use actix_http::{
|
||||||
body::{BodyStream, BoxBody, SizedStream},
|
body::{BodyStream, BoxBody, SizedStream},
|
||||||
error::PayloadError,
|
error::PayloadError,
|
||||||
header::{self, HeaderValue},
|
header::{self, HeaderValue},
|
||||||
Error, HttpService, Method, Request, Response, StatusCode, Version,
|
Error, HttpService, Method, Request, Response, StatusCode, TlsAcceptorConfig, Version,
|
||||||
};
|
};
|
||||||
use actix_http_test::test_server;
|
use actix_http_test::test_server;
|
||||||
use actix_service::{fn_service, ServiceFactoryExt};
|
use actix_service::{fn_service, ServiceFactoryExt};
|
||||||
|
@ -89,7 +89,10 @@ async fn h2_1() -> io::Result<()> {
|
||||||
assert_eq!(req.version(), Version::HTTP_2);
|
assert_eq!(req.version(), Version::HTTP_2);
|
||||||
ok::<_, Error>(Response::ok())
|
ok::<_, Error>(Response::ok())
|
||||||
})
|
})
|
||||||
.openssl(tls_config())
|
.openssl_with_config(
|
||||||
|
tls_config(),
|
||||||
|
TlsAcceptorConfig::default().handshake_timeout(Duration::from_secs(5)),
|
||||||
|
)
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
|
@ -8,13 +8,14 @@ use std::{
|
||||||
net::{SocketAddr, TcpStream as StdTcpStream},
|
net::{SocketAddr, TcpStream as StdTcpStream},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
task::Poll,
|
task::Poll,
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_http::{
|
use actix_http::{
|
||||||
body::{BodyStream, BoxBody, SizedStream},
|
body::{BodyStream, BoxBody, SizedStream},
|
||||||
error::PayloadError,
|
error::PayloadError,
|
||||||
header::{self, HeaderName, HeaderValue},
|
header::{self, HeaderName, HeaderValue},
|
||||||
Error, HttpService, Method, Request, Response, StatusCode, Version,
|
Error, HttpService, Method, Request, Response, StatusCode, TlsAcceptorConfig, Version,
|
||||||
};
|
};
|
||||||
use actix_http_test::test_server;
|
use actix_http_test::test_server;
|
||||||
use actix_rt::pin;
|
use actix_rt::pin;
|
||||||
|
@ -160,7 +161,10 @@ async fn h2_1() -> io::Result<()> {
|
||||||
assert_eq!(req.version(), Version::HTTP_2);
|
assert_eq!(req.version(), Version::HTTP_2);
|
||||||
ok::<_, Error>(Response::ok())
|
ok::<_, Error>(Response::ok())
|
||||||
})
|
})
|
||||||
.rustls(tls_config())
|
.rustls_with_config(
|
||||||
|
tls_config(),
|
||||||
|
TlsAcceptorConfig::default().handshake_timeout(Duration::from_secs(5)),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,9 @@ tokio = { version = "1.13.1", features = ["sync"] }
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
actix-test = "0.1.0-beta.13"
|
actix-test = "0.1.0-beta.13"
|
||||||
awc = { version = "3", default-features = false }
|
awc = { version = "3", default-features = false }
|
||||||
|
actix-web = { version = "4", features = ["macros"] }
|
||||||
|
|
||||||
|
mime = "0.3"
|
||||||
|
|
||||||
env_logger = "0.9"
|
env_logger = "0.9"
|
||||||
futures-util = { version = "0.3.7", default-features = false }
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
|
|
|
@ -14,6 +14,58 @@ use futures_core::Stream;
|
||||||
use tokio::sync::oneshot::Sender;
|
use tokio::sync::oneshot::Sender;
|
||||||
|
|
||||||
/// Execution context for HTTP actors
|
/// Execution context for HTTP actors
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// A demonstration of [server-sent events](https://developer.mozilla.org/docs/Web/API/Server-sent_events) using actors:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::time::Duration;
|
||||||
|
///
|
||||||
|
/// use actix::{Actor, AsyncContext};
|
||||||
|
/// use actix_web::{get, http::header, App, HttpResponse, HttpServer};
|
||||||
|
/// use actix_web_actors::HttpContext;
|
||||||
|
/// use bytes::Bytes;
|
||||||
|
///
|
||||||
|
/// struct MyActor {
|
||||||
|
/// count: usize,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl Actor for MyActor {
|
||||||
|
/// type Context = HttpContext<Self>;
|
||||||
|
///
|
||||||
|
/// fn started(&mut self, ctx: &mut Self::Context) {
|
||||||
|
/// ctx.run_later(Duration::from_millis(100), Self::write);
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl MyActor {
|
||||||
|
/// fn write(&mut self, ctx: &mut HttpContext<Self>) {
|
||||||
|
/// self.count += 1;
|
||||||
|
/// if self.count > 3 {
|
||||||
|
/// ctx.write_eof()
|
||||||
|
/// } else {
|
||||||
|
/// ctx.write(Bytes::from(format!("event: count\ndata: {}\n\n", self.count)));
|
||||||
|
/// ctx.run_later(Duration::from_millis(100), Self::write);
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #[get("/")]
|
||||||
|
/// async fn index() -> HttpResponse {
|
||||||
|
/// HttpResponse::Ok()
|
||||||
|
/// .insert_header(header::ContentType(mime::TEXT_EVENT_STREAM))
|
||||||
|
/// .streaming(HttpContext::create(MyActor { count: 0 }))
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #[actix_web::main]
|
||||||
|
/// async fn main() -> std::io::Result<()> {
|
||||||
|
/// HttpServer::new(|| App::new().service(index))
|
||||||
|
/// .bind(("127.0.0.1", 8080))?
|
||||||
|
/// .run()
|
||||||
|
/// .await
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
pub struct HttpContext<A>
|
pub struct HttpContext<A>
|
||||||
where
|
where
|
||||||
A: Actor<Context = HttpContext<A>>,
|
A: Actor<Context = HttpContext<A>>,
|
||||||
|
@ -210,7 +262,7 @@ mod tests {
|
||||||
type Context = HttpContext<Self>;
|
type Context = HttpContext<Self>;
|
||||||
|
|
||||||
fn started(&mut self, ctx: &mut Self::Context) {
|
fn started(&mut self, ctx: &mut Self::Context) {
|
||||||
ctx.run_later(Duration::from_millis(100), |slf, ctx| slf.write(ctx));
|
ctx.run_later(Duration::from_millis(100), Self::write);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,7 +273,7 @@ mod tests {
|
||||||
ctx.write_eof()
|
ctx.write_eof()
|
||||||
} else {
|
} else {
|
||||||
ctx.write(Bytes::from(format!("LINE-{}", self.count)));
|
ctx.write(Bytes::from(format!("LINE-{}", self.count)));
|
||||||
ctx.run_later(Duration::from_millis(100), |slf, ctx| slf.write(ctx));
|
ctx.run_later(Duration::from_millis(100), Self::write);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,59 @@
|
||||||
//! Actix actors support for Actix Web.
|
//! Actix actors support for Actix Web.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use actix::{Actor, StreamHandler};
|
||||||
|
//! use actix_web::{get, web, App, Error, HttpRequest, HttpResponse, HttpServer};
|
||||||
|
//! use actix_web_actors::ws;
|
||||||
|
//!
|
||||||
|
//! /// Define Websocket actor
|
||||||
|
//! struct MyWs;
|
||||||
|
//!
|
||||||
|
//! impl Actor for MyWs {
|
||||||
|
//! type Context = ws::WebsocketContext<Self>;
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! /// Handler for ws::Message message
|
||||||
|
//! impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWs {
|
||||||
|
//! fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
|
||||||
|
//! match msg {
|
||||||
|
//! Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),
|
||||||
|
//! Ok(ws::Message::Text(text)) => ctx.text(text),
|
||||||
|
//! Ok(ws::Message::Binary(bin)) => ctx.binary(bin),
|
||||||
|
//! _ => (),
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[get("/ws")]
|
||||||
|
//! async fn index(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
|
||||||
|
//! ws::start(MyWs, &req, stream)
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[actix_web::main]
|
||||||
|
//! async fn main() -> std::io::Result<()> {
|
||||||
|
//! HttpServer::new(|| App::new().service(index))
|
||||||
|
//! .bind(("127.0.0.1", 8080))?
|
||||||
|
//! .run()
|
||||||
|
//! .await
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Documentation & Community Resources
|
||||||
|
//! In addition to this API documentation, several other resources are available:
|
||||||
|
//!
|
||||||
|
//! * [Website & User Guide](https://actix.rs/)
|
||||||
|
//! * [Documentation for `actix_web`](actix_web)
|
||||||
|
//! * [Examples Repository](https://github.com/actix/examples)
|
||||||
|
//! * [Community Chat on Discord](https://discord.gg/NWpN5mmg3x)
|
||||||
|
//!
|
||||||
|
//! To get started navigating the API docs, you may consider looking at the following pages first:
|
||||||
|
//!
|
||||||
|
//! * [`ws`]: This module provides actor support for WebSockets.
|
||||||
|
//!
|
||||||
|
//! * [`HttpContext`]: This struct provides actor support for streaming HTTP responses.
|
||||||
|
//!
|
||||||
|
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||||
#![warn(future_incompatible)]
|
#![warn(future_incompatible)]
|
||||||
|
|
|
@ -1,4 +1,60 @@
|
||||||
//! Websocket integration.
|
//! Websocket integration.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use actix::{Actor, StreamHandler};
|
||||||
|
//! use actix_web::{get, web, App, Error, HttpRequest, HttpResponse, HttpServer};
|
||||||
|
//! use actix_web_actors::ws;
|
||||||
|
//!
|
||||||
|
//! /// Define Websocket actor
|
||||||
|
//! struct MyWs;
|
||||||
|
//!
|
||||||
|
//! impl Actor for MyWs {
|
||||||
|
//! type Context = ws::WebsocketContext<Self>;
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! /// Handler for ws::Message message
|
||||||
|
//! impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWs {
|
||||||
|
//! fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
|
||||||
|
//! match msg {
|
||||||
|
//! Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),
|
||||||
|
//! Ok(ws::Message::Text(text)) => ctx.text(text),
|
||||||
|
//! Ok(ws::Message::Binary(bin)) => ctx.binary(bin),
|
||||||
|
//! _ => (),
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[get("/ws")]
|
||||||
|
//! async fn websocket(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
|
||||||
|
//! ws::start(MyWs, &req, stream)
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! const MAX_FRAME_SIZE: usize = 16_384; // 16KiB
|
||||||
|
//!
|
||||||
|
//! #[get("/custom-ws")]
|
||||||
|
//! async fn custom_websocket(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
|
||||||
|
//! // Create a Websocket session with a specific max frame size, and protocols.
|
||||||
|
//! ws::WsResponseBuilder::new(MyWs, &req, stream)
|
||||||
|
//! .frame_size(MAX_FRAME_SIZE)
|
||||||
|
//! .protocols(&["A", "B"])
|
||||||
|
//! .start()
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[actix_web::main]
|
||||||
|
//! async fn main() -> std::io::Result<()> {
|
||||||
|
//! HttpServer::new(|| {
|
||||||
|
//! App::new()
|
||||||
|
//! .service(websocket)
|
||||||
|
//! .service(custom_websocket)
|
||||||
|
//! })
|
||||||
|
//! .bind(("127.0.0.1", 8080))?
|
||||||
|
//! .run()
|
||||||
|
//! .await
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
|
@ -41,20 +97,51 @@ use tokio::sync::oneshot;
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// Create a Websocket session response with default configuration.
|
/// ```no_run
|
||||||
/// ```ignore
|
/// # use actix::{Actor, StreamHandler};
|
||||||
/// WsResponseBuilder::new(WsActor, &req, stream).start()
|
/// # use actix_web::{get, web, App, Error, HttpRequest, HttpResponse, HttpServer};
|
||||||
/// ```
|
/// # use actix_web_actors::ws;
|
||||||
|
/// #
|
||||||
|
/// # struct MyWs;
|
||||||
|
/// #
|
||||||
|
/// # impl Actor for MyWs {
|
||||||
|
/// # type Context = ws::WebsocketContext<Self>;
|
||||||
|
/// # }
|
||||||
|
/// #
|
||||||
|
/// # /// Handler for ws::Message message
|
||||||
|
/// # impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWs {
|
||||||
|
/// # fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {}
|
||||||
|
/// # }
|
||||||
|
/// #
|
||||||
|
/// #[get("/ws")]
|
||||||
|
/// async fn websocket(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
|
||||||
|
/// ws::WsResponseBuilder::new(MyWs, &req, stream).start()
|
||||||
|
/// }
|
||||||
///
|
///
|
||||||
/// Create a Websocket session with a specific max frame size, [`Codec`], and protocols.
|
|
||||||
/// ```ignore
|
|
||||||
/// const MAX_FRAME_SIZE: usize = 16_384; // 16KiB
|
/// const MAX_FRAME_SIZE: usize = 16_384; // 16KiB
|
||||||
///
|
///
|
||||||
/// ws::WsResponseBuilder::new(WsActor, &req, stream)
|
/// #[get("/custom-ws")]
|
||||||
/// .codec(Codec::new())
|
/// async fn custom_websocket(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
|
||||||
/// .protocols(&["A", "B"])
|
/// // Create a Websocket session with a specific max frame size, codec, and protocols.
|
||||||
/// .frame_size(MAX_FRAME_SIZE)
|
/// ws::WsResponseBuilder::new(MyWs, &req, stream)
|
||||||
/// .start()
|
/// .codec(actix_http::ws::Codec::new())
|
||||||
|
/// // This will overwrite the codec's max frame-size
|
||||||
|
/// .frame_size(MAX_FRAME_SIZE)
|
||||||
|
/// .protocols(&["A", "B"])
|
||||||
|
/// .start()
|
||||||
|
/// }
|
||||||
|
/// #
|
||||||
|
/// # #[actix_web::main]
|
||||||
|
/// # async fn main() -> std::io::Result<()> {
|
||||||
|
/// # HttpServer::new(|| {
|
||||||
|
/// # App::new()
|
||||||
|
/// # .service(websocket)
|
||||||
|
/// # .service(custom_websocket)
|
||||||
|
/// # })
|
||||||
|
/// # .bind(("127.0.0.1", 8080))?
|
||||||
|
/// # .run()
|
||||||
|
/// # .await
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct WsResponseBuilder<'a, A, T>
|
pub struct WsResponseBuilder<'a, A, T>
|
||||||
where
|
where
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## Unreleased - 2022-xx-xx
|
## Unreleased - 2022-xx-xx
|
||||||
|
### Added
|
||||||
|
- Add configuration options for TLS handshake timeout via `HttpServer::{rustls, openssl}_with_config` methods. [#2752]
|
||||||
|
- Add `ErrorHandlers::default_handler()` (as well as `default_handler_{server, client}()`) to make registering handlers with the `ErrorHandlers` middleware easier. [#2784]
|
||||||
|
- Add `ServiceRequest::{parts, request}()` getter methods. [#2786]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.
|
- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.
|
||||||
### Added
|
|
||||||
- Add `ServiceRequest::{parts, request}()` getter methods. [#2786]
|
|
||||||
- Add `ErrorHandlers::default_handler()` (as well as `default_handler_{server, client}()`) to make registering handlers with the `ErrorHandlers` middleware easier. [#2784]
|
|
||||||
|
|
||||||
|
[#2752]: https://github.com/actix/actix-web/pull/2752
|
||||||
[#2784]: https://github.com/actix/actix-web/pull/2784
|
[#2784]: https://github.com/actix/actix-web/pull/2784
|
||||||
[#2786]: https://github.com/actix/actix-web/pull/2786
|
[#2786]: https://github.com/actix/actix-web/pull/2786
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ where
|
||||||
/// [`HttpRequest::app_data`](crate::HttpRequest::app_data) method at runtime.
|
/// [`HttpRequest::app_data`](crate::HttpRequest::app_data) method at runtime.
|
||||||
///
|
///
|
||||||
/// # [`Data<T>`]
|
/// # [`Data<T>`]
|
||||||
/// Any [`Data<T>`] type added here can utilize it's extractor implementation in handlers.
|
/// Any [`Data<T>`] type added here can utilize its extractor implementation in handlers.
|
||||||
/// Types not wrapped in `Data<T>` cannot use this extractor. See [its docs](Data<T>) for more
|
/// Types not wrapped in `Data<T>` cannot use this extractor. See [its docs](Data<T>) for more
|
||||||
/// about its usage and patterns.
|
/// about its usage and patterns.
|
||||||
///
|
///
|
||||||
|
|
|
@ -257,7 +257,7 @@ impl ServiceFactory<ServiceRequest> for AppRoutingFactory {
|
||||||
type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
|
||||||
|
|
||||||
fn new_service(&self, _: ()) -> Self::Future {
|
fn new_service(&self, _: ()) -> Self::Future {
|
||||||
// construct all services factory future with it's resource def and guards.
|
// construct all services factory future with its resource def and guards.
|
||||||
let factory_fut = join_all(self.services.iter().map(|(path, factory, guards)| {
|
let factory_fut = join_all(self.services.iter().map(|(path, factory, guards)| {
|
||||||
let path = path.clone();
|
let path = path.clone();
|
||||||
let guards = guards.borrow_mut().take().unwrap_or_default();
|
let guards = guards.borrow_mut().take().unwrap_or_default();
|
||||||
|
|
|
@ -153,6 +153,16 @@ impl AppConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AppConfig {
|
impl Default for AppConfig {
|
||||||
|
/// Returns the default AppConfig.
|
||||||
|
/// Note: The included socket address is "127.0.0.1".
|
||||||
|
///
|
||||||
|
/// 127.0.0.1: non-routable meta address that denotes an unknown, invalid or non-applicable target.
|
||||||
|
/// If you need a service only accessed by itself, use a loopback address.
|
||||||
|
/// A loopback address for IPv4 is any loopback address that begins with "127".
|
||||||
|
/// Loopback addresses should be only used to test your application locally.
|
||||||
|
/// The default configuration provides a loopback address.
|
||||||
|
///
|
||||||
|
/// 0.0.0.0: if configured to use this special address, the application will listen to any IP address configured on the machine.
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
AppConfig::new(
|
AppConfig::new(
|
||||||
false,
|
false,
|
||||||
|
|
|
@ -254,7 +254,7 @@ impl Guard for AllGuard {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wraps a guard and inverts the outcome of it's `Guard` implementation.
|
/// Wraps a guard and inverts the outcome of its `Guard` implementation.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// The handler below will be called for any request method apart from `GET`.
|
/// The handler below will be called for any request method apart from `GET`.
|
||||||
|
@ -459,7 +459,7 @@ impl Guard for HostGuard {
|
||||||
return scheme == req_host_uri_scheme;
|
return scheme == req_host_uri_scheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: is the the correct behavior?
|
// TODO: is this the correct behavior?
|
||||||
// falls through if scheme cannot be determined
|
// falls through if scheme cannot be determined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ use crate::{
|
||||||
/// Thanks to Rust's type system, Actix Web can infer the function parameter types. During the
|
/// Thanks to Rust's type system, Actix Web can infer the function parameter types. During the
|
||||||
/// extraction step, the parameter types are described as a tuple type, [`from_request`] is run on
|
/// extraction step, the parameter types are described as a tuple type, [`from_request`] is run on
|
||||||
/// that tuple, and the `Handler::call` implementation for that particular function arity
|
/// that tuple, and the `Handler::call` implementation for that particular function arity
|
||||||
/// destructures the tuple into it's component types and calls your handler function with them.
|
/// destructures the tuple into its component types and calls your handler function with them.
|
||||||
///
|
///
|
||||||
/// In pseudo-code the process looks something like this:
|
/// In pseudo-code the process looks something like this:
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
|
|
|
@ -343,7 +343,7 @@ mod response_fut_impl {
|
||||||
|
|
||||||
// Future is only implemented for BoxBody payload type because it's the most useful for making
|
// Future is only implemented for BoxBody payload type because it's the most useful for making
|
||||||
// simple handlers without async blocks. Making it generic over all MessageBody types requires a
|
// simple handlers without async blocks. Making it generic over all MessageBody types requires a
|
||||||
// future impl on Response which would cause it's body field to be, undesirably, Option<B>.
|
// future impl on Response which would cause its body field to be, undesirably, Option<B>.
|
||||||
//
|
//
|
||||||
// This impl is not particularly efficient due to the Response construction and should probably
|
// This impl is not particularly efficient due to the Response construction and should probably
|
||||||
// not be invoked if performance is important. Prefer an async fn/block in such cases.
|
// not be invoked if performance is important. Prefer an async fn/block in such cases.
|
||||||
|
|
|
@ -18,6 +18,9 @@ use actix_tls::accept::openssl::reexports::{AlpnError, SslAcceptor, SslAcceptorB
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(feature = "rustls")]
|
||||||
use actix_tls::accept::rustls::reexports::ServerConfig as RustlsServerConfig;
|
use actix_tls::accept::rustls::reexports::ServerConfig as RustlsServerConfig;
|
||||||
|
|
||||||
|
#[cfg(any(feature = "openssl", feature = "rustls"))]
|
||||||
|
use actix_http::TlsAcceptorConfig;
|
||||||
|
|
||||||
use crate::{config::AppConfig, Error};
|
use crate::{config::AppConfig, Error};
|
||||||
|
|
||||||
struct Socket {
|
struct Socket {
|
||||||
|
@ -30,6 +33,8 @@ struct Config {
|
||||||
keep_alive: KeepAlive,
|
keep_alive: KeepAlive,
|
||||||
client_request_timeout: Duration,
|
client_request_timeout: Duration,
|
||||||
client_disconnect_timeout: Duration,
|
client_disconnect_timeout: Duration,
|
||||||
|
#[cfg(any(feature = "openssl", feature = "rustls"))]
|
||||||
|
tls_handshake_timeout: Option<Duration>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An HTTP Server.
|
/// An HTTP Server.
|
||||||
|
@ -92,6 +97,8 @@ where
|
||||||
keep_alive: KeepAlive::default(),
|
keep_alive: KeepAlive::default(),
|
||||||
client_request_timeout: Duration::from_secs(5),
|
client_request_timeout: Duration::from_secs(5),
|
||||||
client_disconnect_timeout: Duration::from_secs(1),
|
client_disconnect_timeout: Duration::from_secs(1),
|
||||||
|
#[cfg(any(feature = "rustls", feature = "openssl"))]
|
||||||
|
tls_handshake_timeout: None,
|
||||||
})),
|
})),
|
||||||
backlog: 1024,
|
backlog: 1024,
|
||||||
sockets: Vec::new(),
|
sockets: Vec::new(),
|
||||||
|
@ -225,6 +232,24 @@ where
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set TLS handshake timeout.
|
||||||
|
///
|
||||||
|
/// Defines a timeout for TLS handshake. If the TLS handshake does not complete
|
||||||
|
/// within this time, the connection is closed.
|
||||||
|
///
|
||||||
|
/// By default handshake timeout is set to 3000 milliseconds.
|
||||||
|
#[cfg(any(feature = "openssl", feature = "rustls"))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "openssl", feature = "rustls"))))]
|
||||||
|
pub fn tls_handshake_timeout(self, dur: Duration) -> Self {
|
||||||
|
self.config
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.tls_handshake_timeout
|
||||||
|
.replace(dur);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[deprecated(since = "4.0.0", note = "Renamed to `client_disconnect_timeout`.")]
|
#[deprecated(since = "4.0.0", note = "Renamed to `client_disconnect_timeout`.")]
|
||||||
pub fn client_shutdown(self, dur: u64) -> Self {
|
pub fn client_shutdown(self, dur: u64) -> Self {
|
||||||
|
@ -376,10 +401,15 @@ where
|
||||||
.into_factory()
|
.into_factory()
|
||||||
.map_err(|err| err.into().error_response());
|
.map_err(|err| err.into().error_response());
|
||||||
|
|
||||||
|
let acceptor_config = match c.tls_handshake_timeout {
|
||||||
|
Some(dur) => TlsAcceptorConfig::default().handshake_timeout(dur),
|
||||||
|
None => TlsAcceptorConfig::default(),
|
||||||
|
};
|
||||||
|
|
||||||
svc.finish(map_config(fac, move |_| {
|
svc.finish(map_config(fac, move |_| {
|
||||||
AppConfig::new(true, host.clone(), addr)
|
AppConfig::new(true, host.clone(), addr)
|
||||||
}))
|
}))
|
||||||
.openssl(acceptor.clone())
|
.openssl_with_config(acceptor.clone(), acceptor_config)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
|
@ -434,10 +464,15 @@ where
|
||||||
.into_factory()
|
.into_factory()
|
||||||
.map_err(|err| err.into().error_response());
|
.map_err(|err| err.into().error_response());
|
||||||
|
|
||||||
|
let acceptor_config = match c.tls_handshake_timeout {
|
||||||
|
Some(dur) => TlsAcceptorConfig::default().handshake_timeout(dur),
|
||||||
|
None => TlsAcceptorConfig::default(),
|
||||||
|
};
|
||||||
|
|
||||||
svc.finish(map_config(fac, move |_| {
|
svc.finish(map_config(fac, move |_| {
|
||||||
AppConfig::new(true, host.clone(), addr)
|
AppConfig::new(true, host.clone(), addr)
|
||||||
}))
|
}))
|
||||||
.rustls(config.clone())
|
.rustls_with_config(config.clone(), acceptor_config)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
|
|
|
@ -183,6 +183,7 @@ mod tests {
|
||||||
assert!(Path::<MyStruct>::from_request(&req, &mut pl).await.is_err());
|
assert!(Path::<MyStruct>::from_request(&req, &mut pl).await.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::let_unit_value)]
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_tuple_extract() {
|
async fn test_tuple_extract() {
|
||||||
let resource = ResourceDef::new("/{key}/{value}/");
|
let resource = ResourceDef::new("/{key}/{value}/");
|
||||||
|
|
|
@ -113,7 +113,7 @@ pub struct BytesExtractFut {
|
||||||
body_fut: HttpMessageBody,
|
body_fut: HttpMessageBody,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Future for BytesExtractFut {
|
impl Future for BytesExtractFut {
|
||||||
type Output = Result<Bytes, Error>;
|
type Output = Result<Bytes, Error>;
|
||||||
|
|
||||||
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> {
|
||||||
|
@ -167,7 +167,7 @@ pub struct StringExtractFut {
|
||||||
encoding: &'static Encoding,
|
encoding: &'static Encoding,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Future for StringExtractFut {
|
impl Future for StringExtractFut {
|
||||||
type Output = Result<String, Error>;
|
type Output = Result<String, Error>;
|
||||||
|
|
||||||
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> {
|
||||||
|
|
Loading…
Reference in New Issue