mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-23 23:51:06 +01:00
expose internal http server types and allow to create custom http pipelines
This commit is contained in:
parent
5966ee6192
commit
c1e0b4f322
@ -1,4 +1,3 @@
|
|||||||
use std::marker::PhantomData;
|
|
||||||
use std::{fmt, net};
|
use std::{fmt, net};
|
||||||
|
|
||||||
use actix_net::either::Either;
|
use actix_net::either::Either;
|
||||||
@ -9,61 +8,39 @@ use super::acceptor::{
|
|||||||
AcceptorServiceFactory, AcceptorTimeout, ServerMessageAcceptor, TcpAcceptor,
|
AcceptorServiceFactory, AcceptorTimeout, ServerMessageAcceptor, TcpAcceptor,
|
||||||
};
|
};
|
||||||
use super::error::AcceptorError;
|
use super::error::AcceptorError;
|
||||||
use super::handler::{HttpHandler, IntoHttpHandler};
|
use super::handler::IntoHttpHandler;
|
||||||
use super::service::HttpService;
|
use super::service::HttpService;
|
||||||
use super::settings::{ServerSettings, WorkerSettings};
|
use super::settings::{ServerSettings, WorkerSettings};
|
||||||
use super::{IoStream, KeepAlive};
|
use super::KeepAlive;
|
||||||
|
|
||||||
pub(crate) trait ServiceProvider {
|
pub(crate) trait ServiceProvider {
|
||||||
fn register(
|
fn register(
|
||||||
&self, server: Server, lst: net::TcpListener, host: Option<String>,
|
&self, server: Server, lst: net::TcpListener, host: String,
|
||||||
addr: net::SocketAddr, keep_alive: KeepAlive, client_timeout: usize,
|
addr: net::SocketAddr, keep_alive: KeepAlive, client_timeout: usize,
|
||||||
) -> Server;
|
) -> Server;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Utility type that builds complete http pipeline
|
/// Utility type that builds complete http pipeline
|
||||||
pub struct HttpServiceBuilder<F, H, A, P>
|
pub struct HttpServiceBuilder<F, H, A>
|
||||||
where
|
where
|
||||||
F: Fn() -> H + Send + Clone,
|
F: Fn() -> H + Send + Clone,
|
||||||
{
|
{
|
||||||
factory: F,
|
factory: F,
|
||||||
acceptor: A,
|
acceptor: A,
|
||||||
pipeline: P,
|
|
||||||
no_client_timer: bool,
|
no_client_timer: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, H, A, Io> HttpServiceBuilder<F, H, A, DefaultPipelineFactory<H::Handler, Io>>
|
impl<F, H, A> HttpServiceBuilder<F, H, A>
|
||||||
where
|
|
||||||
Io: IoStream + Send,
|
|
||||||
F: Fn() -> H + Send + Clone + 'static,
|
|
||||||
H: IntoHttpHandler,
|
|
||||||
A: AcceptorServiceFactory,
|
|
||||||
<A::NewService as NewService>::InitError: fmt::Debug,
|
|
||||||
{
|
|
||||||
/// Create http service builder with default pipeline factory
|
|
||||||
pub fn with_default_pipeline(factory: F, acceptor: A) -> Self {
|
|
||||||
Self {
|
|
||||||
factory,
|
|
||||||
acceptor,
|
|
||||||
pipeline: DefaultPipelineFactory::new(),
|
|
||||||
no_client_timer: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F, H, A, P> HttpServiceBuilder<F, H, A, P>
|
|
||||||
where
|
where
|
||||||
F: Fn() -> H + Send + Clone + 'static,
|
F: Fn() -> H + Send + Clone + 'static,
|
||||||
H: IntoHttpHandler,
|
H: IntoHttpHandler,
|
||||||
A: AcceptorServiceFactory,
|
A: AcceptorServiceFactory,
|
||||||
<A::NewService as NewService>::InitError: fmt::Debug,
|
<A::NewService as NewService>::InitError: fmt::Debug,
|
||||||
P: HttpPipelineFactory<H::Handler, Io = A::Io>,
|
|
||||||
{
|
{
|
||||||
/// Create http service builder
|
/// Create http service builder
|
||||||
pub fn new(factory: F, acceptor: A, pipeline: P) -> Self {
|
pub fn new(factory: F, acceptor: A) -> Self {
|
||||||
Self {
|
Self {
|
||||||
factory,
|
factory,
|
||||||
pipeline,
|
|
||||||
acceptor,
|
acceptor,
|
||||||
no_client_timer: false,
|
no_client_timer: false,
|
||||||
}
|
}
|
||||||
@ -75,34 +52,20 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Use different acceptor factory
|
/// Use different acceptor factory
|
||||||
pub fn acceptor<A1>(self, acceptor: A1) -> HttpServiceBuilder<F, H, A1, P>
|
pub fn acceptor<A1>(self, acceptor: A1) -> HttpServiceBuilder<F, H, A1>
|
||||||
where
|
where
|
||||||
A1: AcceptorServiceFactory,
|
A1: AcceptorServiceFactory,
|
||||||
<A1::NewService as NewService>::InitError: fmt::Debug,
|
<A1::NewService as NewService>::InitError: fmt::Debug,
|
||||||
{
|
{
|
||||||
HttpServiceBuilder {
|
HttpServiceBuilder {
|
||||||
acceptor,
|
acceptor,
|
||||||
pipeline: self.pipeline,
|
|
||||||
factory: self.factory.clone(),
|
|
||||||
no_client_timer: self.no_client_timer,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Use different pipeline factory
|
|
||||||
pub fn pipeline<P1>(self, pipeline: P1) -> HttpServiceBuilder<F, H, A, P1>
|
|
||||||
where
|
|
||||||
P1: HttpPipelineFactory<H::Handler>,
|
|
||||||
{
|
|
||||||
HttpServiceBuilder {
|
|
||||||
pipeline,
|
|
||||||
acceptor: self.acceptor,
|
|
||||||
factory: self.factory.clone(),
|
factory: self.factory.clone(),
|
||||||
no_client_timer: self.no_client_timer,
|
no_client_timer: self.no_client_timer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(
|
fn finish(
|
||||||
&self, host: Option<String>, addr: net::SocketAddr, keep_alive: KeepAlive,
|
&self, host: String, addr: net::SocketAddr, keep_alive: KeepAlive,
|
||||||
client_timeout: usize,
|
client_timeout: usize,
|
||||||
) -> impl ServiceFactory {
|
) -> impl ServiceFactory {
|
||||||
let timeout = if self.no_client_timer {
|
let timeout = if self.no_client_timer {
|
||||||
@ -111,7 +74,6 @@ where
|
|||||||
client_timeout
|
client_timeout
|
||||||
};
|
};
|
||||||
let factory = self.factory.clone();
|
let factory = self.factory.clone();
|
||||||
let pipeline = self.pipeline.clone();
|
|
||||||
let acceptor = self.acceptor.clone();
|
let acceptor = self.acceptor.clone();
|
||||||
move || {
|
move || {
|
||||||
let app = (factory)().into_handler();
|
let app = (factory)().into_handler();
|
||||||
@ -119,7 +81,7 @@ where
|
|||||||
app,
|
app,
|
||||||
keep_alive,
|
keep_alive,
|
||||||
timeout as u64,
|
timeout as u64,
|
||||||
ServerSettings::new(Some(addr), &host, false),
|
ServerSettings::new(addr, &host, false),
|
||||||
);
|
);
|
||||||
|
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
@ -129,8 +91,7 @@ where
|
|||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
.map_init_err(|_| ())
|
.map_init_err(|_| ())
|
||||||
.and_then(
|
.and_then(
|
||||||
pipeline
|
HttpService::new(settings)
|
||||||
.create(settings)
|
|
||||||
.map_init_err(|_| ())
|
.map_init_err(|_| ())
|
||||||
.map_err(|_| ()),
|
.map_err(|_| ()),
|
||||||
),
|
),
|
||||||
@ -142,8 +103,7 @@ where
|
|||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
.map_init_err(|_| ())
|
.map_init_err(|_| ())
|
||||||
.and_then(
|
.and_then(
|
||||||
pipeline
|
HttpService::new(settings)
|
||||||
.create(settings)
|
|
||||||
.map_init_err(|_| ())
|
.map_init_err(|_| ())
|
||||||
.map_err(|_| ()),
|
.map_err(|_| ()),
|
||||||
),
|
),
|
||||||
@ -153,33 +113,30 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, H, A, P> Clone for HttpServiceBuilder<F, H, A, P>
|
impl<F, H, A> Clone for HttpServiceBuilder<F, H, A>
|
||||||
where
|
where
|
||||||
F: Fn() -> H + Send + Clone,
|
F: Fn() -> H + Send + Clone,
|
||||||
H: IntoHttpHandler,
|
H: IntoHttpHandler,
|
||||||
A: AcceptorServiceFactory,
|
A: AcceptorServiceFactory,
|
||||||
P: HttpPipelineFactory<H::Handler, Io = A::Io>,
|
|
||||||
{
|
{
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
HttpServiceBuilder {
|
HttpServiceBuilder {
|
||||||
factory: self.factory.clone(),
|
factory: self.factory.clone(),
|
||||||
acceptor: self.acceptor.clone(),
|
acceptor: self.acceptor.clone(),
|
||||||
pipeline: self.pipeline.clone(),
|
|
||||||
no_client_timer: self.no_client_timer,
|
no_client_timer: self.no_client_timer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, H, A, P> ServiceProvider for HttpServiceBuilder<F, H, A, P>
|
impl<F, H, A> ServiceProvider for HttpServiceBuilder<F, H, A>
|
||||||
where
|
where
|
||||||
F: Fn() -> H + Send + Clone + 'static,
|
F: Fn() -> H + Send + Clone + 'static,
|
||||||
A: AcceptorServiceFactory,
|
A: AcceptorServiceFactory,
|
||||||
<A::NewService as NewService>::InitError: fmt::Debug,
|
<A::NewService as NewService>::InitError: fmt::Debug,
|
||||||
P: HttpPipelineFactory<H::Handler, Io = A::Io>,
|
|
||||||
H: IntoHttpHandler,
|
H: IntoHttpHandler,
|
||||||
{
|
{
|
||||||
fn register(
|
fn register(
|
||||||
&self, server: Server, lst: net::TcpListener, host: Option<String>,
|
&self, server: Server, lst: net::TcpListener, host: String,
|
||||||
addr: net::SocketAddr, keep_alive: KeepAlive, client_timeout: usize,
|
addr: net::SocketAddr, keep_alive: KeepAlive, client_timeout: usize,
|
||||||
) -> Server {
|
) -> Server {
|
||||||
server.listen2(
|
server.listen2(
|
||||||
@ -189,64 +146,3 @@ where
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HttpPipelineFactory<H: HttpHandler>: Send + Clone + 'static {
|
|
||||||
type Io: IoStream;
|
|
||||||
type NewService: NewService<Request = Self::Io, Response = ()>;
|
|
||||||
|
|
||||||
fn create(&self, settings: WorkerSettings<H>) -> Self::NewService;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F, T, H> HttpPipelineFactory<H> for F
|
|
||||||
where
|
|
||||||
F: Fn(WorkerSettings<H>) -> T + Send + Clone + 'static,
|
|
||||||
T: NewService<Response = ()>,
|
|
||||||
T::Request: IoStream,
|
|
||||||
H: HttpHandler,
|
|
||||||
{
|
|
||||||
type Io = T::Request;
|
|
||||||
type NewService = T;
|
|
||||||
|
|
||||||
fn create(&self, settings: WorkerSettings<H>) -> T {
|
|
||||||
(self)(settings)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct DefaultPipelineFactory<H, Io> {
|
|
||||||
_t: PhantomData<(H, Io)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<H, Io> Send for DefaultPipelineFactory<H, Io> {}
|
|
||||||
|
|
||||||
impl<H, Io> DefaultPipelineFactory<H, Io>
|
|
||||||
where
|
|
||||||
Io: IoStream + Send,
|
|
||||||
H: HttpHandler + 'static,
|
|
||||||
{
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self { _t: PhantomData }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<H, Io> Clone for DefaultPipelineFactory<H, Io>
|
|
||||||
where
|
|
||||||
Io: IoStream,
|
|
||||||
H: HttpHandler,
|
|
||||||
{
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self { _t: PhantomData }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<H, Io> HttpPipelineFactory<H> for DefaultPipelineFactory<H, Io>
|
|
||||||
where
|
|
||||||
Io: IoStream,
|
|
||||||
H: HttpHandler + 'static,
|
|
||||||
{
|
|
||||||
type Io = Io;
|
|
||||||
type NewService = HttpService<H, Io>;
|
|
||||||
|
|
||||||
fn create(&self, settings: WorkerSettings<H>) -> Self::NewService {
|
|
||||||
HttpService::new(settings)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -6,6 +6,7 @@ use futures::{Async, Future, Poll};
|
|||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use tokio_io::{AsyncRead, AsyncWrite};
|
||||||
use tokio_timer::Delay;
|
use tokio_timer::Delay;
|
||||||
|
|
||||||
|
use super::error::HttpDispatchError;
|
||||||
use super::settings::WorkerSettings;
|
use super::settings::WorkerSettings;
|
||||||
use super::{h1, h2, HttpHandler, IoStream};
|
use super::{h1, h2, HttpHandler, IoStream};
|
||||||
|
|
||||||
@ -86,7 +87,7 @@ where
|
|||||||
H: HttpHandler + 'static,
|
H: HttpHandler + 'static,
|
||||||
{
|
{
|
||||||
type Item = ();
|
type Item = ();
|
||||||
type Error = ();
|
type Error = HttpDispatchError;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||||
// keep-alive timer
|
// keep-alive timer
|
||||||
@ -127,6 +128,7 @@ where
|
|||||||
return h2.poll();
|
return h2.poll();
|
||||||
}
|
}
|
||||||
Some(HttpProtocol::Unknown(_, _, ref mut io, ref mut buf)) => {
|
Some(HttpProtocol::Unknown(_, _, ref mut io, ref mut buf)) => {
|
||||||
|
let mut err = None;
|
||||||
let mut disconnect = false;
|
let mut disconnect = false;
|
||||||
match io.read_available(buf) {
|
match io.read_available(buf) {
|
||||||
Ok(Async::Ready((read_some, stream_closed))) => {
|
Ok(Async::Ready((read_some, stream_closed))) => {
|
||||||
@ -136,14 +138,16 @@ where
|
|||||||
disconnect = true;
|
disconnect = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(e) => {
|
||||||
disconnect = true;
|
err = Some(e.into());
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
if disconnect {
|
if disconnect {
|
||||||
debug!("Ignored premature client disconnection");
|
debug!("Ignored premature client disconnection");
|
||||||
return Err(());
|
return Ok(Async::Ready(()));
|
||||||
|
} else if let Some(e) = err {
|
||||||
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if buf.len() >= 14 {
|
if buf.len() >= 14 {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use futures::{Async, Poll};
|
use futures::{Async, Poll};
|
||||||
|
use http2;
|
||||||
|
|
||||||
use super::{helpers, HttpHandlerTask, Writer};
|
use super::{helpers, HttpHandlerTask, Writer};
|
||||||
use http::{StatusCode, Version};
|
use http::{StatusCode, Version};
|
||||||
@ -19,6 +20,39 @@ pub enum AcceptorError<T> {
|
|||||||
Timeout,
|
Timeout,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Fail, Debug)]
|
||||||
|
/// A set of errors that can occur during dispatching http requests
|
||||||
|
pub enum HttpDispatchError {
|
||||||
|
/// Application error
|
||||||
|
#[fail(display = "Application specific error")]
|
||||||
|
AppError,
|
||||||
|
|
||||||
|
/// An `io::Error` that occurred while trying to read or write to a network
|
||||||
|
/// stream.
|
||||||
|
#[fail(display = "IO error: {}", _0)]
|
||||||
|
Io(io::Error),
|
||||||
|
|
||||||
|
/// The first request did not complete within the specified timeout.
|
||||||
|
#[fail(display = "The first request did not complete within the specified timeout")]
|
||||||
|
SlowRequestTimeout,
|
||||||
|
|
||||||
|
/// HTTP2 error
|
||||||
|
#[fail(display = "HTTP2 error: {}", _0)]
|
||||||
|
Http2(http2::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for HttpDispatchError {
|
||||||
|
fn from(err: io::Error) -> Self {
|
||||||
|
HttpDispatchError::Io(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<http2::Error> for HttpDispatchError {
|
||||||
|
fn from(err: http2::Error) -> Self {
|
||||||
|
HttpDispatchError::Http2(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct ServerError(Version, StatusCode);
|
pub(crate) struct ServerError(Version, StatusCode);
|
||||||
|
|
||||||
impl ServerError {
|
impl ServerError {
|
||||||
|
@ -10,7 +10,7 @@ use error::{Error, PayloadError};
|
|||||||
use http::{StatusCode, Version};
|
use http::{StatusCode, Version};
|
||||||
use payload::{Payload, PayloadStatus, PayloadWriter};
|
use payload::{Payload, PayloadStatus, PayloadWriter};
|
||||||
|
|
||||||
use super::error::ServerError;
|
use super::error::{HttpDispatchError, ServerError};
|
||||||
use super::h1decoder::{DecoderError, H1Decoder, Message};
|
use super::h1decoder::{DecoderError, H1Decoder, Message};
|
||||||
use super::h1writer::H1Writer;
|
use super::h1writer::H1Writer;
|
||||||
use super::input::PayloadType;
|
use super::input::PayloadType;
|
||||||
@ -172,7 +172,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn poll(&mut self) -> Poll<(), ()> {
|
pub fn poll(&mut self) -> Poll<(), HttpDispatchError> {
|
||||||
// check connection keep-alive
|
// check connection keep-alive
|
||||||
if !self.poll_keep_alive() {
|
if !self.poll_keep_alive() {
|
||||||
return Ok(Async::Ready(()));
|
return Ok(Async::Ready(()));
|
||||||
@ -190,7 +190,7 @@ where
|
|||||||
Ok(Async::Ready(_)) => return Ok(Async::Ready(())),
|
Ok(Async::Ready(_)) => return Ok(Async::Ready(())),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
debug!("Error sending data: {}", err);
|
debug!("Error sending data: {}", err);
|
||||||
return Err(());
|
return Err(err.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -303,7 +303,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll_handler(&mut self) -> Poll<bool, ()> {
|
pub fn poll_handler(&mut self) -> Poll<bool, HttpDispatchError> {
|
||||||
let retry = self.can_read();
|
let retry = self.can_read();
|
||||||
|
|
||||||
// check in-flight messages
|
// check in-flight messages
|
||||||
@ -321,7 +321,7 @@ where
|
|||||||
return Ok(Async::NotReady);
|
return Ok(Async::NotReady);
|
||||||
}
|
}
|
||||||
self.flags.insert(Flags::ERROR);
|
self.flags.insert(Flags::ERROR);
|
||||||
return Err(());
|
return Err(HttpDispatchError::AppError);
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.tasks[idx].pipe.poll_io(&mut self.stream) {
|
match self.tasks[idx].pipe.poll_io(&mut self.stream) {
|
||||||
@ -404,7 +404,7 @@ where
|
|||||||
debug!("Error sending data: {}", err);
|
debug!("Error sending data: {}", err);
|
||||||
self.read_disconnected();
|
self.read_disconnected();
|
||||||
self.write_disconnected();
|
self.write_disconnected();
|
||||||
return Err(());
|
return Err(err.into());
|
||||||
}
|
}
|
||||||
Ok(Async::Ready(_)) => {
|
Ok(Async::Ready(_)) => {
|
||||||
// non consumed payload in that case close connection
|
// non consumed payload in that case close connection
|
||||||
|
@ -19,7 +19,7 @@ use http::{StatusCode, Version};
|
|||||||
use payload::{Payload, PayloadStatus, PayloadWriter};
|
use payload::{Payload, PayloadStatus, PayloadWriter};
|
||||||
use uri::Url;
|
use uri::Url;
|
||||||
|
|
||||||
use super::error::ServerError;
|
use super::error::{HttpDispatchError, ServerError};
|
||||||
use super::h2writer::H2Writer;
|
use super::h2writer::H2Writer;
|
||||||
use super::input::PayloadType;
|
use super::input::PayloadType;
|
||||||
use super::settings::WorkerSettings;
|
use super::settings::WorkerSettings;
|
||||||
@ -86,7 +86,7 @@ where
|
|||||||
&self.settings
|
&self.settings
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll(&mut self) -> Poll<(), ()> {
|
pub fn poll(&mut self) -> Poll<(), HttpDispatchError> {
|
||||||
// server
|
// server
|
||||||
if let State::Connection(ref mut conn) = self.state {
|
if let State::Connection(ref mut conn) = self.state {
|
||||||
// keep-alive timer
|
// keep-alive timer
|
||||||
@ -244,9 +244,7 @@ where
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// keep-alive disable, drop connection
|
// keep-alive disable, drop connection
|
||||||
return conn.poll_close().map_err(|e| {
|
return conn.poll_close().map_err(|e| e.into());
|
||||||
error!("Error during connection close: {}", e)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// keep-alive unset, rely on operating system
|
// keep-alive unset, rely on operating system
|
||||||
@ -267,9 +265,7 @@ where
|
|||||||
if not_ready {
|
if not_ready {
|
||||||
if self.tasks.is_empty() && self.flags.contains(Flags::DISCONNECTED)
|
if self.tasks.is_empty() && self.flags.contains(Flags::DISCONNECTED)
|
||||||
{
|
{
|
||||||
return conn
|
return conn.poll_close().map_err(|e| e.into());
|
||||||
.poll_close()
|
|
||||||
.map_err(|e| error!("Error during connection close: {}", e));
|
|
||||||
} else {
|
} else {
|
||||||
return Ok(Async::NotReady);
|
return Ok(Async::NotReady);
|
||||||
}
|
}
|
||||||
@ -284,7 +280,7 @@ where
|
|||||||
Ok(Async::NotReady) => return Ok(Async::NotReady),
|
Ok(Async::NotReady) => return Ok(Async::NotReady),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
trace!("Error handling connection: {}", err);
|
trace!("Error handling connection: {}", err);
|
||||||
return Err(());
|
return Err(err.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -18,7 +18,7 @@ use openssl::ssl::SslAcceptorBuilder;
|
|||||||
use rustls::ServerConfig;
|
use rustls::ServerConfig;
|
||||||
|
|
||||||
use super::acceptor::{AcceptorServiceFactory, DefaultAcceptor};
|
use super::acceptor::{AcceptorServiceFactory, DefaultAcceptor};
|
||||||
use super::builder::{DefaultPipelineFactory, HttpServiceBuilder, ServiceProvider};
|
use super::builder::{HttpServiceBuilder, ServiceProvider};
|
||||||
use super::{IntoHttpHandler, KeepAlive};
|
use super::{IntoHttpHandler, KeepAlive};
|
||||||
|
|
||||||
struct Socket {
|
struct Socket {
|
||||||
@ -131,7 +131,7 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set server client timneout in milliseconds for first request.
|
/// Set server client timeout in milliseconds for first request.
|
||||||
///
|
///
|
||||||
/// Defines a timeout for reading client request header. If a client does not transmit
|
/// Defines a timeout for reading client request header. If a client does not transmit
|
||||||
/// the entire set headers within this time, the request is terminated with
|
/// the entire set headers within this time, the request is terminated with
|
||||||
@ -218,11 +218,8 @@ where
|
|||||||
addr,
|
addr,
|
||||||
scheme: "http",
|
scheme: "http",
|
||||||
handler: Box::new(
|
handler: Box::new(
|
||||||
HttpServiceBuilder::new(
|
HttpServiceBuilder::new(self.factory.clone(), DefaultAcceptor)
|
||||||
self.factory.clone(),
|
.no_client_timer(),
|
||||||
DefaultAcceptor,
|
|
||||||
DefaultPipelineFactory::new(),
|
|
||||||
).no_client_timer(),
|
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -231,7 +228,7 @@ where
|
|||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
/// Use listener for accepting incoming connection requests
|
/// Use listener for accepting incoming connection requests
|
||||||
pub(crate) fn listen_with<A>(mut self, lst: net::TcpListener, acceptor: A) -> Self
|
pub fn listen_with<A>(mut self, lst: net::TcpListener, acceptor: A) -> Self
|
||||||
where
|
where
|
||||||
A: AcceptorServiceFactory,
|
A: AcceptorServiceFactory,
|
||||||
<A::NewService as NewService>::InitError: fmt::Debug,
|
<A::NewService as NewService>::InitError: fmt::Debug,
|
||||||
@ -241,11 +238,7 @@ where
|
|||||||
lst,
|
lst,
|
||||||
addr,
|
addr,
|
||||||
scheme: "https",
|
scheme: "https",
|
||||||
handler: Box::new(HttpServiceBuilder::new(
|
handler: Box::new(HttpServiceBuilder::new(self.factory.clone(), acceptor)),
|
||||||
self.factory.clone(),
|
|
||||||
acceptor,
|
|
||||||
DefaultPipelineFactory::new(),
|
|
||||||
)),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
self
|
self
|
||||||
@ -339,7 +332,6 @@ where
|
|||||||
handler: Box::new(HttpServiceBuilder::new(
|
handler: Box::new(HttpServiceBuilder::new(
|
||||||
self.factory.clone(),
|
self.factory.clone(),
|
||||||
acceptor.clone(),
|
acceptor.clone(),
|
||||||
DefaultPipelineFactory::new(),
|
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -483,10 +475,15 @@ impl<H: IntoHttpHandler, F: Fn() -> H + Send + Clone> HttpServer<H, F> {
|
|||||||
let sockets = mem::replace(&mut self.sockets, Vec::new());
|
let sockets = mem::replace(&mut self.sockets, Vec::new());
|
||||||
|
|
||||||
for socket in sockets {
|
for socket in sockets {
|
||||||
|
let host = self
|
||||||
|
.host
|
||||||
|
.as_ref()
|
||||||
|
.map(|h| h.to_owned())
|
||||||
|
.unwrap_or_else(|| format!("{}", socket.addr));
|
||||||
srv = socket.handler.register(
|
srv = socket.handler.register(
|
||||||
srv,
|
srv,
|
||||||
socket.lst,
|
socket.lst,
|
||||||
self.host.clone(),
|
host,
|
||||||
socket.addr,
|
socket.addr,
|
||||||
self.keep_alive.clone(),
|
self.keep_alive.clone(),
|
||||||
self.client_timeout,
|
self.client_timeout,
|
||||||
@ -524,10 +521,15 @@ impl<H: IntoHttpHandler, F: Fn() -> H + Send + Clone> HttpServer<H, F> {
|
|||||||
/// Register current http server as actix-net's server service
|
/// Register current http server as actix-net's server service
|
||||||
pub fn register(self, mut srv: Server) -> Server {
|
pub fn register(self, mut srv: Server) -> Server {
|
||||||
for socket in self.sockets {
|
for socket in self.sockets {
|
||||||
|
let host = self
|
||||||
|
.host
|
||||||
|
.as_ref()
|
||||||
|
.map(|h| h.to_owned())
|
||||||
|
.unwrap_or_else(|| format!("{}", socket.addr));
|
||||||
srv = socket.handler.register(
|
srv = socket.handler.register(
|
||||||
srv,
|
srv,
|
||||||
socket.lst,
|
socket.lst,
|
||||||
self.host.clone(),
|
host,
|
||||||
socket.addr,
|
socket.addr,
|
||||||
self.keep_alive.clone(),
|
self.keep_alive.clone(),
|
||||||
self.client_timeout,
|
self.client_timeout,
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
use std::{io, net};
|
use std::{io, net};
|
||||||
|
|
||||||
use actix::{Actor, Arbiter, AsyncContext, Context, Handler, Message};
|
use actix::{Actor, Arbiter, AsyncContext, Context, Handler, Message};
|
||||||
use futures::Stream;
|
use futures::{Future, Stream};
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use tokio_io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
use super::channel::{HttpChannel, WrapperStream};
|
use super::channel::{HttpChannel, WrapperStream};
|
||||||
@ -36,7 +36,7 @@ where
|
|||||||
apps,
|
apps,
|
||||||
self.keep_alive,
|
self.keep_alive,
|
||||||
self.client_timeout as u64,
|
self.client_timeout as u64,
|
||||||
ServerSettings::new(Some(addr), &self.host, secure),
|
ServerSettings::new(addr, "127.0.0.1:8080", secure),
|
||||||
);
|
);
|
||||||
|
|
||||||
// start server
|
// start server
|
||||||
@ -65,6 +65,8 @@ where
|
|||||||
type Result = ();
|
type Result = ();
|
||||||
|
|
||||||
fn handle(&mut self, msg: WrapperStream<T>, _: &mut Context<Self>) -> Self::Result {
|
fn handle(&mut self, msg: WrapperStream<T>, _: &mut Context<Self>) -> Self::Result {
|
||||||
Arbiter::spawn(HttpChannel::new(self.settings.clone(), msg, None));
|
Arbiter::spawn(
|
||||||
|
HttpChannel::new(self.settings.clone(), msg, None).map_err(|_| ()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,15 +140,15 @@ mod ssl;
|
|||||||
pub use self::handler::*;
|
pub use self::handler::*;
|
||||||
pub use self::http::HttpServer;
|
pub use self::http::HttpServer;
|
||||||
pub use self::message::Request;
|
pub use self::message::Request;
|
||||||
pub use self::settings::ServerSettings;
|
|
||||||
pub use self::ssl::*;
|
pub use self::ssl::*;
|
||||||
|
|
||||||
|
pub use self::error::{AcceptorError, HttpDispatchError};
|
||||||
|
pub use self::service::HttpService;
|
||||||
|
pub use self::settings::{ServerSettings, WorkerSettings};
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub use self::helpers::write_content_length;
|
pub use self::helpers::write_content_length;
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub use self::builder::HttpServiceBuilder;
|
|
||||||
|
|
||||||
use body::Binary;
|
use body::Binary;
|
||||||
use extensions::Extensions;
|
use extensions::Extensions;
|
||||||
use header::ContentEncoding;
|
use header::ContentEncoding;
|
||||||
|
@ -5,11 +5,12 @@ use futures::future::{ok, FutureResult};
|
|||||||
use futures::{Async, Poll};
|
use futures::{Async, Poll};
|
||||||
|
|
||||||
use super::channel::HttpChannel;
|
use super::channel::HttpChannel;
|
||||||
|
use super::error::HttpDispatchError;
|
||||||
use super::handler::HttpHandler;
|
use super::handler::HttpHandler;
|
||||||
use super::settings::WorkerSettings;
|
use super::settings::WorkerSettings;
|
||||||
use super::IoStream;
|
use super::IoStream;
|
||||||
|
|
||||||
pub(crate) struct HttpService<H, Io>
|
pub struct HttpService<H, Io>
|
||||||
where
|
where
|
||||||
H: HttpHandler,
|
H: HttpHandler,
|
||||||
Io: IoStream,
|
Io: IoStream,
|
||||||
@ -23,6 +24,7 @@ where
|
|||||||
H: HttpHandler,
|
H: HttpHandler,
|
||||||
Io: IoStream,
|
Io: IoStream,
|
||||||
{
|
{
|
||||||
|
/// Create new `HttpService` instance.
|
||||||
pub fn new(settings: WorkerSettings<H>) -> Self {
|
pub fn new(settings: WorkerSettings<H>) -> Self {
|
||||||
HttpService {
|
HttpService {
|
||||||
settings,
|
settings,
|
||||||
@ -38,17 +40,17 @@ where
|
|||||||
{
|
{
|
||||||
type Request = Io;
|
type Request = Io;
|
||||||
type Response = ();
|
type Response = ();
|
||||||
type Error = ();
|
type Error = HttpDispatchError;
|
||||||
type InitError = ();
|
type InitError = ();
|
||||||
type Service = HttpServiceHandler<H, Io>;
|
type Service = HttpServiceHandler<H, Io>;
|
||||||
type Future = FutureResult<Self::Service, Self::Error>;
|
type Future = FutureResult<Self::Service, Self::InitError>;
|
||||||
|
|
||||||
fn new_service(&self) -> Self::Future {
|
fn new_service(&self) -> Self::Future {
|
||||||
ok(HttpServiceHandler::new(self.settings.clone()))
|
ok(HttpServiceHandler::new(self.settings.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct HttpServiceHandler<H, Io>
|
pub struct HttpServiceHandler<H, Io>
|
||||||
where
|
where
|
||||||
H: HttpHandler,
|
H: HttpHandler,
|
||||||
Io: IoStream,
|
Io: IoStream,
|
||||||
@ -84,7 +86,7 @@ where
|
|||||||
{
|
{
|
||||||
type Request = Io;
|
type Request = Io;
|
||||||
type Response = ();
|
type Response = ();
|
||||||
type Error = ();
|
type Error = HttpDispatchError;
|
||||||
type Future = HttpChannel<Io, H>;
|
type Future = HttpChannel<Io, H>;
|
||||||
|
|
||||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||||
|
@ -43,7 +43,7 @@ lazy_static! {
|
|||||||
|
|
||||||
/// Various server settings
|
/// Various server settings
|
||||||
pub struct ServerSettings {
|
pub struct ServerSettings {
|
||||||
addr: Option<net::SocketAddr>,
|
addr: net::SocketAddr,
|
||||||
secure: bool,
|
secure: bool,
|
||||||
host: String,
|
host: String,
|
||||||
cpu_pool: LazyCell<CpuPool>,
|
cpu_pool: LazyCell<CpuPool>,
|
||||||
@ -65,7 +65,7 @@ impl Clone for ServerSettings {
|
|||||||
impl Default for ServerSettings {
|
impl Default for ServerSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
ServerSettings {
|
ServerSettings {
|
||||||
addr: None,
|
addr: "127.0.0.1:8080".parse().unwrap(),
|
||||||
secure: false,
|
secure: false,
|
||||||
host: "localhost:8080".to_owned(),
|
host: "localhost:8080".to_owned(),
|
||||||
responses: HttpResponsePool::get_pool(),
|
responses: HttpResponsePool::get_pool(),
|
||||||
@ -76,16 +76,8 @@ impl Default for ServerSettings {
|
|||||||
|
|
||||||
impl ServerSettings {
|
impl ServerSettings {
|
||||||
/// Crate server settings instance
|
/// Crate server settings instance
|
||||||
pub(crate) fn new(
|
pub fn new(addr: net::SocketAddr, host: &str, secure: bool) -> ServerSettings {
|
||||||
addr: Option<net::SocketAddr>, host: &Option<String>, secure: bool,
|
let host = host.to_owned();
|
||||||
) -> ServerSettings {
|
|
||||||
let host = if let Some(ref host) = *host {
|
|
||||||
host.clone()
|
|
||||||
} else if let Some(ref addr) = addr {
|
|
||||||
format!("{}", addr)
|
|
||||||
} else {
|
|
||||||
"localhost".to_owned()
|
|
||||||
};
|
|
||||||
let cpu_pool = LazyCell::new();
|
let cpu_pool = LazyCell::new();
|
||||||
let responses = HttpResponsePool::get_pool();
|
let responses = HttpResponsePool::get_pool();
|
||||||
ServerSettings {
|
ServerSettings {
|
||||||
@ -98,7 +90,7 @@ impl ServerSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the socket address of the local half of this TCP connection
|
/// Returns the socket address of the local half of this TCP connection
|
||||||
pub fn local_addr(&self) -> Option<net::SocketAddr> {
|
pub fn local_addr(&self) -> net::SocketAddr {
|
||||||
self.addr
|
self.addr
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +145,7 @@ impl<H> Clone for WorkerSettings<H> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<H> WorkerSettings<H> {
|
impl<H> WorkerSettings<H> {
|
||||||
pub(crate) fn new(
|
pub fn new(
|
||||||
handler: H, keep_alive: KeepAlive, client_timeout: u64, settings: ServerSettings,
|
handler: H, keep_alive: KeepAlive, client_timeout: u64, settings: ServerSettings,
|
||||||
) -> WorkerSettings<H> {
|
) -> WorkerSettings<H> {
|
||||||
let (keep_alive, ka_enabled) = match keep_alive {
|
let (keep_alive, ka_enabled) = match keep_alive {
|
||||||
@ -188,11 +180,13 @@ impl<H> WorkerSettings<H> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
/// Keep alive duration if configured.
|
||||||
pub fn keep_alive(&self) -> Option<Duration> {
|
pub fn keep_alive(&self) -> Option<Duration> {
|
||||||
self.0.keep_alive
|
self.0.keep_alive
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
/// Return state of connection keep-alive funcitonality
|
||||||
pub fn keep_alive_enabled(&self) -> bool {
|
pub fn keep_alive_enabled(&self) -> bool {
|
||||||
self.0.ka_enabled
|
self.0.ka_enabled
|
||||||
}
|
}
|
||||||
@ -217,6 +211,7 @@ impl<H> WorkerSettings<H> {
|
|||||||
|
|
||||||
impl<H: 'static> WorkerSettings<H> {
|
impl<H: 'static> WorkerSettings<H> {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
/// Client timeout for first request.
|
||||||
pub fn client_timer(&self) -> Option<Delay> {
|
pub fn client_timer(&self) -> Option<Delay> {
|
||||||
let delay = self.0.client_timeout;
|
let delay = self.0.client_timeout;
|
||||||
if delay != 0 {
|
if delay != 0 {
|
||||||
@ -227,6 +222,7 @@ impl<H: 'static> WorkerSettings<H> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
/// Return keep-alive timer delay is configured.
|
||||||
pub fn keep_alive_timer(&self) -> Option<Delay> {
|
pub fn keep_alive_timer(&self) -> Option<Delay> {
|
||||||
if let Some(ka) = self.0.keep_alive {
|
if let Some(ka) = self.0.keep_alive {
|
||||||
Some(Delay::new(self.now() + ka))
|
Some(Delay::new(self.now() + ka))
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
extern crate actix;
|
extern crate actix;
|
||||||
|
extern crate actix_net;
|
||||||
extern crate actix_web;
|
extern crate actix_web;
|
||||||
#[cfg(feature = "brotli")]
|
#[cfg(feature = "brotli")]
|
||||||
extern crate brotli2;
|
extern crate brotli2;
|
||||||
@ -18,6 +19,7 @@ use std::io::{Read, Write};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{thread, time};
|
use std::{thread, time};
|
||||||
|
|
||||||
|
use actix_net::server::Server;
|
||||||
#[cfg(feature = "brotli")]
|
#[cfg(feature = "brotli")]
|
||||||
use brotli2::write::{BrotliDecoder, BrotliEncoder};
|
use brotli2::write::{BrotliDecoder, BrotliEncoder};
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
@ -1010,3 +1012,38 @@ fn test_server_cookies() {
|
|||||||
assert_eq!(cookies[1], first_cookie);
|
assert_eq!(cookies[1], first_cookie);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_custom_pipeline() {
|
||||||
|
use actix::System;
|
||||||
|
use actix_web::server::{HttpService, KeepAlive, ServerSettings, WorkerSettings};
|
||||||
|
|
||||||
|
let addr = test::TestServer::unused_addr();
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
Server::new()
|
||||||
|
.bind("test", addr, move || {
|
||||||
|
let app = App::new()
|
||||||
|
.route("/", http::Method::GET, |_: HttpRequest| "OK")
|
||||||
|
.finish();
|
||||||
|
let settings = WorkerSettings::new(
|
||||||
|
app,
|
||||||
|
KeepAlive::Disabled,
|
||||||
|
10,
|
||||||
|
ServerSettings::new(addr, "localhost", false),
|
||||||
|
);
|
||||||
|
|
||||||
|
HttpService::new(settings)
|
||||||
|
}).unwrap()
|
||||||
|
.run();
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut sys = System::new("test");
|
||||||
|
{
|
||||||
|
let req = client::ClientRequest::get(format!("http://{}/", addr).as_str())
|
||||||
|
.finish()
|
||||||
|
.unwrap();
|
||||||
|
let response = sys.block_on(req.send()).unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user