Skip to content

Commit 62a08cf

Browse files
authored
Version/0.6.5 (#40)
* rebase to master - version 0.6.2 * Feature/better config (#38) * adhoc serve added * minor fixes * move safe_join_paths to responders * Add support for Connect method, allowing https proxying * Create Dockerfile * Improve speed and socket handling in TunnelResponse, add https support to main * Add test in request * Add proxy mode * Fix proxy handling, add arg to change port in cli * set trait generic set_stream function * improve cache system * HTTP core refactor * Make headers case insensitive, remove unnecessary code because this * Feature/response handling (#39) * basic traits and POCs * Do Handling system and add file handler * Improve context sharing with habdlers. Finish ProxyHandler * add Cache to Handler Context * add HandlerEngine * Rename Headers struct to HttpHeaders and minor adjustments * Improved http parsers * Add proper Socket status * fix done in request builder * fix speed regresion * add support for chunked body * Move chunked check to header state
1 parent fac5d82 commit 62a08cf

23 files changed

Lines changed: 1515 additions & 777 deletions

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "hteapot"
3-
version = "0.6.2"
3+
version = "0.6.5"
44
edition = "2024"
55
authors = ["Alb Ruiz G. <me@albruiz.dev>"]
66
description = "HTeaPot is a lightweight HTTP server library designed to be easy to use and extend."
@@ -25,6 +25,7 @@ path = "src/hteapot/mod.rs"
2525
name = "hteapot"
2626

2727
[dependencies]
28+
[target.'cfg(unix)'.dependencies]
2829
libc = "0.2.172"
2930

3031

Dockerfile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
FROM rust AS builder
2+
3+
WORKDIR /app
4+
COPY Cargo.lock Cargo.lock
5+
COPY Cargo.toml Cargo.toml
6+
COPY src ./src
7+
8+
RUN cargo build --release
9+
10+
FROM ubuntu
11+
12+
COPY --from=builder /app/target/release/hteapot /bin/hteapot
13+
14+
EXPOSE 80
15+
16+
WORKDIR /config
17+
18+
ENTRYPOINT ["/bin/hteapot"]
19+
CMD ["config.toml"]

examples/basic2.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use hteapot::{Hteapot, HttpRequest, HttpResponse, HttpStatus};
2+
3+
fn main() {
4+
let server = Hteapot::new("localhost", 8081);
5+
server.listen(move |req: HttpRequest| {
6+
// This will be executed for each request
7+
let body = String::from_utf8(req.body).unwrap_or("NOPE".to_string());
8+
for header in req.headers {
9+
println!("- {}: {}", header.0, header.1);
10+
}
11+
println!("{}", body);
12+
HttpResponse::new(
13+
HttpStatus::IAmATeapot,
14+
format!("Hello, I am HTeaPot\n{}", body),
15+
None,
16+
)
17+
});
18+
}

examples/proxy_con.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use hteapot::{Hteapot, HttpMethod, HttpRequest, HttpResponse, TunnelResponse};
2+
3+
fn main() {
4+
let server = Hteapot::new_threaded("0.0.0.0", 8081, 3);
5+
server.listen(move |req: HttpRequest| {
6+
println!("New request to {} {}!", req.method.to_str(), &req.path);
7+
if req.method == HttpMethod::CONNECT {
8+
TunnelResponse::new(&req.path)
9+
} else {
10+
println!("{:?}", req);
11+
let addr = req.headers.get("host");
12+
let addr = if let Some(addr) = addr {
13+
addr
14+
} else {
15+
return HttpResponse::new(
16+
hteapot::HttpStatus::InternalServerError,
17+
"content",
18+
None,
19+
);
20+
};
21+
req.brew(addr).unwrap_or(HttpResponse::new(
22+
hteapot::HttpStatus::InternalServerError,
23+
"content",
24+
None,
25+
))
26+
}
27+
});
28+
}

src/cache.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
// Written by Alberto Ruiz, 2024-11-05
2-
//
2+
//
33
// Config module: handles application configuration setup and parsing.
44
// This module defines structs and functions to load and validate
55
// configuration settings from files, environment variables, or other sources.
66

77
use std::collections::HashMap;
8+
use std::hash::Hash;
89
use std::time;
910
use std::time::SystemTime;
1011

@@ -24,14 +25,18 @@ use std::time::SystemTime;
2425
/// let data = cache.get("hello".into());
2526
/// assert!(data.is_some());
2627
/// ```
27-
pub struct Cache {
28+
pub struct Cache<K, V> {
2829
// TODO: consider make it generic
2930
// The internal store: (data, expiration timestamp)
30-
data: HashMap<String, (Vec<u8>, u64)>,
31+
data: HashMap<K, (V, u64)>,
3132
max_ttl: u64,
3233
}
3334

34-
impl Cache {
35+
impl<K, V> Cache<K, V>
36+
where
37+
K: Eq + Hash,
38+
V: Clone,
39+
{
3540
/// Creates a new `Cache` with the specified TTL in seconds.
3641
pub fn new(max_ttl: u64) -> Self {
3742
Cache {
@@ -61,14 +66,14 @@ impl Cache {
6166
}
6267

6368
/// Stores data in the cache with the given key and a TTL.
64-
pub fn set(&mut self, key: String, data: Vec<u8>) {
69+
pub fn set(&mut self, key: K, data: V) {
6570
self.data.insert(key, (data, self.get_ttl()));
6671
}
6772

6873
/// Retrieves data from the cache if it exists and hasn't expired.
6974
///
7075
/// Removes and returns `None` if the TTL has expired.
71-
pub fn get(&mut self, key: String) -> Option<Vec<u8>> {
76+
pub fn get(&mut self, key: &K) -> Option<V> {
7277
let r = self.data.get(&key);
7378
if r.is_some() {
7479
let (data, ttl) = r.unwrap();

src/config.rs

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// Written by Alberto Ruiz 2024-04-07 (Happy 3th monthsary)
2-
//
2+
//
33
// This is the config module: responsible for loading application configuration
44
// from a file and providing structured access to settings.
55

6-
use std::{any::Any, collections::HashMap, fs};
6+
use std::{any::Any, collections::HashMap, fs, path::Path};
77

88
/// Dynamic TOML value representation.
99
///
@@ -62,7 +62,7 @@ pub fn toml_parser(content: &str) -> HashMap<String, TOMLSchema> {
6262
let mut map = HashMap::new();
6363
let mut submap = HashMap::new();
6464
let mut title = "".to_string();
65-
65+
6666
let lines = content.split("\n");
6767
for line in lines {
6868
if line.starts_with("#") || line.is_empty() {
@@ -88,13 +88,13 @@ pub fn toml_parser(content: &str) -> HashMap<String, TOMLSchema> {
8888
submap = HashMap::new();
8989
continue;
9090
}
91-
91+
9292
// Split key and value
9393
let parts = line.split("=").collect::<Vec<&str>>();
9494
if parts.len() != 2 {
9595
continue;
9696
}
97-
97+
9898
// Remove leading and trailing whitespace
9999
let key = parts[0]
100100
.trim()
@@ -103,7 +103,7 @@ pub fn toml_parser(content: &str) -> HashMap<String, TOMLSchema> {
103103
if key.is_empty() {
104104
continue;
105105
}
106-
106+
107107
// Remove leading and trailing whitespace
108108
let value = parts[1].trim();
109109
let value = if value.contains('\'') || value.contains('"') {
@@ -152,14 +152,14 @@ pub fn toml_parser(content: &str) -> HashMap<String, TOMLSchema> {
152152
/// such as host, port, caching behavior, and proxy rules.
153153
#[derive(Debug)]
154154
pub struct Config {
155-
pub port: u16, // Port number to listen
156-
pub host: String, // Host name or IP
157-
pub root: String, // Root directory to serve files
155+
pub port: u16, // Port number to listen
156+
pub host: String, // Host name or IP
157+
pub root: String, // Root directory to serve files
158158
pub cache: bool,
159159
pub cache_ttl: u16,
160160
pub threads: u16,
161161
pub log_file: Option<String>,
162-
pub index: String, // Index file to serve by default
162+
pub index: String, // Index file to serve by default
163163
// pub error: String, // Error file to serve when a file is not found
164164
pub proxy_rules: HashMap<String, String>,
165165
}
@@ -192,6 +192,35 @@ impl Config {
192192
}
193193
}
194194

195+
pub fn new_serve(path: &str) -> Config {
196+
let mut s_path = "./".to_string();
197+
s_path.push_str(path);
198+
let serving_path = Path::new(&s_path);
199+
let file_name: &str;
200+
let root_dir: String;
201+
if serving_path.is_file() {
202+
let parent_path = serving_path.parent().unwrap();
203+
root_dir = parent_path.to_str().unwrap().to_string();
204+
file_name = serving_path.file_name().unwrap().to_str().unwrap();
205+
} else {
206+
file_name = "index.html";
207+
root_dir = serving_path.to_str().unwrap().to_string();
208+
};
209+
210+
Config {
211+
port: 8080,
212+
host: "0.0.0.0".to_string(),
213+
root: root_dir,
214+
index: file_name.to_string(),
215+
log_file: None,
216+
217+
threads: 1,
218+
cache: false,
219+
cache_ttl: 0,
220+
proxy_rules: HashMap::new(),
221+
}
222+
}
223+
195224
/// Loads configuration from a TOML file, returning defaults on failure.
196225
///
197226
/// Expects the file to contain `[HTEAPOT]` and optionally `[proxy]` sections.
@@ -224,13 +253,13 @@ impl Config {
224253

225254
// Suggested alternative parsing logic
226255
// if let Some(proxy_map) = map.get("proxy") {
227-
// for k in proxy_map.keys() {
228-
// if let Some(url) = proxy_map.get2(k) {
229-
// proxy_rules.insert(k.clone(), url);
230-
// } else {
231-
// println!("Missing or invalid proxy URL for key: {}", k);
232-
// }
233-
// }
256+
// for k in proxy_map.keys() {
257+
// if let Some(url) = proxy_map.get2(k) {
258+
// proxy_rules.insert(k.clone(), url);
259+
// } else {
260+
// println!("Missing or invalid proxy URL for key: {}", k);
261+
// }
262+
// }
234263
// }
235264

236265
// Extract main configuration
@@ -239,7 +268,6 @@ impl Config {
239268
// Suggested alternative parsing logic (Not working)
240269
// let map = map.get("HTEAPOT").unwrap_or(&TOMLSchema::new());
241270

242-
243271
Config {
244272
port: map.get2("port").unwrap_or(8080),
245273
host: map.get2("host").unwrap_or("".to_string()),
@@ -253,4 +281,20 @@ impl Config {
253281
proxy_rules,
254282
}
255283
}
284+
285+
pub fn new_proxy() -> Config {
286+
let mut proxy_rules = HashMap::new();
287+
proxy_rules.insert("/".to_string(), "".to_string());
288+
Config {
289+
port: 8080,
290+
host: "0.0.0.0".to_string(),
291+
root: "./".to_string(),
292+
cache: false,
293+
cache_ttl: 0,
294+
threads: 2,
295+
log_file: None,
296+
index: "index.html".to_string(),
297+
proxy_rules,
298+
}
299+
}
256300
}

0 commit comments

Comments
 (0)