This commit is contained in:
MeexReay 2024-08-02 15:10:42 +03:00
parent a6127b6f60
commit 9cf1a41b52
9 changed files with 1055 additions and 1005 deletions

View File

@ -21,9 +21,9 @@ impl HttpServer for EzSite {
// println!("{} > {} {}", req.addr, req.method, req.page);
if req.page == "/" {
Some(HttpResponse::from_str(
Some(HttpResponse::from_string(
Headers::from(vec![("Content-Type", "text/html")]), // response headers
"200 OK".to_string(), // response status code
"200 OK", // response status code
&self.index_page, // response body
))
} else {
@ -31,7 +31,7 @@ impl HttpServer for EzSite {
}
}
async fn on_start(&mut self, host: &str) {
async fn on_start(&mut self, _: &str) {
// println!("Http server started on {}", host);
}

24
src/ezhttp/error.rs Normal file
View File

@ -0,0 +1,24 @@
use std::error::Error;
/// Http error
#[derive(Debug)]
pub enum HttpError {
ReadLineEof,
ReadLineUnknown,
InvalidHeaders,
InvalidQuery,
InvalidContentSize,
InvalidContent,
JsonParseError,
WriteHeadError,
WriteBodyError,
InvalidStatus,
}
impl std::fmt::Display for HttpError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
}
}
impl Error for HttpError {}

102
src/ezhttp/headers.rs Normal file
View File

@ -0,0 +1,102 @@
use std::{
collections::HashMap,
fmt::{Debug, Display},
};
/// Http headers
#[derive(Clone, Debug)]
pub struct Headers {
entries: Vec<(String, String)>,
}
impl Into<HashMap<String, String>> for Headers {
fn into(self) -> HashMap<String, String> {
HashMap::from_iter(self.entries().into_iter())
}
}
impl<T, U> From<Vec<(T, U)>> for Headers
where
T: ToString,
U: ToString,
{
fn from(value: Vec<(T, U)>) -> Self {
Headers {
entries: value
.into_iter()
.map(|v| (v.0.to_string(), v.1.to_string()))
.collect(),
}
}
}
impl Headers {
pub fn new() -> Self {
Headers {
entries: Vec::new(),
}
}
pub fn contains(self, header: impl ToString) -> bool {
for (k, _) in self.entries {
if k.to_lowercase() == header.to_string().to_lowercase() {
return true;
}
}
return false;
}
pub fn get(self, key: impl ToString) -> Option<String> {
for (k, v) in self.entries {
if k.to_lowercase() == key.to_string().to_lowercase() {
return Some(v);
}
}
return None;
}
pub fn put(&mut self, key: impl ToString, value: String) {
for t in self.entries.iter_mut() {
if t.0.to_lowercase() == key.to_string().to_lowercase() {
t.1 = value;
return;
}
}
self.entries.push((key.to_string(), value));
}
pub fn remove(&mut self, key: impl ToString) {
for (i, t) in self.entries.iter_mut().enumerate() {
if t.0.to_lowercase() == key.to_string().to_lowercase() {
self.entries.remove(i);
return;
}
}
}
pub fn keys(self) -> Vec<String> {
self.entries.iter().map(|e| e.0.clone()).collect()
}
pub fn values(self) -> Vec<String> {
self.entries.iter().map(|e| e.1.clone()).collect()
}
pub fn entries(self) -> Vec<(String, String)> {
return self.entries;
}
pub fn len(self) -> usize {
return self.entries.len();
}
pub fn clear(&mut self) {
self.entries.clear();
}
}
impl Display for Headers {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(self, f)
}
}

275
src/ezhttp/mod.rs Normal file
View File

@ -0,0 +1,275 @@
use futures::executor::block_on;
use std::{
boxed::Box,
error::Error,
future::Future,
io::Read,
net::{TcpListener, TcpStream},
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
thread,
time::Duration,
};
use threadpool::ThreadPool;
pub mod error;
pub mod headers;
pub mod request;
pub mod response;
pub mod starter;
pub use error::*;
pub use headers::*;
pub use request::*;
pub use response::*;
pub use starter::*;
fn read_line(data: &mut impl Read) -> Result<String, HttpError> {
let mut bytes = Vec::new();
for byte in data.bytes() {
let byte = match byte {
Ok(i) => i,
Err(_) => return Err(HttpError::ReadLineEof),
};
bytes.push(byte);
if byte == 0x0A {
break;
}
}
match String::from_utf8(bytes) {
Ok(i) => Ok(i),
Err(_) => Err(HttpError::ReadLineUnknown),
}
}
fn read_line_crlf(data: &mut impl Read) -> Result<String, HttpError> {
match read_line(data) {
Ok(i) => Ok(i[..i.len() - 2].to_string()),
Err(e) => Err(e),
}
}
fn read_line_lf(data: &mut impl Read) -> Result<String, HttpError> {
match read_line(data) {
Ok(i) => Ok(i[..i.len() - 1].to_string()),
Err(e) => Err(e),
}
}
fn rem_first(value: &str) -> &str {
let mut chars = value.chars();
chars.next();
chars.as_str()
}
fn split(text: String, delimiter: &str, times: usize) -> Vec<String> {
match times {
0 => text.split(delimiter).map(|v| v.to_string()).collect(),
1 => {
let mut v: Vec<String> = Vec::new();
match text.split_once(delimiter) {
Some(i) => {
v.push(i.0.to_string());
v.push(i.1.to_string());
}
None => {
v.push(text);
}
}
v
}
_ => text
.splitn(times, delimiter)
.map(|v| v.to_string())
.collect(),
}
}
/// Async http server trait
pub trait HttpServer {
fn on_start(&mut self, host: &str) -> impl Future<Output = ()> + Send;
fn on_close(&mut self) -> impl Future<Output = ()> + Send;
fn on_request(
&mut self,
req: &HttpRequest,
) -> impl Future<Output = Option<HttpResponse>> + Send;
}
fn start_server_with_threadpool<F, S>(
server: S,
host: &str,
timeout: Option<Duration>,
threads: usize,
handler: F,
running: Arc<AtomicBool>,
) -> 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));
while running.load(Ordering::Acquire) {
let (sock, _) = match listener.accept() {
Ok(i) => i,
Err(_) => {
continue;
}
};
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);
});
}
threadpool.join();
block_on(server.lock().unwrap().on_close());
Ok(())
}
fn start_server_new_thread<F, S>(
server: S,
host: &str,
timeout: Option<Duration>,
handler: F,
running: Arc<AtomicBool>,
) -> Result<(), Box<dyn Error>>
where
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));
while running.load(Ordering::Acquire) {
let (sock, _) = match listener.accept() {
Ok(i) => i,
Err(_) => {
continue;
}
};
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,
running: Arc<AtomicBool>,
) -> Result<(), Box<dyn Error>>
where
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));
while running.load(Ordering::Acquire) {
let (sock, _) = match listener.accept() {
Ok(i) => i,
Err(_) => {
continue;
}
};
sock.set_read_timeout(timeout).unwrap();
sock.set_write_timeout(timeout).unwrap();
let now_server = Arc::clone(&server);
handler(now_server, sock);
}
block_on(server.lock().unwrap().on_close());
Ok(())
}
fn handle_connection<S: HttpServer + Send + 'static>(server: Arc<Mutex<S>>, mut sock: TcpStream) {
let addr = sock.peer_addr().unwrap();
let req = match HttpRequest::read(&mut sock, &addr) {
Ok(i) => i,
Err(_) => {
return;
}
};
let resp = match block_on(server.lock().unwrap().on_request(&req)) {
Some(i) => i,
None => {
return;
}
};
resp.write(&mut sock).unwrap();
}
fn handle_connection_rrs<S: HttpServer + Send + 'static>(
server: Arc<Mutex<S>>,
mut sock: TcpStream,
) {
let req = match HttpRequest::read_with_rrs(&mut sock) {
Ok(i) => i,
Err(_) => {
return;
}
};
let resp = match block_on(server.lock().unwrap().on_request(&req)) {
Some(i) => i,
None => {
return;
}
};
resp.write(&mut sock).unwrap();
}
/// Start [`HttpServer`](HttpServer) on some host
///
/// Use [`HttpServerStarter`](HttpServerStarter) to set more options
pub fn start_server<S: HttpServer + Send + 'static>(server: S, host: &str) {
start_server_new_thread(
server,
host,
None,
handle_connection,
Arc::new(AtomicBool::new(true)),
)
.unwrap();
}

280
src/ezhttp/request.rs Normal file
View File

@ -0,0 +1,280 @@
use super::{read_line_crlf, read_line_lf, rem_first, split, Headers, HttpError};
use serde_json::Value;
use std::{
fmt::{Debug, Display},
io::{Read, Write},
net::{IpAddr, SocketAddr, ToSocketAddrs},
};
/// Http request
#[derive(Debug, Clone)]
pub struct HttpRequest {
pub page: String,
pub method: String,
pub addr: String,
pub headers: Headers,
pub params: Value,
pub data: Vec<u8>,
}
impl Display for HttpRequest {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(self, f)
}
}
impl HttpRequest {
/// Create new http request
pub fn new(page: &str, method: &str, params: Value, headers: Headers, data: Vec<u8>) -> Self {
HttpRequest {
page: page.to_string(),
method: method.to_string(),
addr: String::new(),
params,
headers,
data,
}
}
/// Read http request from stream
pub fn read(data: &mut impl Read, addr: &SocketAddr) -> Result<HttpRequest, HttpError> {
let octets = match addr.ip() {
IpAddr::V4(ip) => ip.octets(),
_ => [127, 0, 0, 1],
};
let ip_str = octets[0].to_string()
+ "."
+ &octets[1].to_string()
+ "."
+ &octets[2].to_string()
+ "."
+ &octets[3].to_string();
let status = split(
match read_line_crlf(data) {
Ok(i) => i,
Err(e) => return Err(e),
},
" ",
3,
);
let method = status[0].clone();
let (page, query) = match status[1].split_once("?") {
Some(i) => (i.0.to_string(), Some(i.1)),
None => (status[1].clone(), None),
};
let mut headers = Headers::new();
loop {
let text = match read_line_crlf(data) {
Ok(i) => i,
Err(_) => return Err(HttpError::InvalidHeaders),
};
if text.len() == 0 {
break;
}
let (key, value) = match text.split_once(": ") {
Some(i) => i,
None => return Err(HttpError::InvalidHeaders),
};
headers.put(key.to_lowercase(), value.to_string());
}
let mut params = serde_json::Map::new();
if let Some(i) = query {
for ele in i.split("&") {
let (k, v) = match ele.split_once("=") {
Some(i) => i,
None => return Err(HttpError::InvalidQuery),
};
params.insert(
match urlencoding::decode(k) {
Ok(i) => i.to_string(),
Err(_) => return Err(HttpError::InvalidQuery),
},
match urlencoding::decode(v) {
Ok(i) => Value::String(i.to_string()),
Err(_) => return Err(HttpError::InvalidQuery),
},
);
}
}
let mut reqdata: Vec<u8> = Vec::new();
if let Some(content_size) = headers.clone().get("content-length".to_string()) {
let content_size: usize = match content_size.parse() {
Ok(i) => i,
Err(_) => return Err(HttpError::InvalidContentSize),
};
if content_size > reqdata.len() {
let mut buf: Vec<u8> = Vec::new();
buf.resize(content_size - reqdata.len(), 0);
match data.read_exact(&mut buf) {
Ok(i) => i,
Err(_) => return Err(HttpError::InvalidContent),
};
reqdata.append(&mut buf);
}
}
if let Some(content_type) = headers.clone().get("content-type".to_string()) {
let mut body = match String::from_utf8(reqdata.clone()) {
Ok(i) => i,
Err(_) => return Err(HttpError::InvalidContent),
};
match content_type.as_str() {
"application/json" => {
let val: Value = match serde_json::from_str(&body) {
Ok(i) => i,
Err(_) => return Err(HttpError::JsonParseError),
};
if let Value::Object(mut dict) = val {
params.append(&mut dict);
}
}
"multipart/form-data" => {
let boundary = "--".to_string()
+ &content_type[(content_type.find("boundary=").unwrap() + 9)..]
+ "\r\n";
for part in body.split(boundary.as_str()) {
let lines: Vec<&str> = part.split("\r\n").collect();
if lines.len() >= 3 {
if lines[0].starts_with("Content-Disposition: form-data; name=\"") {
let name: &str =
&lines[0]["Content-Disposition: form-data; name=\"".len()..];
let name: &str = &name[..name.len() - 1];
params
.insert(name.to_string(), Value::String(lines[2].to_string()));
}
}
}
}
"application/x-www-form-urlencoded" => {
if body.starts_with("?") {
body = rem_first(body.as_str()).to_string()
}
for ele in body.split("&") {
let (k, v) = match ele.split_once("=") {
Some(i) => i,
None => return Err(HttpError::InvalidQuery),
};
params.insert(
match urlencoding::decode(k) {
Ok(i) => i.to_string(),
Err(_) => return Err(HttpError::InvalidQuery),
},
match urlencoding::decode(v) {
Ok(i) => Value::String(i.to_string()),
Err(_) => return Err(HttpError::InvalidQuery),
},
);
}
}
_ => {}
}
}
Ok(HttpRequest {
page,
method,
addr: ip_str.to_string(),
params: Value::Object(params),
headers,
data: reqdata.clone(),
})
}
/// Read http request with http_rrs support
pub fn read_with_rrs(data: &mut impl Read) -> Result<HttpRequest, HttpError> {
let addr = match read_line_lf(data) {
Ok(i) => i,
Err(e) => {
return Err(e);
}
}
.to_socket_addrs()
.unwrap()
.collect::<Vec<SocketAddr>>()[0];
HttpRequest::read(data, &addr)
}
/// Set params to query in url
pub fn params_to_page(&mut self) {
let mut query = String::new();
let mut i: bool = !self.page.contains("?");
if let Value::Object(obj) = self.params.clone() {
for (k, v) in obj {
query.push_str(if i { "?" } else { "&" });
query.push_str(urlencoding::encode(k.as_str()).to_string().as_str());
query.push_str("=");
query.push_str(
urlencoding::encode(v.as_str().unwrap())
.to_string()
.as_str(),
);
i = false;
}
}
self.page += query.as_str();
}
/// Set params to json data
pub fn params_to_json(&mut self) {
self.data = Vec::from(self.params.to_string().as_bytes());
}
/// Write http request to stream
///
/// [`params`](Self::params) is not written to the stream, you need to use [`params_to_json`](Self::params_to_json) or [`params_to_page`](Self::params_to_page)
pub fn write(self, data: &mut impl Write) -> Result<(), HttpError> {
let mut head: String = String::new();
head.push_str(&self.method);
head.push_str(" ");
head.push_str(&self.page);
head.push_str(" HTTP/1.1");
head.push_str("\r\n");
for (k, v) in self.headers.entries() {
head.push_str(&k);
head.push_str(": ");
head.push_str(&v);
head.push_str("\r\n");
}
head.push_str("\r\n");
match data.write_all(head.as_bytes()) {
Ok(i) => i,
Err(_) => return Err(HttpError::WriteHeadError),
};
if !self.data.is_empty() {
match data.write_all(&self.data) {
Ok(i) => i,
Err(_) => return Err(HttpError::WriteBodyError),
};
}
Ok(())
}
}

168
src/ezhttp/response.rs Normal file
View File

@ -0,0 +1,168 @@
use super::{read_line_crlf, Headers, HttpError};
use serde_json::Value;
use std::{
fmt::{Debug, Display},
io::{Read, Write},
};
/// Http response
#[derive(Debug, Clone)]
pub struct HttpResponse {
pub headers: Headers,
pub status_code: String,
pub data: Vec<u8>,
}
impl Display for HttpResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(self, f)
}
}
impl HttpResponse {
/// Create new http response with empty headers and data and a 200 OK status code
pub fn new() -> Self {
Self::from_bytes(Headers::new(), "200 OK", Vec::new())
}
/// Create new http response from headers, bytes data, and status code
pub fn from_bytes(headers: Headers, status_code: impl ToString, data: Vec<u8>) -> Self {
HttpResponse {
headers,
data,
status_code: status_code.to_string(),
}
}
/// Create new http response from headers, string data, and status code
pub fn from_string(headers: Headers, status_code: impl ToString, data: impl ToString) -> Self {
HttpResponse {
headers,
data: data.to_string().into_bytes(),
status_code: status_code.to_string(),
}
}
/// Get data in UTF-8
pub fn get_text(self) -> String {
match String::from_utf8(self.data) {
Ok(i) => i,
Err(_) => String::new(),
}
}
/// Get json [`Value`](Value) from data
pub fn get_json(self) -> Value {
match serde_json::from_str(self.get_text().as_str()) {
Ok(i) => i,
Err(_) => Value::Null,
}
}
/// Read http response from stream
pub fn read(data: &mut impl Read) -> Result<HttpResponse, HttpError> {
let status = match read_line_crlf(data) {
Ok(i) => i,
Err(e) => {
return Err(e);
}
};
let (_, status_code) = match status.split_once(" ") {
Some(i) => i,
None => return Err(HttpError::InvalidStatus),
};
let mut headers = Headers::new();
loop {
let text = match read_line_crlf(data) {
Ok(i) => i,
Err(_) => return Err(HttpError::InvalidHeaders),
};
if text.len() == 0 {
break;
}
let (key, value) = match text.split_once(": ") {
Some(i) => i,
None => return Err(HttpError::InvalidHeaders),
};
headers.put(key.to_lowercase(), value.to_string());
}
let mut reqdata: Vec<u8> = Vec::new();
if let Some(content_size) = headers.clone().get("content-length".to_string()) {
let content_size: usize = match content_size.parse() {
Ok(i) => i,
Err(_) => return Err(HttpError::InvalidContentSize),
};
if content_size > reqdata.len() {
let mut buf: Vec<u8> = Vec::new();
buf.resize(content_size - reqdata.len(), 0);
match data.read_exact(&mut buf) {
Ok(i) => i,
Err(_) => return Err(HttpError::InvalidContent),
};
reqdata.append(&mut buf);
}
} else {
loop {
let mut buf: Vec<u8> = vec![0; 1024 * 32];
let buf_len = match data.read(&mut buf) {
Ok(i) => i,
Err(_) => {
break;
}
};
if buf_len == 0 {
break;
}
buf.truncate(buf_len);
reqdata.append(&mut buf);
}
}
Ok(HttpResponse::from_bytes(headers, status_code, reqdata))
}
/// Write http response to stream
pub fn write(self, data: &mut impl Write) -> Result<(), &str> {
let mut head: String = String::new();
head.push_str("HTTP/1.1 ");
head.push_str(&self.status_code);
head.push_str("\r\n");
for (k, v) in self.headers.entries() {
head.push_str(&k);
head.push_str(": ");
head.push_str(&v);
head.push_str("\r\n");
}
head.push_str("\r\n");
match data.write_all(head.as_bytes()) {
Ok(i) => i,
Err(_) => return Err("write head error"),
};
match data.write_all(&self.data) {
Ok(i) => i,
Err(_) => return Err("write body error"),
};
Ok(())
}
}

199
src/ezhttp/starter.rs Normal file
View File

@ -0,0 +1,199 @@
use super::{
handle_connection, handle_connection_rrs, start_server_new_thread, start_server_sync,
start_server_with_threadpool, HttpServer,
};
use std::{
error::Error,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
thread,
time::Duration,
};
/// Http server start builder
pub struct HttpServerStarter<T: HttpServer + Send + 'static> {
http_server: T,
support_http_rrs: bool,
timeout: Option<Duration>,
host: String,
threads: usize,
}
/// Running http server
pub struct RunningHttpServer {
thread: thread::JoinHandle<()>,
running: Arc<AtomicBool>,
}
impl RunningHttpServer {
fn new(thread: thread::JoinHandle<()>, running: Arc<AtomicBool>) -> Self {
RunningHttpServer { thread, running }
}
/// Stop http server
pub fn close(self) {
self.running.store(false, Ordering::Release);
self.thread.join().unwrap();
}
}
impl<T: HttpServer + Send + 'static> HttpServerStarter<T> {
/// Create new HttpServerStarter
pub fn new(http_server: T, host: &str) -> Self {
HttpServerStarter {
http_server,
support_http_rrs: false,
timeout: None,
host: host.to_string(),
threads: 0,
}
}
/// Set http server
pub fn http_server(mut self, http_server: T) -> Self {
self.http_server = http_server;
return self;
}
/// Set if http_rrs is supported
pub fn support_http_rrs(mut self, support_http_rrs: bool) -> Self {
self.support_http_rrs = support_http_rrs;
return self;
}
/// Set timeout for read & write
pub fn timeout(mut self, timeout: Option<Duration>) -> Self {
self.timeout = timeout;
return self;
}
/// Set host
pub fn host(mut self, host: String) -> Self {
self.host = host;
return self;
}
/// Set threads in threadpool and return builder
///
/// 0 threads means that a new thread is created for each connection \
/// 1 thread means that all connections are processed in the main thread
pub fn threads(mut self, threads: usize) -> Self {
self.threads = threads;
return self;
}
/// Get http server
pub fn get_http_server(self) -> T {
self.http_server
}
/// Get if http_rrs is supported
pub fn get_support_http_rrs(&self) -> bool {
self.support_http_rrs
}
/// Get timeout for read & write
pub fn get_timeout(&self) -> Option<Duration> {
self.timeout
}
/// Get host
pub fn get_host(&self) -> &str {
&self.host
}
/// Get threads in threadpool
///
/// 0 threads means that a new thread is created for each connection \
/// 1 thread means that all connections are processed in the main thread
pub fn get_threads(&self) -> usize {
self.threads
}
/// Start http server forever with options
pub fn start_forever(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);
}
};
let running = Arc::new(AtomicBool::new(true));
if self.threads == 0 {
start_server_new_thread(self.http_server, &self.host, self.timeout, handler, running)
} else if self.threads == 1 {
start_server_sync(self.http_server, &self.host, self.timeout, handler, running)
} else {
start_server_with_threadpool(
self.http_server,
&self.host,
self.timeout,
self.threads,
handler,
running,
)
}
}
/// Start http server with options in new thread
pub fn start(self) -> RunningHttpServer {
let handler = if self.support_http_rrs {
move |server, sock| {
handle_connection_rrs(server, sock);
}
} else {
move |server, sock| {
handle_connection(server, sock);
}
};
let running = Arc::new(AtomicBool::new(true));
let running_clone = running.clone();
let thread = if self.threads == 0 {
thread::spawn(move || {
start_server_new_thread(
self.http_server,
&self.host,
self.timeout,
handler,
running_clone,
)
.expect("http server error");
})
} else if self.threads == 1 {
thread::spawn(move || {
start_server_sync(
self.http_server,
&self.host,
self.timeout,
handler,
running_clone,
)
.expect("http server error");
})
} else {
thread::spawn(move || {
start_server_with_threadpool(
self.http_server,
&self.host,
self.timeout,
self.threads,
handler,
running_clone,
)
.expect("http server error")
})
};
RunningHttpServer::new(thread, running.clone())
}
}

1002
src/lib.rs

File diff suppressed because it is too large Load Diff

View File

@ -20,8 +20,8 @@ impl HttpServer for EzSite {
if req.page == "/" {
Some(HttpResponse::from_string(
Headers::from(vec![("Content-Type", "text/html")]), // response headers
"200 OK", // response status code
self.index_page.clone(), // response body
"200 OK", // response status code
self.index_page.clone(), // response body
))
} else {
None // close connection