add threadpool and HttpServerStarter
This commit is contained in:
parent
efc36aa3f8
commit
0ef43dcb47
27
README.md
27
README.md
@ -5,12 +5,21 @@ This library is under developement, so if you found any bugs, please write them
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
```rust
|
```rust
|
||||||
use ezhttp::{Headers, HttpRequest, HttpResponse, HttpServer};
|
use ezhttp::{Headers, HttpRequest, HttpResponse, HttpServer, HttpServerStarter};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
struct EzSite {
|
struct EzSite {
|
||||||
index_page: String,
|
index_page: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl EzSite {
|
||||||
|
fn new(index_page: &str) -> Self {
|
||||||
|
EzSite {
|
||||||
|
index_page: index_page.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl HttpServer for EzSite {
|
impl HttpServer for EzSite {
|
||||||
async fn on_request(&mut self, req: &HttpRequest) -> Option<HttpResponse> {
|
async fn on_request(&mut self, req: &HttpRequest) -> Option<HttpResponse> {
|
||||||
println!("{} > {} {}", req.addr, req.method, req.page);
|
println!("{} > {} {}", req.addr, req.method, req.page);
|
||||||
@ -22,7 +31,7 @@ impl HttpServer for EzSite {
|
|||||||
&self.index_page, // response body
|
&self.index_page, // response body
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
None // just shutdown socket
|
None // close connection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,19 +44,15 @@ impl HttpServer for EzSite {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EzSite {
|
|
||||||
fn new(index_page: &str) -> Self {
|
|
||||||
EzSite {
|
|
||||||
index_page: index_page.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let site = EzSite::new("Hello World!");
|
let site = EzSite::new("Hello World!");
|
||||||
let host = "localhost:8080";
|
let host = "localhost:8080";
|
||||||
|
|
||||||
ezhttp::start_server(site, host).unwrap();
|
HttpServerStarter::new(site, host)
|
||||||
|
.timeout(Some(Duration::from_secs(5))) // read & write timeout
|
||||||
|
.threads(5) // threadpool size
|
||||||
|
.start()
|
||||||
|
.expect("http server error");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use ezhttp::{Headers, HttpRequest, HttpResponse, HttpServer};
|
use ezhttp::{Headers, HttpRequest, HttpResponse, HttpServer, HttpServerStarter};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
struct EzSite {
|
struct EzSite {
|
||||||
main_page: String,
|
main_page: String,
|
||||||
@ -66,5 +67,9 @@ fn main() {
|
|||||||
let site = EzSite::new("<h1>Hello World!</h1>");
|
let site = EzSite::new("<h1>Hello World!</h1>");
|
||||||
let host = "localhost:8080";
|
let host = "localhost:8080";
|
||||||
|
|
||||||
ezhttp::start_server(site, host).unwrap();
|
HttpServerStarter::new(site, host)
|
||||||
|
.timeout(Some(Duration::from_secs(5)))
|
||||||
|
.threads(5)
|
||||||
|
.start()
|
||||||
|
.expect("http server error");
|
||||||
}
|
}
|
||||||
|
256
src/lib.rs
256
src/lib.rs
@ -12,7 +12,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
net::{TcpListener, TcpStream},
|
net::{TcpListener, TcpStream},
|
||||||
sync::Mutex,
|
sync::{mpsc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -628,7 +628,7 @@ impl HttpResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HttpServer: Sync {
|
pub trait HttpServer {
|
||||||
fn on_start(&mut self, host: &str) -> impl Future<Output = ()> + Send;
|
fn on_start(&mut self, host: &str) -> impl Future<Output = ()> + Send;
|
||||||
fn on_close(&mut self) -> impl Future<Output = ()> + Send;
|
fn on_close(&mut self) -> impl Future<Output = ()> + Send;
|
||||||
fn on_request(
|
fn on_request(
|
||||||
@ -637,16 +637,166 @@ pub trait HttpServer: Sync {
|
|||||||
) -> impl Future<Output = Option<HttpResponse>> + Send;
|
) -> impl Future<Output = Option<HttpResponse>> + Send;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_server_with_handler<F, S>(
|
pub struct HttpServerStarter<T: HttpServer + Send + 'static> {
|
||||||
|
http_server: T,
|
||||||
|
support_http_rrs: bool,
|
||||||
|
timeout: Option<Duration>,
|
||||||
|
host: String,
|
||||||
|
threads: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: HttpServer + Send + 'static> HttpServerStarter<T> {
|
||||||
|
pub fn new(http_server: T, host: &str) -> Self {
|
||||||
|
HttpServerStarter {
|
||||||
|
http_server,
|
||||||
|
support_http_rrs: false,
|
||||||
|
timeout: None,
|
||||||
|
host: host.to_string(),
|
||||||
|
threads: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn http_server(mut self, http_server: T) -> Self {
|
||||||
|
self.http_server = http_server;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn support_http_rrs(mut self, support_http_rrs: bool) -> Self {
|
||||||
|
self.support_http_rrs = support_http_rrs;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn timeout(mut self, timeout: Option<Duration>) -> Self {
|
||||||
|
self.timeout = timeout;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn host(mut self, host: String) -> Self {
|
||||||
|
self.host = host;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn threads(mut self, threads: usize) -> Self {
|
||||||
|
self.threads = threads;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(self) -> Result<(), Box<dyn Error>> {
|
||||||
|
let handler = if self.support_http_rrs {
|
||||||
|
move |server, sock| {
|
||||||
|
handle_connection_rrs(server, sock);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
move |server, sock| {
|
||||||
|
handle_connection(server, sock);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.threads == 0 {
|
||||||
|
start_server(self.http_server, &self.host, self.timeout, handler)
|
||||||
|
} else if self.threads == 1 {
|
||||||
|
start_server_sync(self.http_server, &self.host, self.timeout, handler)
|
||||||
|
} else {
|
||||||
|
start_server_with_threadpool(
|
||||||
|
self.http_server,
|
||||||
|
&self.host,
|
||||||
|
self.timeout,
|
||||||
|
self.threads,
|
||||||
|
handler,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_server_with_threadpool<F, S>(
|
||||||
|
server: S,
|
||||||
|
host: &str,
|
||||||
|
timeout: Option<Duration>,
|
||||||
|
threads: usize,
|
||||||
|
handler: F,
|
||||||
|
) -> Result<(), Box<dyn Error>>
|
||||||
|
where
|
||||||
|
F: (Fn(Arc<Mutex<S>>, TcpStream) -> ()) + Send + 'static + Copy,
|
||||||
|
S: HttpServer + Send + 'static,
|
||||||
|
{
|
||||||
|
let threadpool = ThreadPool::new(threads);
|
||||||
|
let server = Arc::new(Mutex::new(server));
|
||||||
|
let listener = TcpListener::bind(host)?;
|
||||||
|
|
||||||
|
let host_clone = String::from(host).clone();
|
||||||
|
let server_clone = server.clone();
|
||||||
|
block_on(server_clone.lock().unwrap().on_start(&host_clone));
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let (sock, _) = match listener.accept() {
|
||||||
|
Ok(i) => i,
|
||||||
|
Err(_) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
sock.set_read_timeout(timeout).unwrap();
|
||||||
|
sock.set_write_timeout(timeout).unwrap();
|
||||||
|
|
||||||
|
let now_server = Arc::clone(&server);
|
||||||
|
threadpool.execute(move || {
|
||||||
|
handler(now_server, sock);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
block_on(server.lock().unwrap().on_close());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_server<F, S>(
|
||||||
server: S,
|
server: S,
|
||||||
host: &str,
|
host: &str,
|
||||||
timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
handler: F,
|
handler: F,
|
||||||
) -> Result<(), Box<dyn Error>>
|
) -> Result<(), Box<dyn Error>>
|
||||||
where
|
where
|
||||||
F: Fn(Arc<Mutex<S>>, TcpStream) -> (),
|
F: (Fn(Arc<Mutex<S>>, TcpStream) -> ()) + Send + 'static + Copy,
|
||||||
|
S: HttpServer + Send + 'static,
|
||||||
|
{
|
||||||
|
let server = Arc::new(Mutex::new(server));
|
||||||
|
let listener = TcpListener::bind(host)?;
|
||||||
|
|
||||||
|
let host_clone = String::from(host).clone();
|
||||||
|
let server_clone = server.clone();
|
||||||
|
block_on(server_clone.lock().unwrap().on_start(&host_clone));
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let (sock, _) = match listener.accept() {
|
||||||
|
Ok(i) => i,
|
||||||
|
Err(_) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
sock.set_read_timeout(timeout).unwrap();
|
||||||
|
sock.set_write_timeout(timeout).unwrap();
|
||||||
|
|
||||||
|
let now_server = Arc::clone(&server);
|
||||||
|
thread::spawn(move || {
|
||||||
|
handler(now_server, sock);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
block_on(server.lock().unwrap().on_close());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_server_sync<F, S>(
|
||||||
|
server: S,
|
||||||
|
host: &str,
|
||||||
|
timeout: Option<Duration>,
|
||||||
|
handler: F,
|
||||||
|
) -> Result<(), Box<dyn Error>>
|
||||||
|
where
|
||||||
|
F: (Fn(Arc<Mutex<S>>, TcpStream) -> ()) + Send + 'static + Copy,
|
||||||
S: HttpServer + Send + 'static,
|
S: HttpServer + Send + 'static,
|
||||||
F: Send + 'static,
|
|
||||||
{
|
{
|
||||||
let server = Arc::new(Mutex::new(server));
|
let server = Arc::new(Mutex::new(server));
|
||||||
let listener = TcpListener::bind(host)?;
|
let listener = TcpListener::bind(host)?;
|
||||||
@ -675,46 +825,6 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_server(
|
|
||||||
server: impl HttpServer + Send + 'static,
|
|
||||||
host: &str,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
start_server_with_handler(server, host, None, move |server, sock| {
|
|
||||||
thread::spawn(move || handle_connection(server, sock));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// http rrs support
|
|
||||||
pub fn start_server_rrs(
|
|
||||||
server: impl HttpServer + Send + 'static,
|
|
||||||
host: &str,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
start_server_with_handler(server, host, None, move |server, sock| {
|
|
||||||
thread::spawn(move || handle_connection_rrs(server, sock));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start_server_timeout(
|
|
||||||
server: impl HttpServer + Send + 'static,
|
|
||||||
host: &str,
|
|
||||||
timeout: Duration,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
start_server_with_handler(server, host, Some(timeout), move |server, sock| {
|
|
||||||
thread::spawn(move || handle_connection(server, sock));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// http rrs support
|
|
||||||
pub fn start_server_rrs_timeout(
|
|
||||||
server: impl HttpServer + Send + 'static,
|
|
||||||
host: &str,
|
|
||||||
timeout: Duration,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
start_server_with_handler(server, host, Some(timeout), move |server, sock| {
|
|
||||||
thread::spawn(move || handle_connection_rrs(server, sock));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_connection<S: HttpServer + Send + 'static>(server: Arc<Mutex<S>>, mut sock: TcpStream) {
|
fn handle_connection<S: HttpServer + Send + 'static>(server: Arc<Mutex<S>>, mut sock: TcpStream) {
|
||||||
let addr = sock.peer_addr().unwrap();
|
let addr = sock.peer_addr().unwrap();
|
||||||
|
|
||||||
@ -733,7 +843,6 @@ fn handle_connection<S: HttpServer + Send + 'static>(server: Arc<Mutex<S>>, mut
|
|||||||
resp.write(&mut sock).unwrap();
|
resp.write(&mut sock).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// http rrs support
|
|
||||||
fn handle_connection_rrs<S: HttpServer + Send + 'static>(
|
fn handle_connection_rrs<S: HttpServer + Send + 'static>(
|
||||||
server: Arc<Mutex<S>>,
|
server: Arc<Mutex<S>>,
|
||||||
mut sock: TcpStream,
|
mut sock: TcpStream,
|
||||||
@ -752,3 +861,58 @@ fn handle_connection_rrs<S: HttpServer + Send + 'static>(
|
|||||||
};
|
};
|
||||||
resp.write(&mut sock).unwrap();
|
resp.write(&mut sock).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Job = Box<dyn FnOnce() + Send + 'static>;
|
||||||
|
|
||||||
|
struct ThreadPool {
|
||||||
|
workers: Vec<Worker>,
|
||||||
|
sender: mpsc::Sender<Job>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThreadPool {
|
||||||
|
fn new(size: usize) -> ThreadPool {
|
||||||
|
assert!(size > 0);
|
||||||
|
|
||||||
|
let (sender, receiver) = mpsc::channel();
|
||||||
|
let receiver = Arc::new(Mutex::new(receiver));
|
||||||
|
|
||||||
|
let mut workers = Vec::with_capacity(size);
|
||||||
|
|
||||||
|
for _ in 0..size {
|
||||||
|
workers.push(Worker::new(Arc::clone(&receiver)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadPool { workers, sender }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn join(self) {
|
||||||
|
for ele in self.workers.into_iter() {
|
||||||
|
ele.thread.join().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute<F>(&self, f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce() + Send + 'static,
|
||||||
|
{
|
||||||
|
let job = Box::new(f);
|
||||||
|
|
||||||
|
self.sender.send(job).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Worker {
|
||||||
|
thread: thread::JoinHandle<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Worker {
|
||||||
|
fn new(receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
|
||||||
|
let thread = thread::spawn(move || {
|
||||||
|
while let Ok(job) = receiver.lock().unwrap().recv() {
|
||||||
|
job();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Worker { thread }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
27
src/main.rs
27
src/main.rs
@ -1,9 +1,18 @@
|
|||||||
use ezhttp::{Headers, HttpRequest, HttpResponse, HttpServer};
|
use ezhttp::{Headers, HttpRequest, HttpResponse, HttpServer, HttpServerStarter};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
struct EzSite {
|
struct EzSite {
|
||||||
index_page: String,
|
index_page: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl EzSite {
|
||||||
|
fn new(index_page: &str) -> Self {
|
||||||
|
EzSite {
|
||||||
|
index_page: index_page.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl HttpServer for EzSite {
|
impl HttpServer for EzSite {
|
||||||
async fn on_request(&mut self, req: &HttpRequest) -> Option<HttpResponse> {
|
async fn on_request(&mut self, req: &HttpRequest) -> Option<HttpResponse> {
|
||||||
println!("{} > {} {}", req.addr, req.method, req.page);
|
println!("{} > {} {}", req.addr, req.method, req.page);
|
||||||
@ -15,7 +24,7 @@ impl HttpServer for EzSite {
|
|||||||
&self.index_page, // response body
|
&self.index_page, // response body
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
None // just shutdown socket
|
None // close connection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,17 +37,13 @@ impl HttpServer for EzSite {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EzSite {
|
|
||||||
fn new(index_page: &str) -> Self {
|
|
||||||
EzSite {
|
|
||||||
index_page: index_page.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let site = EzSite::new("Hello World!");
|
let site = EzSite::new("Hello World!");
|
||||||
let host = "localhost:8080";
|
let host = "localhost:8080";
|
||||||
|
|
||||||
ezhttp::start_server(site, host).unwrap();
|
HttpServerStarter::new(site, host)
|
||||||
|
.timeout(Some(Duration::from_secs(5))) // read & write timeout
|
||||||
|
.threads(5) // threadpool size
|
||||||
|
.start()
|
||||||
|
.expect("http server error");
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user