Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions hteapot.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
[HTEAPOT]
port = 8081
host = "0.0.0.0" #test de comentario
host = "0.0.0.0"
root = "public"
threads = 5
cache = true
cache_ttl = 3600
[proxy]
"/test" = "https://example.com"
"/google" = "http://google.com"
"/test" = "http://example.com"
"/google" = "http://google.com"
# "/" = "http://ifconfig.co" # this will override all the proxys and local request
21 changes: 13 additions & 8 deletions src/brew.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
// Written by Alberto Ruiz 2024-04-08
// This is the HTTP client module, it will handle the requests and responses

use std::{io::{Read, Write}, net::TcpStream};
use std::{io::{Read, Write}, net::TcpStream, vec};


struct Url {
scheme: String,
domain: String,
path: String,
port: String
}


fn parse_url(url: &str) -> Result<Url,&str> {
let url_parts = url.split(":").collect::<Vec<&str>>();
let prefix = url_parts[0];
let domain = url_parts[1].trim_start_matches("//");
let domain_path = url_parts[1].trim_start_matches("//");
let port = if url_parts.len() == 3 {
url_parts[2]
} else {
Expand All @@ -24,41 +25,45 @@ fn parse_url(url: &str) -> Result<Url,&str> {
_ => "80"
}
};
let (domain,path) = domain_path.split_once('/').unwrap();
Ok(Url {
scheme: prefix.to_string(),
domain: domain.to_string(),
path: path.to_string(),
port: port.to_string()
})
}

pub fn fetch(url: &str) -> Result<String,&str> {
pub fn fetch(url: &str) -> Result<Vec<u8>,&str> {
let url = parse_url(url);
if url.is_err() { return Err("Error parsing url")}
let url = url.unwrap();
if url.scheme == "https" {
return Err("not supported yet");
}

let client = TcpStream::connect(format!("{}:{}",url.domain,url.port));
if client.is_err() {
return Err("Error fetching");
}
let mut client = client.unwrap();
let http_request = format!("GET / HTTP/1.1\r\nHost: {}\r\n\r\n", url.domain);
let http_request = format!("GET /{} HTTP/1.1\r\nHost: {}\r\n\r\n",url.path, url.domain);
client.write(http_request.as_bytes()).unwrap();
let mut response = String::new();
let mut full_buffer: Vec<u8> = Vec::new();
let mut buffer = [0; 1024];
loop {
match client.read(&mut buffer) {
Ok(0) => break,
Ok(n) => {
response.push_str(std::str::from_utf8(&buffer[..n]).unwrap());
if response.ends_with("\n") {break} //TODO: break when size == header

if n == 0 {break;}
full_buffer.extend(buffer.iter().cloned());
if buffer.last().unwrap() == &0 {break;}
},
Err(_) => break
}
}
Ok(response)
Ok(full_buffer)
}


35 changes: 30 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,18 @@ fn main() {
} else {
config::Config::new_default()
};
let proxy_only = config.proxy_rules.get("/").is_some();
let logger = Mutex::new(Logger::new(io::stdout()));
let cache: Mutex<HashMap<String, (Vec<u8>, u64)>> = Mutex::new(HashMap::new());
let server = Hteapot::new_threaded(config.host.as_str(), config.port,config.threads);
logger.lock().expect("this doesnt work :C").msg(format!("Server started at http://{}:{}", config.host, config.port));
if config.cache {
logger.lock().expect("this doesnt work :C").msg("Cache Enabled".to_string());
}
if proxy_only {
logger.lock().expect("this doesnt work :C").msg("WARNING: All requests are proxied to /. Local paths won’t be used.".to_string());
}


server.listen( move |req| {
//let mut logger = Logger::new(io::stdout());
Expand All @@ -42,12 +47,32 @@ fn main() {
} else {
req.path.clone()
};
if config.proxy_rules.contains_key(&req.path) {
logger.lock().expect("this doesnt work :C").msg(format!("Proxying to: {}", config.proxy_rules.get(&req.path).unwrap()));
let url = config.proxy_rules.get(&req.path).unwrap();
return match fetch(url) {
let path_clone = req.path.clone();
let divided_path: Vec<&str> = path_clone.split('/').skip(1).collect();
if divided_path.is_empty() {
return Hteapot::response_maker(HttpStatus::BadRequest, b"Invalid path", None);
}

let first_one = format!("/{}",divided_path[0]);
let rest_path = divided_path[1..].join("/");

if proxy_only || config.proxy_rules.contains_key(&first_one) {
let url = if proxy_only {
let url = config.proxy_rules.get("/").unwrap();
if rest_path.len() != 0 {
format!("{}{}/{}",url,first_one,rest_path)

} else {
format!("{}{}",url,first_one)
}
} else {
let url = config.proxy_rules.get(&first_one).unwrap();
format!("{}/{}",url,rest_path)
};
logger.lock().expect("this doesnt work :C").msg(format!("Proxying to: {}", url));
return match fetch(&url) {
Ok(response) => {
response.into()
response
},
Err(err) => {
Hteapot::response_maker(HttpStatus::InternalServerError, err.as_bytes(), None)
Expand Down