#[cfg(feature = "client")]
pub mod client;
mod client_response;
pub mod config;
pub mod request;
pub mod response;
pub mod stream;
pub mod url;
pub mod websocket;
use crate::config::Config;
use crate::request::Request;
use crate::response::Response;
use crate::stream::Scheme;
use crate::websocket::{CloseCode, ErrorCode, Message, Websocket};
use fs::read;
use log::{error, info, warn};
use rustls_pemfile::certs;
use flate2::read::GzDecoder;
use flate2::write::GzEncoder;
use flate2::Compression;
use json::{object, JsonValue};
use rustls::{ServerConfig, ServerConnection, StreamOwned};
use std::fmt::Debug;
use std::io::{BufReader, Error, Read, Write};
use std::net::TcpListener;
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use std::{fs, io, thread};
#[derive(Clone, Debug)]
pub struct WebServer;
struct ConnectionGuard(Arc<AtomicUsize>);
impl Drop for ConnectionGuard {
fn drop(&mut self) {
self.0.fetch_sub(1, Ordering::Relaxed);
}
}
impl WebServer {
pub fn new_service(config: Config, factory: fn(out: Websocket) -> Box<dyn Handler>) {
loop {
match WebServer::service(config.clone(), factory) {
Ok(()) => {}
Err(e) => error!("服务器错误: {}[{}]: {}", file!(), line!(), e),
}
warn!("服务器 1秒后重启");
thread::sleep(Duration::from_secs(1));
}
}
fn service(config: Config, factory: fn(out: Websocket) -> Box<dyn Handler>) -> io::Result<()> {
info!("==================== 网络服务 服务信息 ====================");
info!("日志记录: {}", if config.log { "开启" } else { "关闭" });
info!("调试模式: {}", if config.debug { "开启" } else { "关闭" });
info!("监听地址: {}", config.host);
info!(
"服务地址: {}://{}",
if config.https { "https" } else { "http" },
config.host
);
info!("根 目 录: {}", config.root_path.to_str().unwrap_or(""));
info!("访问目录: {}", config.public);
info!("运行目录: {}", config.runtime);
info!("SSL/TLS: {}", if config.https { "开启" } else { "关闭" });
if config.https {
info!("证书目录KEY: {:?}", config.tls.key);
info!("证书目录PEM: {:?}", config.tls.certs);
}
let listener = TcpListener::bind(config.host.clone())?;
info!("==================== 网络服务 启动成功 ====================");
let acceptor = Self::ssl(config.clone())?;
let connection_count = Arc::new(AtomicUsize::new(0));
for stream in listener.incoming() {
match stream {
Ok(stream) => {
let current = connection_count.load(Ordering::Relaxed);
if current >= config.max_connections {
warn!(
"连接数已达上限 ({}/{}), 拒绝新连接",
current, config.max_connections
);
drop(stream);
continue;
}
connection_count.fetch_add(1, Ordering::Relaxed);
let config_new = config.clone();
let acceptor_new = acceptor.clone();
let conn_count = connection_count.clone();
thread::spawn(move || -> io::Result<()> {
let _guard = ConnectionGuard(conn_count);
stream.set_nonblocking(false)?;
stream
.set_read_timeout(Some(Duration::from_secs(config_new.read_timeout)))
.unwrap_or_default();
stream
.set_write_timeout(Some(Duration::from_secs(config_new.write_timeout)))
.unwrap_or_default();
let scheme = if config_new.https {
let acceptor = acceptor_new
.ok_or_else(|| Error::other("TLS acceptor not configured"))?;
let conn = match ServerConnection::new(acceptor) {
Ok(e) => e,
Err(e) => {
return Err(Error::other(e.to_string()));
}
};
Scheme::Https(Arc::new(Mutex::new(StreamOwned::new(conn, stream))))
} else {
Scheme::Http(Arc::new(Mutex::new(stream)))
};
let mut request =
Request::new(config_new.clone(), Arc::new(Mutex::new(scheme.clone())));
let response = match request.handle() {
Ok(()) => Response::new(&request.clone(), factory),
Err(e) => {
return Err(Error::other(e.body.as_str()));
}
};
match response.handle() {
Ok(()) => {}
Err(e) => {
return Err(Error::other(e));
}
};
match request.save_log() {
Ok(()) => {}
Err(_) => error!("日志记录错误"),
}
Ok(())
});
}
Err(e) => return Err(e),
}
}
Ok(())
}
fn ssl(config: Config) -> io::Result<Option<Arc<ServerConfig>>> {
if config.https {
if !config.tls.key.is_file() {
return Err(Error::other(
format!("private.key 不存在: {:?}", config.tls.key.clone()).as_str(),
));
}
if !config.tls.certs.is_file() {
return Err(Error::other(
format!("certificate.pem 不存在: {:?}", config.tls.certs).as_str(),
));
}
let t = read(config.tls.key)?;
let mut reader = BufReader::new(t.as_slice());
let key = rustls_pemfile::private_key(&mut reader)
.map_err(|e| Error::other(format!("failed to parse private key: {}", e)))?
.ok_or_else(|| Error::other("no private key found in key file"))?;
let t = read(config.tls.certs)?;
let mut reader = BufReader::new(t.as_slice());
let certs = certs(&mut reader)
.collect::<Result<Vec<_>, _>>()?
.as_slice()
.to_owned();
let config = match ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certs, key)
{
Ok(e) => e,
Err(e) => return Err(Error::other(e)),
};
Ok(Some(Arc::new(config)))
} else {
Ok(None)
}
}
}
pub trait HandlerClone {
fn clone_box(&self) -> Box<dyn Handler>;
}
impl<T> HandlerClone for T
where
T: 'static + Handler + Clone,
{
fn clone_box(&self) -> Box<dyn Handler> {
Box::new(self.clone())
}
}
impl Clone for Box<dyn Handler> {
fn clone(&self) -> Box<dyn Handler> {
self.clone_box()
}
}
pub trait Handler: Send + Sync + HandlerClone + Debug {
fn on_request(&mut self, _request: Request, _response: &mut Response);
fn on_options(&mut self, response: &mut Response) {
response.allow_origins = vec![];
response.allow_methods = vec!["GET", "POST", "PUT", "DELETE", "OPTIONS"];
response.allow_headers = vec!["Authorization", "X-Forwarded-For", "X-Real-IP"];
response.header("Access-Control-Expose-Headers", "Content-Disposition");
response.header("Access-Control-Max-Age", 86400.to_string().as_str());
}
fn on_response(&mut self, response: &mut Response) {
if !response.headers.has_key("Access-Control-Allow-Origin") {
response.header("Access-Control-Allow-Origin", "*");
}
}
fn on_frame(&mut self) -> Result<(), HttpError> {
Ok(())
}
fn on_open(&mut self) -> Result<(), HttpError> {
Ok(())
}
fn on_message(&mut self, _msg: Message) -> Result<(), HttpError> {
Ok(())
}
fn on_close(&mut self, _code: CloseCode, _reason: &str) {}
fn on_error(&mut self, _err: ErrorCode) {}
fn on_shutdown(&mut self) {}
}
#[derive(Clone, Debug)]
pub enum Connection {
KeepAlive,
Close,
Other(String),
}
impl Connection {
pub fn from(value: &str) -> Self {
match value.to_lowercase().as_str() {
"keep-alive" => Self::KeepAlive,
"close" => Self::Close,
_ => Self::Other(value.to_string()),
}
}
pub fn str(&self) -> &str {
match self {
Connection::KeepAlive => "keep-alive",
Connection::Close => "close",
Connection::Other(name) => name,
}
}
}
#[derive(Clone, Debug)]
pub enum Upgrade {
Websocket,
Http,
H2c,
Other(String),
}
impl Upgrade {
#[must_use]
pub fn from(name: &str) -> Self {
match name.to_lowercase().as_str() {
"websocket" => Upgrade::Websocket,
"http" => Upgrade::Http,
"h2c" => Upgrade::H2c,
_ => Upgrade::Other(name.to_lowercase().as_str().to_string()),
}
}
#[must_use]
pub fn str(&self) -> &str {
match self {
Upgrade::Websocket => "websocket",
Upgrade::Http => "http",
Upgrade::H2c => "h2c",
Upgrade::Other(name) => name,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct Uri {
pub uri: String,
pub url: String,
pub query: String,
pub fragment: String,
pub path: String,
pub path_segments: Vec<String>,
}
impl Uri {
#[must_use]
pub fn from(url: &str) -> Self {
let mut decoded_url = br_crypto::encoding::urlencoding_decode(url);
let fragment = match decoded_url.rfind('#') {
None => String::new(),
Some(index) => decoded_url.drain(index..).collect::<String>(),
};
let query = match decoded_url.rfind('?') {
None => String::new(),
Some(index) => decoded_url
.drain(index..)
.collect::<String>()
.trim_start_matches("?")
.to_string(),
};
let path_segments = decoded_url
.split('/')
.map(|x| x.to_string())
.filter(|x| !x.is_empty())
.collect::<Vec<String>>();
Self {
uri: decoded_url.clone(),
url: url.to_string(),
query,
fragment,
path: decoded_url.clone(),
path_segments,
}
}
#[must_use]
pub fn get_query_params(&self) -> JsonValue {
let text = self.query.split('&').collect::<Vec<&str>>();
let mut params = object! {};
for item in text {
if let Some(index) = item.find('=') {
let key = item[..index].to_string();
let value = item[index + 1..].to_string();
let _ = params.insert(key.as_str(), value);
}
}
params
}
#[must_use]
pub fn to_json(&self) -> JsonValue {
object! {
url: self.url.clone(),
query: self.query.clone(),
fragment: self.fragment.clone(),
path: self.path.clone(),
path_segments: self.path_segments.clone()
}
}
}
#[derive(Clone, Debug)]
pub enum Method {
POST,
GET,
HEAD,
PUT,
DELETE,
OPTIONS,
PATCH,
TRACE,
VIEW,
CONNECT,
PROPFIND,
PRI,
Other(String),
}
impl Method {
#[must_use]
pub fn from(name: &str) -> Self {
match name.to_lowercase().as_str() {
"post" => Self::POST,
"get" => Self::GET,
"head" => Self::HEAD,
"put" => Self::PUT,
"delete" => Self::DELETE,
"options" => Self::OPTIONS,
"patch" => Self::PATCH,
"trace" => Self::TRACE,
"view" => Self::VIEW,
"propfind" => Self::PROPFIND,
"connect" => Self::CONNECT,
"pri" => Self::PRI,
_ => Self::Other(name.to_lowercase()),
}
}
#[must_use]
pub fn str(&self) -> &str {
match self {
Self::POST => "POST",
Self::GET => "GET",
Self::HEAD => "HEAD",
Self::PUT => "PUT",
Self::DELETE => "DELETE",
Self::OPTIONS => "OPTIONS",
Self::PATCH => "PATCH",
Self::TRACE => "TRACE",
Self::VIEW => "VIEW",
Self::PROPFIND => "PROPFIND",
Self::PRI => "PRI",
Self::CONNECT => "CONNECT",
Method::Other(e) => e.as_str(),
}
}
}
#[derive(Debug, Clone)]
pub struct HttpError {
pub code: u16,
pub body: String,
}
impl HttpError {
#[must_use]
pub fn new(code: u16, body: &str) -> Self {
Self {
code,
body: body.to_string(),
}
}
}
#[derive(Debug, Clone)]
pub enum ContentType {
FormData,
FormUrlencoded,
Json,
Xml,
Javascript,
Text,
Html,
Stream,
Other(String),
}
impl ContentType {
#[must_use]
pub fn from(name: &str) -> Self {
match name {
"multipart/form-data" => Self::FormData,
"application/x-www-form-urlencoded" => Self::FormUrlencoded,
"application/json" => Self::Json,
"application/xml" | "text/xml" => Self::Xml,
"application/javascript" => Self::Javascript,
"application/octet-stream" => Self::Stream,
"text/html" => Self::Html,
"text/plain" => Self::Text,
_ => Self::Other(name.to_string()),
}
}
#[must_use]
pub fn str(&self) -> &str {
match self {
ContentType::FormData => "multipart/form-data",
ContentType::FormUrlencoded => "application/x-www-form-urlencoded",
ContentType::Json => "application/json",
ContentType::Xml => "application/xml",
ContentType::Javascript => "application/javascript",
ContentType::Text => "text/plain",
ContentType::Html => "text/html",
ContentType::Other(name) => name.as_str(),
ContentType::Stream => "application/octet-stream",
}
}
}
pub(crate) fn parse_content_type_header_value(
value: &str,
) -> (String, std::collections::HashMap<String, String>) {
let mut it = value.split(';');
let mime = it.next().unwrap_or("").trim().to_lowercase();
let mut params = std::collections::HashMap::<String, String>::new();
for raw in it {
let raw = raw.trim();
if raw.is_empty() {
continue;
}
if let Some((k, v)) = raw.split_once('=') {
let key = k.trim().to_lowercase();
let mut val = v.trim();
if val.len() >= 2 && val.starts_with('"') && val.ends_with('"') {
val = &val[1..val.len() - 1];
}
params.insert(key, val.to_string());
} else {
params.insert(raw.to_lowercase(), String::new());
}
}
(mime, params)
}
#[derive(Clone, Debug)]
pub enum Authorization {
Basic(String, String),
Bearer(String),
Digest(JsonValue),
Other(String),
}
impl Authorization {
#[must_use]
pub fn from(data: &str) -> Self {
let authorization = data.split_whitespace().collect::<Vec<&str>>();
let mode = authorization[0].to_lowercase();
match mode.as_str() {
"basic" => {
let text = br_crypto::base64::decode(&authorization[1].to_string());
let text: Vec<&str> = text.split(':').collect();
Self::Basic(text[0].to_string(), text[1].to_string())
}
"bearer" => Self::Bearer(authorization[1].to_string()),
"digest" => {
let text = authorization[1..].concat().clone();
let text = text.split(',').collect::<Vec<&str>>();
let mut params = object! {};
for item in &text {
let Some(index) = item.find('=') else {
continue;
};
let key = item[..index].to_string();
let value = item[index + 2..item.len() - 1].to_string();
let _ = params.insert(key.as_str(), value);
}
Self::Digest(params)
}
_ => Self::Other(data.to_string()),
}
}
#[must_use]
pub fn str(&self) -> JsonValue {
match self {
Self::Basic(key, value) => {
let mut data = object! {};
data[key.as_str()] = value.clone().into();
data
}
Self::Bearer(e) => e.clone().into(),
Self::Digest(e) => e.clone(),
Self::Other(name) => name.clone().into(),
}
}
}
#[derive(Clone, Debug)]
pub enum Content {
FormUrlencoded(JsonValue),
FormData(JsonValue),
Json(JsonValue),
Text(JsonValue),
Xml(JsonValue),
None,
}
impl Content {}
#[derive(Clone, Debug)]
pub enum FormData {
File(String, PathBuf),
Field(JsonValue),
}
#[derive(Clone, Debug)]
pub enum Language {
ZhCN,
ZhHans,
En,
Other(String),
}
impl Language {
#[must_use]
pub fn from(name: &str) -> Self {
let binding = name.split(',').collect::<Vec<&str>>()[0]
.trim()
.to_lowercase();
let name = binding.as_str();
match name {
"zh-cn" => Self::ZhCN,
"zh-hans" => Self::ZhHans,
"en" => Self::En,
_ => Self::Other(name.to_string()),
}
}
#[must_use]
pub fn str(&self) -> &str {
match self {
Language::ZhCN => "zh-CN",
Language::ZhHans => "zh-Hans",
Language::En => "en",
Language::Other(e) => e.as_str(),
}
}
}
#[derive(Clone, Debug)]
pub enum Encoding {
Gzip,
Deflate,
Br,
Bzip2,
None,
}
impl Encoding {
#[must_use]
pub fn from(s: &str) -> Encoding {
match s.to_lowercase().as_str() {
x if x.contains("gzip") => Encoding::Gzip,
x if x.contains("deflate") => Encoding::Deflate,
x if x.contains("br") => Encoding::Br,
x if x.contains("bzip2") => Encoding::Bzip2,
_ => Encoding::None,
}
}
#[must_use]
pub fn str(&self) -> &str {
match self {
Encoding::Gzip => "gzip",
Encoding::Deflate => "deflate",
Encoding::Br => "br",
Encoding::Bzip2 => "bzip2",
Encoding::None => "",
}
}
pub fn compress(&mut self, data: &[u8]) -> Result<Vec<u8>, String> {
match self {
Encoding::Gzip => {
let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
match encoder.write_all(data) {
Ok(()) => {}
Err(e) => {
return Err(format!("Failed to compress file {}", e));
}
};
match encoder.finish() {
Ok(e) => Ok(e),
Err(e) => Err(format!("Failed to compress file {}", e)),
}
}
_ => Ok(data.to_vec()),
}
}
pub fn decompress(&mut self, data: &[u8]) -> Result<Vec<u8>, String> {
match self {
Encoding::Gzip => {
let mut d = GzDecoder::new(data);
let mut s = String::new();
match d.read_to_string(&mut s) {
Ok(_) => {}
Err(e) => {
return Err(format!("Failed to decompress file {}", e));
}
};
Ok(s.as_bytes().to_vec())
}
Encoding::Br => {
let mut decompressed = Vec::new();
let mut reader = brotli::Decompressor::new(data, 4096);
reader
.read_to_end(&mut decompressed)
.map_err(|e| format!("brotli decompress error: {e}"))?;
Ok(decompressed)
}
_ => Ok(data.to_vec()),
}
}
}
#[derive(Clone, Debug)]
pub enum Protocol {
HTTP1_0,
HTTP1_1,
HTTP2,
HTTP3,
Other(String),
}
impl Protocol {
#[must_use]
pub fn from(name: &str) -> Self {
match name.to_lowercase().as_str() {
"http/1.0" => Protocol::HTTP1_0,
"http/1.1" => Protocol::HTTP1_1,
"http/2.0" | "http/2" => Protocol::HTTP2,
"http/3.0" | "http/3" => Protocol::HTTP3,
_ => Protocol::Other(name.to_lowercase()),
}
}
#[must_use]
pub fn str(&self) -> &str {
match self {
Protocol::HTTP1_0 => "HTTP/1.0",
Protocol::HTTP1_1 => "HTTP/1.1",
Protocol::HTTP2 => "HTTP/2.0",
Protocol::HTTP3 => "HTTP/3.0",
Protocol::Other(protocol) => protocol.as_str(),
}
}
}
#[derive(Clone, Debug)]
pub struct Status {
pub code: u16,
reason: String,
}
impl Status {
pub fn set_code(&mut self, code: u16) {
self.code = code;
self.reason = match code {
100 => "Continue", 101 => "Switching Protocols", 102 => "Processing",
103 => "Early Hints", 200 => "OK", 201 => "Created", 202 => "Accepted", 204 => "No Content", 206 => "Partial Content", 301 => "Moved Permanently", 302 => "Found", 303 => "See Other", 304 => "Not Modified", 307 => "Temporary Redirect", 308 => "Permanent Redirect",
400 => "Bad Request", 401 => "Unauthorized", 403 => "Forbidden",
404 => "Not Found", 405 => "Method Not Allowed", 411 => "Length Required", 413 => "Payload Too Large", 414 => "URI Too Long", 416 => "Range Not Satisfiable",
429 => "Too Many Requests", 431 => "Request Header Fields Too Large",
500 => "Internal Server Error", 501 => "Not Implemented", 502 => "Bad Gateway", 503 => "Service Unavailable", 504 => "Gateway Time-out", 505 => "HTTP Version Not Supported", _ => "",
}
.to_string();
}
#[must_use]
pub fn from(code: u16, reason: &str) -> Self {
Status {
code,
reason: reason.to_string(),
}
}
}
impl Default for Status {
fn default() -> Self {
Self {
code: 200,
reason: "OK".to_string(),
}
}
}
#[derive(Debug)]
pub enum TransferEncoding {
Chunked,
Other(String),
}
impl TransferEncoding {
pub fn from(name: &str) -> TransferEncoding {
match name.to_lowercase().as_str() {
"chunked" => TransferEncoding::Chunked,
_ => TransferEncoding::Other(name.to_string()),
}
}
}
pub fn split_boundary(mut data: Vec<u8>, boundary: &str) -> Result<Vec<Vec<u8>>, String> {
let boundary = format!("--{boundary}");
let boundary_bytes = boundary.as_bytes();
let mut list = vec![];
loop {
if let Some(n) = data
.windows(boundary_bytes.len())
.position(|x| x == boundary_bytes)
{
let drain_end = n + boundary_bytes.len();
if drain_end > data.len() {
return Err("格式错误: boundary 超出数据范围".to_string());
}
data.drain(..drain_end);
if data.is_empty() || data.starts_with(b"--\r\n") || data.starts_with(b"--") {
break;
}
if data.starts_with(b"\r\n") {
data.drain(..2);
}
} else {
return Err("格式错误: 未找到 boundary".to_string());
}
if let Some(n) = data
.windows(boundary_bytes.len())
.position(|x| x == boundary_bytes)
{
if n > 0 {
let content_end = if n >= 2 && data[n - 2..n] == *b"\r\n" {
n - 2
} else {
n
};
list.push(data[..content_end].to_vec());
}
data.drain(..n);
} else {
return Err("格式错误: 未找到结束 boundary".to_string());
}
}
Ok(list)
}