you ever get bored and just write a raw TCP HTTP server
This commit is contained in:
parent
b0de168dda
commit
eda7f79fb0
5 changed files with 605 additions and 14 deletions
253
Cargo.lock
generated
253
Cargo.lock
generated
|
@ -2,16 +2,125 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android-tzdata"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android_system_properties"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362"
|
||||||
|
dependencies = [
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
|
||||||
|
dependencies = [
|
||||||
|
"android-tzdata",
|
||||||
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
|
"num-traits",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone"
|
||||||
|
version = "0.1.63"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"iana-time-zone-haiku",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone-haiku"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.15"
|
version = "1.0.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.77"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.171"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mcstatusface"
|
name = "mcstatusface"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
@ -22,6 +131,21 @@ version = "2.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.94"
|
version = "1.0.94"
|
||||||
|
@ -40,6 +164,12 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.20"
|
version = "1.0.20"
|
||||||
|
@ -78,6 +208,12 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.100"
|
version = "2.0.100"
|
||||||
|
@ -94,3 +230,120 @@ name = "unicode-ident"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"rustversion",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.61.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
|
||||||
|
dependencies = [
|
||||||
|
"windows-implement",
|
||||||
|
"windows-interface",
|
||||||
|
"windows-link",
|
||||||
|
"windows-result",
|
||||||
|
"windows-strings",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-implement"
|
||||||
|
version = "0.60.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-interface"
|
||||||
|
version = "0.59.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-strings"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
|
@ -4,5 +4,6 @@ version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
chrono = "0.4.40"
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = "1.0.140"
|
serde_json = "1.0.140"
|
||||||
|
|
247
src/http.rs
Normal file
247
src/http.rs
Normal file
|
@ -0,0 +1,247 @@
|
||||||
|
use std::{collections::HashMap, io::{BufRead, BufReader, Result, Write}, net::{SocketAddr, TcpListener, TcpStream}, thread, time::Duration};
|
||||||
|
|
||||||
|
use chrono::Local;
|
||||||
|
|
||||||
|
use crate::ThreadPool;
|
||||||
|
|
||||||
|
pub enum StatusCode {
|
||||||
|
OK,
|
||||||
|
NotFound,
|
||||||
|
// InternalServerError,
|
||||||
|
// ImATeapot,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Request<'a> {
|
||||||
|
stream: &'a TcpStream,
|
||||||
|
path: &'a str,
|
||||||
|
method: &'a str,
|
||||||
|
version: &'a str,
|
||||||
|
headers: HashMap<&'a str, &'a str>,
|
||||||
|
body: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Request<'a> {
|
||||||
|
pub fn new(stream: &'a TcpStream, lines: &'a Vec<String>) -> Result<Request<'a>> {
|
||||||
|
let request_line = lines[0].as_str();
|
||||||
|
let request_line_split: Vec<&str> = request_line.split(" ").collect();
|
||||||
|
let method = request_line_split[0];
|
||||||
|
let path = request_line_split[1];
|
||||||
|
let version = request_line_split[2];
|
||||||
|
|
||||||
|
let mut headers: HashMap<&'a str, &'a str> = HashMap::new();
|
||||||
|
if lines.len() > 1 {
|
||||||
|
let mut i: usize = 1;
|
||||||
|
loop {
|
||||||
|
if i >= lines.len() { break; }
|
||||||
|
let line = &lines[i];
|
||||||
|
if line.len() == 0 || !line.contains(":") { break; }
|
||||||
|
let (name, value) = line.split_once(":").unwrap();
|
||||||
|
headers.insert(name, value.trim());
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut body: Option<String> = None;
|
||||||
|
if lines.len() > headers.len() + 2 &&
|
||||||
|
(method == "POST" || method == "PUT") &&
|
||||||
|
lines[headers.len() + 1] == "\r\n"
|
||||||
|
{
|
||||||
|
body = Some(lines[headers.len() + 2..].join("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Request {
|
||||||
|
stream,
|
||||||
|
path,
|
||||||
|
method,
|
||||||
|
version,
|
||||||
|
headers,
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn address(&self) -> Result<SocketAddr> {
|
||||||
|
self.stream.peer_addr()
|
||||||
|
}
|
||||||
|
pub fn path(&self) -> &'a str {
|
||||||
|
self.path
|
||||||
|
}
|
||||||
|
pub fn method(&self) -> &'a str {
|
||||||
|
self.method
|
||||||
|
}
|
||||||
|
pub fn version(&self) -> &'a str {
|
||||||
|
self.version
|
||||||
|
}
|
||||||
|
pub fn body(&self) -> &Option<String> {
|
||||||
|
&self.body
|
||||||
|
}
|
||||||
|
pub fn headers(&self) -> &HashMap<&'a str, &'a str> {
|
||||||
|
&self.headers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Response<'a> {
|
||||||
|
stream: &'a TcpStream,
|
||||||
|
status: StatusCode,
|
||||||
|
headers: HashMap<&'a str, String>,
|
||||||
|
body: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Response<'a> {
|
||||||
|
pub fn new(stream: &'a TcpStream) -> Response<'a> {
|
||||||
|
Response {
|
||||||
|
stream,
|
||||||
|
status: StatusCode::OK,
|
||||||
|
headers: HashMap::from([
|
||||||
|
("Server", "mcstatusface".to_string()),
|
||||||
|
("Content-Type", "text/plain".to_string()),
|
||||||
|
]),
|
||||||
|
body: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn status(&mut self, status: StatusCode) {
|
||||||
|
self.status = status;
|
||||||
|
}
|
||||||
|
pub fn headers(&self) -> &HashMap<&'a str, String> {
|
||||||
|
&self.headers
|
||||||
|
}
|
||||||
|
pub fn set_header(&mut self, name: &'a str, value: String) {
|
||||||
|
self.headers.insert(name, value);
|
||||||
|
}
|
||||||
|
pub fn body(&mut self, body: String) {
|
||||||
|
self.body = Some(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send(&mut self) -> Result<usize> {
|
||||||
|
let mut len: usize = 0;
|
||||||
|
let code = match self.status {
|
||||||
|
StatusCode::OK => 200,
|
||||||
|
StatusCode::NotFound => 404,
|
||||||
|
// StatusCode::ImATeapot => 418,
|
||||||
|
// StatusCode::InternalServerError => 500,
|
||||||
|
};
|
||||||
|
let reason = match self.status {
|
||||||
|
StatusCode::OK => "OK",
|
||||||
|
StatusCode::NotFound => "Not Found",
|
||||||
|
// StatusCode::ImATeapot => "I'm a teapot",
|
||||||
|
// StatusCode::InternalServerError => "Internal Server Error",
|
||||||
|
};
|
||||||
|
len += self.stream.write(format!("HTTP/1.1 {} {}\r\n", code, reason).as_bytes()).unwrap();
|
||||||
|
|
||||||
|
let mut content_length: usize = 0;
|
||||||
|
if self.body.is_some() {
|
||||||
|
content_length = self.body.as_ref().unwrap().len();
|
||||||
|
}
|
||||||
|
self.set_header("Content-Length", content_length.to_string());
|
||||||
|
for (name, value) in &self.headers {
|
||||||
|
len += self.stream.write(format!("{name}: {value}\r\n").as_bytes()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.body.is_some() {
|
||||||
|
len += self.stream.write("\r\n".as_bytes()).unwrap();
|
||||||
|
len += self.stream.write(self.body.as_ref().unwrap().as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
Ok(len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HttpServer {
|
||||||
|
address: String,
|
||||||
|
port: u16,
|
||||||
|
max_connections: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HttpServer {
|
||||||
|
pub fn new(address: &str) -> HttpServer {
|
||||||
|
HttpServer {
|
||||||
|
address: address.to_string(),
|
||||||
|
port: 8080,
|
||||||
|
max_connections: 16,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(&self) -> Result<()> {
|
||||||
|
let pool = ThreadPool::new(self.max_connections);
|
||||||
|
let listener = TcpListener::bind(format!("{}:{}", self.address, self.port)).expect("Failed to bind to port");
|
||||||
|
|
||||||
|
println!("Now listening on {}:{}", self.address, self.port);
|
||||||
|
|
||||||
|
for stream in listener.incoming() {
|
||||||
|
match stream {
|
||||||
|
Ok(stream) => {
|
||||||
|
pool.execute(move || {
|
||||||
|
HttpServer::handle_client(&stream);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to handle incoming connection: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_client(stream: &TcpStream) {
|
||||||
|
let buf_reader = BufReader::new(stream);
|
||||||
|
let http_request: Vec<String> = buf_reader
|
||||||
|
.lines()
|
||||||
|
.map(|result| result.unwrap())
|
||||||
|
.take_while(|line| !line.is_empty())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let request = Request::new(stream, &http_request);
|
||||||
|
if request.is_err() {
|
||||||
|
eprintln!("Failed to process request: {}", request.err().unwrap());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let request = request.unwrap();
|
||||||
|
|
||||||
|
let response = Response::new(stream);
|
||||||
|
|
||||||
|
let start_date = Local::now();
|
||||||
|
match HttpServer::handle_request(&request, response) {
|
||||||
|
Ok(_) => {
|
||||||
|
let end_date = Local::now();
|
||||||
|
println!(
|
||||||
|
"[{}] {} {} {} - {}ms - {}",
|
||||||
|
start_date.format("%Y-%m-%d %H:%M:%S"),
|
||||||
|
request.method(),
|
||||||
|
request.path(),
|
||||||
|
request.version(),
|
||||||
|
(end_date - start_date).num_milliseconds(),
|
||||||
|
request.address().unwrap().ip(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to handle request: {e}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_request(request: &Request, mut response: Response) -> Result<usize> {
|
||||||
|
response.status(StatusCode::OK);
|
||||||
|
response.set_header("x-powered-by", "GIRL FUEL".to_string());
|
||||||
|
|
||||||
|
if request.method != "GET" {
|
||||||
|
response.status(StatusCode::NotFound);
|
||||||
|
return response.send()
|
||||||
|
}
|
||||||
|
|
||||||
|
response.status(StatusCode::OK);
|
||||||
|
response.set_header("Content-Type", "text/html".to_string());
|
||||||
|
response.body(r#"<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>hello world!~</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>it works!!</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"#.to_string());
|
||||||
|
response.send()
|
||||||
|
}
|
||||||
|
}
|
85
src/lib.rs
85
src/lib.rs
|
@ -1,4 +1,87 @@
|
||||||
|
use std::{sync::{mpsc, Arc, Mutex}, thread};
|
||||||
|
|
||||||
pub mod leb128;
|
pub mod leb128;
|
||||||
pub mod status;
|
pub mod status;
|
||||||
|
pub mod http;
|
||||||
pub use status::MinecraftStatus;
|
pub use status::MinecraftStatus;
|
||||||
|
|
||||||
|
pub struct ThreadPool {
|
||||||
|
workers: Vec<ThreadWorker>,
|
||||||
|
sender: Option<mpsc::Sender<Job>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Job = Box<dyn FnOnce() + Send + 'static>;
|
||||||
|
|
||||||
|
impl ThreadPool {
|
||||||
|
// Create a new ThreadPool with `size` available threads.
|
||||||
|
//
|
||||||
|
// # Panics
|
||||||
|
//
|
||||||
|
// `new` will panic if `size` is zero.
|
||||||
|
pub 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 id in 0..size {
|
||||||
|
workers.push(ThreadWorker::new(id, Arc::clone(&receiver)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadPool {
|
||||||
|
workers,
|
||||||
|
sender: Some(sender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute<F>(&self, f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce() + Send + 'static
|
||||||
|
{
|
||||||
|
let job = Box::new(f);
|
||||||
|
|
||||||
|
self.sender.as_ref().unwrap().send(job).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ThreadPool {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
drop(self.sender.take());
|
||||||
|
|
||||||
|
for worker in &mut self.workers.drain(..) {
|
||||||
|
println!("Shutting down worker {}", worker.id);
|
||||||
|
|
||||||
|
worker.thread.join().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ThreadWorker {
|
||||||
|
id: usize,
|
||||||
|
thread: thread::JoinHandle<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThreadWorker {
|
||||||
|
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> ThreadWorker {
|
||||||
|
let thread = thread::spawn(move || loop {
|
||||||
|
let msg = receiver.lock().unwrap().recv();
|
||||||
|
|
||||||
|
match msg {
|
||||||
|
Ok(job) => {
|
||||||
|
// println!("Job received by worker {id}");
|
||||||
|
job();
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
// println!("Worker {id} disconnected. Shutting down...");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ThreadWorker { id, thread }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
11
src/main.rs
11
src/main.rs
|
@ -2,15 +2,17 @@ use std::io::{Result};
|
||||||
use std::net::{ToSocketAddrs};
|
use std::net::{ToSocketAddrs};
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
use mcstatusface::MinecraftStatus;
|
use mcstatusface::http::HttpServer;
|
||||||
|
use mcstatusface::{MinecraftStatus};
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
eprintln!("Usage: {} <address[:port]>", args[0]);
|
eprintln!("Usage: {} <serve | address[:port]>", args[0]);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if args[1] != "serve" {
|
||||||
let mut address = String::from(args[1].as_str());
|
let mut address = String::from(args[1].as_str());
|
||||||
if !address.contains(":") {
|
if !address.contains(":") {
|
||||||
address += ":25565";
|
address += ":25565";
|
||||||
|
@ -25,5 +27,10 @@ fn main() -> Result<()> {
|
||||||
println!("MOTD:");
|
println!("MOTD:");
|
||||||
println!("{}", status.parse_description());
|
println!("{}", status.parse_description());
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpServer::new("0.0.0.0").start().unwrap();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue