mirror of
https://github.com/fafhrd91/actix-web
synced 2024-11-24 08:22:59 +01:00
refactor keep-alive; update guide
This commit is contained in:
parent
653b431895
commit
c2751efa87
2
build.rs
2
build.rs
@ -15,10 +15,10 @@ fn main() {
|
|||||||
"guide/src/qs_1.md",
|
"guide/src/qs_1.md",
|
||||||
"guide/src/qs_2.md",
|
"guide/src/qs_2.md",
|
||||||
"guide/src/qs_3.md",
|
"guide/src/qs_3.md",
|
||||||
|
"guide/src/qs_3_5.md",
|
||||||
"guide/src/qs_4.md",
|
"guide/src/qs_4.md",
|
||||||
"guide/src/qs_4_5.md",
|
"guide/src/qs_4_5.md",
|
||||||
"guide/src/qs_5.md",
|
"guide/src/qs_5.md",
|
||||||
"guide/src/qs_6.md",
|
|
||||||
"guide/src/qs_7.md",
|
"guide/src/qs_7.md",
|
||||||
"guide/src/qs_9.md",
|
"guide/src/qs_9.md",
|
||||||
"guide/src/qs_10.md",
|
"guide/src/qs_10.md",
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
[Quickstart](./qs_1.md)
|
[Quickstart](./qs_1.md)
|
||||||
- [Getting Started](./qs_2.md)
|
- [Getting Started](./qs_2.md)
|
||||||
- [Application](./qs_3.md)
|
- [Application](./qs_3.md)
|
||||||
|
- [Server](./qs_3_5.md)
|
||||||
- [Handler](./qs_4.md)
|
- [Handler](./qs_4.md)
|
||||||
- [Errors](./qs_4_5.md)
|
- [Errors](./qs_4_5.md)
|
||||||
- [State](./qs_6.md)
|
|
||||||
- [URL Dispatch](./qs_5.md)
|
- [URL Dispatch](./qs_5.md)
|
||||||
- [Request & Response](./qs_7.md)
|
- [Request & Response](./qs_7.md)
|
||||||
- [WebSockets](./qs_9.md)
|
- [WebSockets](./qs_9.md)
|
||||||
|
@ -56,3 +56,46 @@ fn main() {
|
|||||||
```
|
```
|
||||||
|
|
||||||
All `/app1` requests route to first application, `/app2` to second and then all other to third.
|
All `/app1` requests route to first application, `/app2` to second and then all other to third.
|
||||||
|
|
||||||
|
## State
|
||||||
|
|
||||||
|
Application state is shared with all routes and resources within same application.
|
||||||
|
State could be accessed with `HttpRequest::state()` method as a read-only item
|
||||||
|
but interior mutability pattern with `RefCell` could be used to archive state mutability.
|
||||||
|
State could be accessed with `HttpContext::state()` in case of http actor.
|
||||||
|
State also available to route matching predicates and middlewares.
|
||||||
|
|
||||||
|
Let's write simple application that uses shared state. We are going to store requests count
|
||||||
|
in the state:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# extern crate actix;
|
||||||
|
# extern crate actix_web;
|
||||||
|
#
|
||||||
|
use actix_web::*;
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
// This struct represents state
|
||||||
|
struct AppState {
|
||||||
|
counter: Cell<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn index(req: HttpRequest<AppState>) -> String {
|
||||||
|
let count = req.state().counter.get() + 1; // <- get count
|
||||||
|
req.state().counter.set(count); // <- store new count in state
|
||||||
|
|
||||||
|
format!("Request number: {}", count) // <- response with count
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
Application::with_state(AppState{counter: Cell::new(0)})
|
||||||
|
.resource("/", |r| r.method(Method::GET).f(index))
|
||||||
|
.finish();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note on application state, http server accepts application factory rather than application
|
||||||
|
instance. Http server construct application instance for each thread, so application state
|
||||||
|
must be constructed multiple times. If you want to share state between different thread
|
||||||
|
shared object should be used, like `Arc`. Application state does not need to be `Send` and `Sync`
|
||||||
|
but application factory must be `Send` + `Sync`.
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
# Application state
|
|
||||||
|
|
||||||
Application state is shared with all routes and resources within same application.
|
|
||||||
State could be accessed with `HttpRequest::state()` method as a read-only item
|
|
||||||
but interior mutability pattern with `RefCell` could be used to archive state mutability.
|
|
||||||
State could be accessed with `HttpContext::state()` in case of http actor.
|
|
||||||
State also available to route matching predicates. State is not available
|
|
||||||
to application middlewares, middlewares receives `HttpRequest<()>` object.
|
|
||||||
|
|
||||||
Let's write simple application that uses shared state. We are going to store requests count
|
|
||||||
in the state:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
# extern crate actix;
|
|
||||||
# extern crate actix_web;
|
|
||||||
#
|
|
||||||
use actix_web::*;
|
|
||||||
use std::cell::Cell;
|
|
||||||
|
|
||||||
// This struct represents state
|
|
||||||
struct AppState {
|
|
||||||
counter: Cell<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index(req: HttpRequest<AppState>) -> String {
|
|
||||||
let count = req.state().counter.get() + 1; // <- get count
|
|
||||||
req.state().counter.set(count); // <- store new count in state
|
|
||||||
|
|
||||||
format!("Request number: {}", count) // <- response with count
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
Application::with_state(AppState{counter: Cell::new(0)})
|
|
||||||
.resource("/", |r| r.method(Method::GET).f(index))
|
|
||||||
.finish();
|
|
||||||
}
|
|
||||||
```
|
|
@ -11,7 +11,7 @@ use h2;
|
|||||||
use error::Error;
|
use error::Error;
|
||||||
use h1writer::Writer;
|
use h1writer::Writer;
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
use server::ServerSettings;
|
use server::{ServerSettings, WorkerSettings};
|
||||||
|
|
||||||
/// Low level http request handler
|
/// Low level http request handler
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
@ -67,7 +67,8 @@ pub struct HttpChannel<T, H>
|
|||||||
impl<T, H> HttpChannel<T, H>
|
impl<T, H> HttpChannel<T, H>
|
||||||
where T: AsyncRead + AsyncWrite + 'static, H: HttpHandler + 'static
|
where T: AsyncRead + AsyncWrite + 'static, H: HttpHandler + 'static
|
||||||
{
|
{
|
||||||
pub fn new(h: Rc<Vec<H>>, io: T, peer: Option<SocketAddr>, http2: bool) -> HttpChannel<T, H>
|
pub(crate) fn new(h: Rc<WorkerSettings<H>>,
|
||||||
|
io: T, peer: Option<SocketAddr>, http2: bool) -> HttpChannel<T, H>
|
||||||
{
|
{
|
||||||
if http2 {
|
if http2 {
|
||||||
HttpChannel {
|
HttpChannel {
|
||||||
|
41
src/h1.rs
41
src/h1.rs
@ -17,12 +17,12 @@ use pipeline::Pipeline;
|
|||||||
use encoding::PayloadType;
|
use encoding::PayloadType;
|
||||||
use channel::{HttpHandler, HttpHandlerTask};
|
use channel::{HttpHandler, HttpHandlerTask};
|
||||||
use h1writer::H1Writer;
|
use h1writer::H1Writer;
|
||||||
|
use server::WorkerSettings;
|
||||||
use httpcodes::HTTPNotFound;
|
use httpcodes::HTTPNotFound;
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
use error::{ParseError, PayloadError, ResponseError};
|
use error::{ParseError, PayloadError, ResponseError};
|
||||||
use payload::{Payload, PayloadWriter, DEFAULT_BUFFER_SIZE};
|
use payload::{Payload, PayloadWriter, DEFAULT_BUFFER_SIZE};
|
||||||
|
|
||||||
const KEEPALIVE_PERIOD: u64 = 15; // seconds
|
|
||||||
const INIT_BUFFER_SIZE: usize = 8192;
|
const INIT_BUFFER_SIZE: usize = 8192;
|
||||||
const MAX_BUFFER_SIZE: usize = 131_072;
|
const MAX_BUFFER_SIZE: usize = 131_072;
|
||||||
const MAX_HEADERS: usize = 100;
|
const MAX_HEADERS: usize = 100;
|
||||||
@ -59,7 +59,7 @@ enum Item {
|
|||||||
|
|
||||||
pub(crate) struct Http1<T: AsyncWrite + 'static, H: 'static> {
|
pub(crate) struct Http1<T: AsyncWrite + 'static, H: 'static> {
|
||||||
flags: Flags,
|
flags: Flags,
|
||||||
handlers: Rc<Vec<H>>,
|
settings: Rc<WorkerSettings<H>>,
|
||||||
addr: Option<SocketAddr>,
|
addr: Option<SocketAddr>,
|
||||||
stream: H1Writer<T>,
|
stream: H1Writer<T>,
|
||||||
reader: Reader,
|
reader: Reader,
|
||||||
@ -77,9 +77,9 @@ impl<T, H> Http1<T, H>
|
|||||||
where T: AsyncRead + AsyncWrite + 'static,
|
where T: AsyncRead + AsyncWrite + 'static,
|
||||||
H: HttpHandler + 'static
|
H: HttpHandler + 'static
|
||||||
{
|
{
|
||||||
pub fn new(h: Rc<Vec<H>>, stream: T, addr: Option<SocketAddr>) -> Self {
|
pub fn new(h: Rc<WorkerSettings<H>>, stream: T, addr: Option<SocketAddr>) -> Self {
|
||||||
Http1{ flags: Flags::KEEPALIVE,
|
Http1{ flags: Flags::KEEPALIVE,
|
||||||
handlers: h,
|
settings: h,
|
||||||
addr: addr,
|
addr: addr,
|
||||||
stream: H1Writer::new(stream),
|
stream: H1Writer::new(stream),
|
||||||
reader: Reader::new(),
|
reader: Reader::new(),
|
||||||
@ -88,8 +88,8 @@ impl<T, H> Http1<T, H>
|
|||||||
keepalive_timer: None }
|
keepalive_timer: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_inner(self) -> (Rc<Vec<H>>, T, Option<SocketAddr>, Bytes) {
|
pub fn into_inner(self) -> (Rc<WorkerSettings<H>>, T, Option<SocketAddr>, Bytes) {
|
||||||
(self.handlers, self.stream.into_inner(), self.addr, self.read_buf.freeze())
|
(self.settings, self.stream.into_inner(), self.addr, self.read_buf.freeze())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll(&mut self) -> Poll<Http1Result, ()> {
|
pub fn poll(&mut self) -> Poll<Http1Result, ()> {
|
||||||
@ -198,7 +198,7 @@ impl<T, H> Http1<T, H>
|
|||||||
|
|
||||||
// start request processing
|
// start request processing
|
||||||
let mut pipe = None;
|
let mut pipe = None;
|
||||||
for h in self.handlers.iter() {
|
for h in self.settings.handlers().iter() {
|
||||||
req = match h.handle(req) {
|
req = match h.handle(req) {
|
||||||
Ok(t) => {
|
Ok(t) => {
|
||||||
pipe = Some(t);
|
pipe = Some(t);
|
||||||
@ -249,19 +249,24 @@ impl<T, H> Http1<T, H>
|
|||||||
Ok(Async::NotReady) => {
|
Ok(Async::NotReady) => {
|
||||||
// start keep-alive timer, this is also slow request timeout
|
// start keep-alive timer, this is also slow request timeout
|
||||||
if self.tasks.is_empty() {
|
if self.tasks.is_empty() {
|
||||||
if self.flags.contains(Flags::KEEPALIVE) {
|
if let Some(keep_alive) = self.settings.keep_alive() {
|
||||||
if self.keepalive_timer.is_none() {
|
if keep_alive > 0 && self.flags.contains(Flags::KEEPALIVE) {
|
||||||
trace!("Start keep-alive timer");
|
if self.keepalive_timer.is_none() {
|
||||||
let mut to = Timeout::new(
|
trace!("Start keep-alive timer");
|
||||||
Duration::new(KEEPALIVE_PERIOD, 0),
|
let mut to = Timeout::new(
|
||||||
Arbiter::handle()).unwrap();
|
Duration::new(keep_alive as u64, 0),
|
||||||
// register timeout
|
Arbiter::handle()).unwrap();
|
||||||
let _ = to.poll();
|
// register timeout
|
||||||
self.keepalive_timer = Some(to);
|
let _ = to.poll();
|
||||||
|
self.keepalive_timer = Some(to);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// keep-alive disable, drop connection
|
||||||
|
return Ok(Async::Ready(Http1Result::Done))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// keep-alive disable, drop connection
|
// keep-alive unset, rely on operating system
|
||||||
return Ok(Async::Ready(Http1Result::Done))
|
return Ok(Async::NotReady)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
43
src/h2.rs
43
src/h2.rs
@ -16,6 +16,7 @@ use tokio_core::reactor::Timeout;
|
|||||||
|
|
||||||
use pipeline::Pipeline;
|
use pipeline::Pipeline;
|
||||||
use h2writer::H2Writer;
|
use h2writer::H2Writer;
|
||||||
|
use server::WorkerSettings;
|
||||||
use channel::{HttpHandler, HttpHandlerTask};
|
use channel::{HttpHandler, HttpHandlerTask};
|
||||||
use error::PayloadError;
|
use error::PayloadError;
|
||||||
use encoding::PayloadType;
|
use encoding::PayloadType;
|
||||||
@ -23,8 +24,6 @@ use httpcodes::HTTPNotFound;
|
|||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
use payload::{Payload, PayloadWriter};
|
use payload::{Payload, PayloadWriter};
|
||||||
|
|
||||||
const KEEPALIVE_PERIOD: u64 = 15; // seconds
|
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
struct Flags: u8 {
|
struct Flags: u8 {
|
||||||
const DISCONNECTED = 0b0000_0010;
|
const DISCONNECTED = 0b0000_0010;
|
||||||
@ -36,7 +35,7 @@ pub(crate) struct Http2<T, H>
|
|||||||
where T: AsyncRead + AsyncWrite + 'static, H: 'static
|
where T: AsyncRead + AsyncWrite + 'static, H: 'static
|
||||||
{
|
{
|
||||||
flags: Flags,
|
flags: Flags,
|
||||||
handlers: Rc<Vec<H>>,
|
settings: Rc<WorkerSettings<H>>,
|
||||||
addr: Option<SocketAddr>,
|
addr: Option<SocketAddr>,
|
||||||
state: State<IoWrapper<T>>,
|
state: State<IoWrapper<T>>,
|
||||||
tasks: VecDeque<Entry>,
|
tasks: VecDeque<Entry>,
|
||||||
@ -53,14 +52,14 @@ impl<T, H> Http2<T, H>
|
|||||||
where T: AsyncRead + AsyncWrite + 'static,
|
where T: AsyncRead + AsyncWrite + 'static,
|
||||||
H: HttpHandler + 'static
|
H: HttpHandler + 'static
|
||||||
{
|
{
|
||||||
pub fn new(h: Rc<Vec<H>>, stream: T, addr: Option<SocketAddr>, buf: Bytes) -> Self
|
pub fn new(h: Rc<WorkerSettings<H>>, io: T, addr: Option<SocketAddr>, buf: Bytes) -> Self
|
||||||
{
|
{
|
||||||
Http2{ flags: Flags::empty(),
|
Http2{ flags: Flags::empty(),
|
||||||
handlers: h,
|
settings: h,
|
||||||
addr: addr,
|
addr: addr,
|
||||||
tasks: VecDeque::new(),
|
tasks: VecDeque::new(),
|
||||||
state: State::Handshake(
|
state: State::Handshake(
|
||||||
Server::handshake(IoWrapper{unread: Some(buf), inner: stream})),
|
Server::handshake(IoWrapper{unread: Some(buf), inner: io})),
|
||||||
keepalive_timer: None,
|
keepalive_timer: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -151,18 +150,28 @@ impl<T, H> Http2<T, H>
|
|||||||
self.keepalive_timer.take();
|
self.keepalive_timer.take();
|
||||||
|
|
||||||
self.tasks.push_back(
|
self.tasks.push_back(
|
||||||
Entry::new(parts, body, resp, self.addr, &self.handlers));
|
Entry::new(parts, body, resp, self.addr, &self.settings));
|
||||||
}
|
}
|
||||||
Ok(Async::NotReady) => {
|
Ok(Async::NotReady) => {
|
||||||
// start keep-alive timer
|
// start keep-alive timer
|
||||||
if self.tasks.is_empty() && self.keepalive_timer.is_none() {
|
if self.tasks.is_empty() {
|
||||||
trace!("Start keep-alive timer");
|
if let Some(keep_alive) = self.settings.keep_alive() {
|
||||||
let mut timeout = Timeout::new(
|
if keep_alive > 0 && self.keepalive_timer.is_none() {
|
||||||
Duration::new(KEEPALIVE_PERIOD, 0),
|
trace!("Start keep-alive timer");
|
||||||
Arbiter::handle()).unwrap();
|
let mut timeout = Timeout::new(
|
||||||
// register timeout
|
Duration::new(keep_alive as u64, 0),
|
||||||
let _ = timeout.poll();
|
Arbiter::handle()).unwrap();
|
||||||
self.keepalive_timer = Some(timeout);
|
// register timeout
|
||||||
|
let _ = timeout.poll();
|
||||||
|
self.keepalive_timer = Some(timeout);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// keep-alive disable, drop connection
|
||||||
|
return Ok(Async::Ready(()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// keep-alive unset, rely on operating system
|
||||||
|
return Ok(Async::NotReady)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@ -230,7 +239,7 @@ impl Entry {
|
|||||||
recv: RecvStream,
|
recv: RecvStream,
|
||||||
resp: Respond<Bytes>,
|
resp: Respond<Bytes>,
|
||||||
addr: Option<SocketAddr>,
|
addr: Option<SocketAddr>,
|
||||||
handlers: &Rc<Vec<H>>) -> Entry
|
settings: &Rc<WorkerSettings<H>>) -> Entry
|
||||||
where H: HttpHandler + 'static
|
where H: HttpHandler + 'static
|
||||||
{
|
{
|
||||||
// Payload and Content-Encoding
|
// Payload and Content-Encoding
|
||||||
@ -247,7 +256,7 @@ impl Entry {
|
|||||||
|
|
||||||
// start request processing
|
// start request processing
|
||||||
let mut task = None;
|
let mut task = None;
|
||||||
for h in handlers.iter() {
|
for h in settings.handlers().iter() {
|
||||||
req = match h.handle(req) {
|
req = match h.handle(req) {
|
||||||
Ok(t) => {
|
Ok(t) => {
|
||||||
task = Some(t);
|
task = Some(t);
|
||||||
|
@ -110,6 +110,7 @@ pub mod headers {
|
|||||||
//! Headers implementation
|
//! Headers implementation
|
||||||
|
|
||||||
pub use encoding::ContentEncoding;
|
pub use encoding::ContentEncoding;
|
||||||
|
pub use httpresponse::ConnectionType;
|
||||||
|
|
||||||
pub use cookie::Cookie;
|
pub use cookie::Cookie;
|
||||||
pub use cookie::CookieBuilder;
|
pub use cookie::CookieBuilder;
|
||||||
|
@ -90,10 +90,11 @@ impl ServerSettings {
|
|||||||
pub struct HttpServer<T, A, H, U>
|
pub struct HttpServer<T, A, H, U>
|
||||||
where H: 'static
|
where H: 'static
|
||||||
{
|
{
|
||||||
h: Rc<Vec<H>>,
|
h: Option<Rc<WorkerSettings<H>>>,
|
||||||
io: PhantomData<T>,
|
io: PhantomData<T>,
|
||||||
addr: PhantomData<A>,
|
addr: PhantomData<A>,
|
||||||
threads: usize,
|
threads: usize,
|
||||||
|
keep_alive: Option<u16>,
|
||||||
factory: Arc<Fn() -> U + Send + Sync>,
|
factory: Arc<Fn() -> U + Send + Sync>,
|
||||||
workers: Vec<SyncAddress<Worker<H>>>,
|
workers: Vec<SyncAddress<Worker<H>>>,
|
||||||
}
|
}
|
||||||
@ -124,10 +125,11 @@ impl<T, A, H, U, V> HttpServer<T, A, H, U>
|
|||||||
pub fn new<F>(factory: F) -> Self
|
pub fn new<F>(factory: F) -> Self
|
||||||
where F: Sync + Send + 'static + Fn() -> U,
|
where F: Sync + Send + 'static + Fn() -> U,
|
||||||
{
|
{
|
||||||
HttpServer{ h: Rc::new(Vec::new()),
|
HttpServer{ h: None,
|
||||||
io: PhantomData,
|
io: PhantomData,
|
||||||
addr: PhantomData,
|
addr: PhantomData,
|
||||||
threads: num_cpus::get(),
|
threads: num_cpus::get(),
|
||||||
|
keep_alive: None,
|
||||||
factory: Arc::new(factory),
|
factory: Arc::new(factory),
|
||||||
workers: Vec::new(),
|
workers: Vec::new(),
|
||||||
}
|
}
|
||||||
@ -141,6 +143,20 @@ impl<T, A, H, U, V> HttpServer<T, A, H, U>
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set server keep-alive setting.
|
||||||
|
///
|
||||||
|
/// By default keep alive is enabled.
|
||||||
|
///
|
||||||
|
/// - `Some(75)` - enable
|
||||||
|
///
|
||||||
|
/// - `Some(0)` - disable
|
||||||
|
///
|
||||||
|
/// - `None` - use `SO_KEEPALIVE` socket option
|
||||||
|
pub fn keep_alive(mut self, val: Option<u16>) -> Self {
|
||||||
|
self.keep_alive = val;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Start listening for incomming connections from a stream.
|
/// Start listening for incomming connections from a stream.
|
||||||
///
|
///
|
||||||
/// This method uses only one thread for handling incoming connections.
|
/// This method uses only one thread for handling incoming connections.
|
||||||
@ -155,7 +171,7 @@ impl<T, A, H, U, V> HttpServer<T, A, H, U>
|
|||||||
for app in &mut apps {
|
for app in &mut apps {
|
||||||
app.server_settings(settings.clone());
|
app.server_settings(settings.clone());
|
||||||
}
|
}
|
||||||
self.h = Rc::new(apps);
|
self.h = Some(Rc::new(WorkerSettings{h: apps, keep_alive: self.keep_alive}));
|
||||||
|
|
||||||
// start server
|
// start server
|
||||||
Ok(HttpServer::create(move |ctx| {
|
Ok(HttpServer::create(move |ctx| {
|
||||||
@ -215,15 +231,16 @@ impl<T, A, H, U, V> HttpServer<T, A, H, U>
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn start_workers(&mut self, settings: &ServerSettings, handler: &StreamHandlerType)
|
fn start_workers(&mut self, settings: &ServerSettings, handler: &StreamHandlerType)
|
||||||
-> Vec<mpsc::UnboundedSender<IoStream<net::TcpStream>>>
|
-> Vec<mpsc::UnboundedSender<IoStream<Socket>>>
|
||||||
{
|
{
|
||||||
// start workers
|
// start workers
|
||||||
let mut workers = Vec::new();
|
let mut workers = Vec::new();
|
||||||
for _ in 0..self.threads {
|
for _ in 0..self.threads {
|
||||||
let s = settings.clone();
|
let s = settings.clone();
|
||||||
let (tx, rx) = mpsc::unbounded::<IoStream<net::TcpStream>>();
|
let (tx, rx) = mpsc::unbounded::<IoStream<Socket>>();
|
||||||
|
|
||||||
let h = handler.clone();
|
let h = handler.clone();
|
||||||
|
let ka = self.keep_alive.clone();
|
||||||
let factory = Arc::clone(&self.factory);
|
let factory = Arc::clone(&self.factory);
|
||||||
let addr = Arbiter::start(move |ctx: &mut Context<_>| {
|
let addr = Arbiter::start(move |ctx: &mut Context<_>| {
|
||||||
let mut apps: Vec<_> = (*factory)()
|
let mut apps: Vec<_> = (*factory)()
|
||||||
@ -232,7 +249,7 @@ impl<T, A, H, U, V> HttpServer<T, A, H, U>
|
|||||||
app.server_settings(s.clone());
|
app.server_settings(s.clone());
|
||||||
}
|
}
|
||||||
ctx.add_stream(rx);
|
ctx.add_stream(rx);
|
||||||
Worker{h: Rc::new(apps), handler: h}
|
Worker::new(apps, h, ka)
|
||||||
});
|
});
|
||||||
workers.push(tx);
|
workers.push(tx);
|
||||||
self.workers.push(addr);
|
self.workers.push(addr);
|
||||||
@ -379,7 +396,7 @@ impl<T, A, H, U> Handler<IoStream<T>, io::Error> for HttpServer<T, A, H, U>
|
|||||||
-> Response<Self, IoStream<T>>
|
-> Response<Self, IoStream<T>>
|
||||||
{
|
{
|
||||||
Arbiter::handle().spawn(
|
Arbiter::handle().spawn(
|
||||||
HttpChannel::new(Rc::clone(&self.h), msg.io, msg.peer, msg.http2));
|
HttpChannel::new(Rc::clone(&self.h.as_ref().unwrap()), msg.io, msg.peer, msg.http2));
|
||||||
Self::empty()
|
Self::empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -389,11 +406,33 @@ impl<T, A, H, U> Handler<IoStream<T>, io::Error> for HttpServer<T, A, H, U>
|
|||||||
///
|
///
|
||||||
/// Worker accepts Socket objects via unbounded channel and start requests processing.
|
/// Worker accepts Socket objects via unbounded channel and start requests processing.
|
||||||
struct Worker<H> {
|
struct Worker<H> {
|
||||||
h: Rc<Vec<H>>,
|
h: Rc<WorkerSettings<H>>,
|
||||||
handler: StreamHandlerType,
|
handler: StreamHandlerType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct WorkerSettings<H> {
|
||||||
|
h: Vec<H>,
|
||||||
|
keep_alive: Option<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H> WorkerSettings<H> {
|
||||||
|
pub fn handlers(&self) -> &Vec<H> {
|
||||||
|
&self.h
|
||||||
|
}
|
||||||
|
pub fn keep_alive(&self) -> Option<u16> {
|
||||||
|
self.keep_alive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<H: 'static> Worker<H> {
|
impl<H: 'static> Worker<H> {
|
||||||
|
|
||||||
|
fn new(h: Vec<H>, handler: StreamHandlerType, keep_alive: Option<u16>) -> Worker<H> {
|
||||||
|
Worker {
|
||||||
|
h: Rc::new(WorkerSettings{h: h, keep_alive: keep_alive}),
|
||||||
|
handler: handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn update_time(&self, ctx: &mut Context<Self>) {
|
fn update_time(&self, ctx: &mut Context<Self>) {
|
||||||
utils::update_date();
|
utils::update_date();
|
||||||
ctx.run_later(Duration::new(1, 0), |slf, ctx| slf.update_time(ctx));
|
ctx.run_later(Duration::new(1, 0), |slf, ctx| slf.update_time(ctx));
|
||||||
@ -408,15 +447,20 @@ impl<H: 'static> Actor for Worker<H> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H> StreamHandler<IoStream<net::TcpStream>> for Worker<H>
|
impl<H> StreamHandler<IoStream<Socket>> for Worker<H>
|
||||||
where H: HttpHandler + 'static {}
|
where H: HttpHandler + 'static {}
|
||||||
|
|
||||||
impl<H> Handler<IoStream<net::TcpStream>> for Worker<H>
|
impl<H> Handler<IoStream<Socket>> for Worker<H>
|
||||||
where H: HttpHandler + 'static,
|
where H: HttpHandler + 'static,
|
||||||
{
|
{
|
||||||
fn handle(&mut self, msg: IoStream<net::TcpStream>, _: &mut Context<Self>)
|
fn handle(&mut self, msg: IoStream<Socket>, _: &mut Context<Self>)
|
||||||
-> Response<Self, IoStream<net::TcpStream>>
|
-> Response<Self, IoStream<Socket>>
|
||||||
{
|
{
|
||||||
|
if let None = self.h.keep_alive {
|
||||||
|
if msg.io.set_keepalive(Some(Duration::new(75, 0))).is_err() {
|
||||||
|
error!("Can not set socket keep-alive option");
|
||||||
|
}
|
||||||
|
}
|
||||||
self.handler.handle(Rc::clone(&self.h), msg);
|
self.handler.handle(Rc::clone(&self.h), msg);
|
||||||
Self::empty()
|
Self::empty()
|
||||||
}
|
}
|
||||||
@ -432,10 +476,11 @@ enum StreamHandlerType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StreamHandlerType {
|
impl StreamHandlerType {
|
||||||
fn handle<H: HttpHandler>(&mut self, h: Rc<Vec<H>>, msg: IoStream<net::TcpStream>) {
|
|
||||||
|
fn handle<H: HttpHandler>(&mut self, h: Rc<WorkerSettings<H>>, msg: IoStream<Socket>) {
|
||||||
match *self {
|
match *self {
|
||||||
StreamHandlerType::Normal => {
|
StreamHandlerType::Normal => {
|
||||||
let io = TcpStream::from_stream(msg.io, Arbiter::handle())
|
let io = TcpStream::from_stream(msg.io.into_tcp_stream(), Arbiter::handle())
|
||||||
.expect("failed to associate TCP stream");
|
.expect("failed to associate TCP stream");
|
||||||
|
|
||||||
Arbiter::handle().spawn(HttpChannel::new(h, io, msg.peer, msg.http2));
|
Arbiter::handle().spawn(HttpChannel::new(h, io, msg.peer, msg.http2));
|
||||||
@ -443,7 +488,7 @@ impl StreamHandlerType {
|
|||||||
#[cfg(feature="tls")]
|
#[cfg(feature="tls")]
|
||||||
StreamHandlerType::Tls(ref acceptor) => {
|
StreamHandlerType::Tls(ref acceptor) => {
|
||||||
let IoStream { io, peer, http2 } = msg;
|
let IoStream { io, peer, http2 } = msg;
|
||||||
let io = TcpStream::from_stream(io, Arbiter::handle())
|
let io = TcpStream::from_stream(io.into_tcp_stream(), Arbiter::handle())
|
||||||
.expect("failed to associate TCP stream");
|
.expect("failed to associate TCP stream");
|
||||||
|
|
||||||
Arbiter::handle().spawn(
|
Arbiter::handle().spawn(
|
||||||
@ -461,7 +506,7 @@ impl StreamHandlerType {
|
|||||||
#[cfg(feature="alpn")]
|
#[cfg(feature="alpn")]
|
||||||
StreamHandlerType::Alpn(ref acceptor) => {
|
StreamHandlerType::Alpn(ref acceptor) => {
|
||||||
let IoStream { io, peer, .. } = msg;
|
let IoStream { io, peer, .. } = msg;
|
||||||
let io = TcpStream::from_stream(io, Arbiter::handle())
|
let io = TcpStream::from_stream(io.into_tcp_stream(), Arbiter::handle())
|
||||||
.expect("failed to associate TCP stream");
|
.expect("failed to associate TCP stream");
|
||||||
|
|
||||||
Arbiter::handle().spawn(
|
Arbiter::handle().spawn(
|
||||||
@ -488,7 +533,7 @@ impl StreamHandlerType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn start_accept_thread(sock: Socket, addr: net::SocketAddr,
|
fn start_accept_thread(sock: Socket, addr: net::SocketAddr,
|
||||||
workers: Vec<mpsc::UnboundedSender<IoStream<net::TcpStream>>>) {
|
workers: Vec<mpsc::UnboundedSender<IoStream<Socket>>>) {
|
||||||
// start acceptors thread
|
// start acceptors thread
|
||||||
let _ = thread::Builder::new().name(format!("Accept on {}", addr)).spawn(move || {
|
let _ = thread::Builder::new().name(format!("Accept on {}", addr)).spawn(move || {
|
||||||
let mut next = 0;
|
let mut next = 0;
|
||||||
@ -500,8 +545,7 @@ fn start_accept_thread(sock: Socket, addr: net::SocketAddr,
|
|||||||
} else {
|
} else {
|
||||||
net::SocketAddr::V6(addr.as_inet6().unwrap())
|
net::SocketAddr::V6(addr.as_inet6().unwrap())
|
||||||
};
|
};
|
||||||
let msg = IoStream{
|
let msg = IoStream{io: socket, peer: Some(addr), http2: false};
|
||||||
io: socket.into_tcp_stream(), peer: Some(addr), http2: false};
|
|
||||||
workers[next].unbounded_send(msg).expect("worker thread died");
|
workers[next].unbounded_send(msg).expect("worker thread died");
|
||||||
next = (next + 1) % workers.len();
|
next = (next + 1) % workers.len();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user