Skip to content

Commit 5484cd1

Browse files
Merge pull request #3 from malandrakisgeo/static
Support for static websites in general
2 parents dff0de7 + 78075ff commit 5484cd1

File tree

14 files changed

+186
-139
lines changed

14 files changed

+186
-139
lines changed

README

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
A web server written in Rust. Suitable for static content.
2+
3+
Add your website under /static either as a subdirectory or by replacing all contents, and voila.
4+
The server expects an index.html.
5+
6+
7+
Features:
8+
Caching
9+
10+
11+
TODOs:
12+
Range requests
13+
Security
14+
Logging

src/cache/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod server_cache;
File renamed without changes.

src/config.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ impl Config {
2424
pub fn default_config() -> Config {
2525

2626
return Config{
27-
address: "127.0.0.1".to_string(), //127.0.0.1, 0.0.0.0 127.0.0.1:7151
27+
address: "127.0.0.1".to_string(), //127.0.0.1:7151
2828
port: "7151".to_string(),
2929
auth: Default::default(),
3030
file_dir: ".".to_string(),
3131
tls: None,
32-
max_cache_files: 5,
32+
max_cache_files: 10,
3333
largest_cacheable_file_size: 1000000000 //100MB in bytes
3434
}
3535
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
use std::{fs};
2+
use std::time::SystemTime;
3+
use urlencoding::decode;
4+
use crate::content_manager::song_site::songs_page;
5+
use crate::response::refera_error::ReferaError;
6+
use crate::server_cache::FileCacheTuple;
7+
8+
9+
pub fn get_file(name: &str) -> FileCacheTuple {
10+
//TODO: Add support for range requests
11+
12+
let f: FileCacheTuple;
13+
let mut ve: Vec<u8>;
14+
if name.eq("") || name.eq(" ") || name.eq("/") {
15+
ve = default_page();
16+
} else {
17+
ve = match parse_file(name) {
18+
Ok(data) => data,
19+
Err(err) => Vec::new()
20+
};
21+
}
22+
23+
f = FileCacheTuple(ve, SystemTime::now(), 0);
24+
25+
return f;
26+
}
27+
28+
29+
fn default_page() -> Vec<u8> {
30+
let contents = fs::read_to_string("./static/index.html").unwrap_or("Page not found!".parse().unwrap());
31+
32+
Vec::from(contents)
33+
}
34+
35+
36+
pub fn error_page() -> Vec<u8> {
37+
let error_page = fs::read_to_string("./static/not_found.html").unwrap();
38+
39+
Vec::from(error_page)
40+
}
41+
42+
43+
fn parse_file(path: &str) -> Result<Vec<u8>, ReferaError> {
44+
let mut proper_path = path.replace("%20", " ");
45+
46+
47+
let mut file: Vec<u8> = Vec::new();
48+
let decoded = decode(path).expect("UTF-8");
49+
50+
if fs::read_dir("./static".to_owned() + proper_path.as_mut_str()).is_err() { //if not a subdirectory
51+
file = fs::read("./static".to_owned() + decoded.as_ref()).map_err(|e| ReferaError::from(e))?;
52+
} else { //if the url corresponds to a directory under /static
53+
file = fs::read("./static".to_owned() + decoded.as_ref() + "/index.html").map_err(|e| ReferaError::from(e))?; //retrieve the index.html
54+
}
55+
56+
if (proper_path.contains("malandrakisgeo-songs-site-example") && !proper_path.contains(".mp3")) {
57+
file = songs_page(file);
58+
}
59+
60+
Ok(file)
61+
}
62+
63+
64+
/*fn parse_song_evolved(path: &str)-> Result<Vec<u8>, ReferaError>{
65+
let decoded = decode(path).expect("UTF-8");
66+
67+
const BUFFER_LEN: usize = 51200;
68+
let mut buffer = [0u8; BUFFER_LEN];
69+
let mut file = File::open("./static/songs".to_owned()+decoded.as_ref())?;
70+
file.read(&mut buffer).unwrap();
71+
72+
Ok(Vec::from(buffer))
73+
74+
}
75+
76+
pub fn generate_tds(files: ReadDir) -> String {
77+
let mut tds_vec = Vec::new();
78+
79+
for file in files {
80+
let data = &file.unwrap();
81+
let path = &data.path();
82+
let _name = data.file_name();
83+
let file_name = _name.to_str().unwrap();
84+
85+
let date_modified = data.metadata().unwrap().modified().unwrap(); //TODO: fix
86+
let dt: DateTime<Utc> = date_modified.clone().into();
87+
let showable_datetime = dt.format("%d/%m/%Y"); //%d/%m/%Y %T to show even time
88+
89+
let file_type = std::path::Path::new(path.as_path()).extension().unwrap_or("unknown_type".as_ref()).to_str().unwrap();
90+
let size = &data.metadata().unwrap().size();
91+
92+
let td = format!("<tr>\n<td><a href=\"songs/{file_name}\">{file_name}</a> </td>\n<td>{showable_datetime}</td>\n<td>{file_type}</td>\n<td>{size}</td>\n</tr>\n");
93+
// let td = format!("<tr>\n<td><a href=\"malandrakisgeo-songs-site-example/songs/{file_name}\">{file_name}</a> </td>\n<td>{showable_datetime}</td>\n<td>{file_type}</td>\n<td>{size}</td>\n</tr>\n");
94+
95+
tds_vec.append(&mut Vec::from(td));
96+
}
97+
98+
String::from_utf8(tds_vec).unwrap()
99+
}*/
100+

src/content_manager/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub mod content_parser;
2+
pub mod song_site;

src/content_manager/song_site.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use std::fs;
2+
use std::fs::ReadDir;
3+
use std::os::unix::fs::MetadataExt;
4+
use chrono::{DateTime, Utc};
5+
6+
/*
7+
The reason this project exists. I wanted to improve my Rust skills by writing a server that renders an HTML file with a list of songs,
8+
and serves them in demand. But I chose to make it a general purpose web server instead.
9+
TODO: Create a separate branch with the song_site
10+
*/
11+
12+
pub fn songs_page(mut contents: Vec<u8>) -> Vec<u8> {
13+
let paths = fs::read_dir("./static/malandrakisgeo-songs-site-example/songs/").unwrap_or(fs::read_dir(".").unwrap());
14+
let tds = generate_tds(paths);
15+
16+
let str = String::from_utf8(contents).unwrap();
17+
let content_to_serve = str.replace("{replace_me!}", &tds);
18+
19+
Vec::from(content_to_serve)
20+
}
21+
fn generate_tds(files: ReadDir) -> String {
22+
let mut tds_vec = Vec::new();
23+
24+
for file in files {
25+
let data = &file.unwrap();
26+
let path = &data.path();
27+
let _name = data.file_name();
28+
let file_name = _name.to_str().unwrap();
29+
30+
let date_modified = data.metadata().unwrap().modified().unwrap(); //TODO: fix
31+
let dt: DateTime<Utc> = date_modified.clone().into();
32+
let showable_datetime = dt.format("%d/%m/%Y"); //%d/%m/%Y %T to show even time
33+
34+
let file_type = std::path::Path::new(path.as_path()).extension().unwrap_or("unknown_type".as_ref()).to_str().unwrap();
35+
let size = &data.metadata().unwrap().size();
36+
37+
let td = format!("<tr>\n<td><a href=\"songs/{file_name}\">{file_name}</a> </td>\n<td>{showable_datetime}</td>\n<td>{file_type}</td>\n<td>{size}</td>\n</tr>\n");
38+
// let td = format!("<tr>\n<td><a href=\"malandrakisgeo-songs-site-example/songs/{file_name}\">{file_name}</a> </td>\n<td>{showable_datetime}</td>\n<td>{file_type}</td>\n<td>{size}</td>\n</tr>\n");
39+
40+
tds_vec.append(&mut Vec::from(td));
41+
}
42+
43+
String::from_utf8(tds_vec).unwrap()
44+
}
45+

src/content_parser.rs

Lines changed: 0 additions & 109 deletions
This file was deleted.

src/main.rs

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@ use std::collections::HashMap;
33
use std::io::{BufRead, BufReader, Read, Write};
44
use std::net::{TcpListener, TcpStream};
55
use std::ops::Add;
6+
use crate::cache::server_cache;
67
use crate::config::Config;
8+
use crate::content_manager::content_parser;
79
use crate::http::http_status::StatusCode;
810
use crate::response::refera_response::ReferaResponse;
911

1012
mod config;
11-
mod content_parser;
12-
mod server_cache;
1313
mod response;
1414
mod http;
15+
mod cache;
16+
mod content_manager;
1517

1618
fn main() {
1719
let conf = Config::default_config();
@@ -52,8 +54,6 @@ fn handle_http_req(request: &mut TcpStream) {
5254

5355
if method_url.get(0).unwrap().contains("GET") {
5456
resp = get_reply(method_url.get(1).unwrap(), http_headers.0);
55-
} else if method_url.get(0).unwrap().contains("POST") {
56-
resp = post_reply(&mut buf_reader, http_headers.0);
5757
} else {
5858
resp = ReferaResponse::new(StatusCode::BadRequest, None, Vec::new())
5959
}
@@ -64,28 +64,18 @@ fn get_reply(url: &str, headers: HashMap<String, String>) -> ReferaResponse {
6464
let cached_file = server_cache::file_lookup(url);
6565
if cached_file.is_none(){
6666
let file = content_parser::get_file(url);
67-
if!file.0.is_empty(){
67+
return if !file.0.is_empty() {
6868
server_cache::insert_file(url, &file);
69-
return ReferaResponse::new(StatusCode::Ok, None, file.0.clone());
70-
}else{
71-
return ReferaResponse::new(StatusCode::NotFound, None, content_parser::error_page());
69+
ReferaResponse::new(StatusCode::Ok, None, file.0.clone())
70+
} else {
71+
ReferaResponse::new(StatusCode::NotFound, None, content_parser::error_page())
7272
}
7373
}
7474

7575
ReferaResponse::new(StatusCode::Ok, None, cached_file.unwrap().0.clone())
7676
}
7777

7878

79-
fn post_reply(buf_reader: &mut BufReader<TcpStream>, headers: HashMap<String, String>) -> ReferaResponse { //WIP - TODO
80-
let content_length_str = headers.get_key_value("Content-Length").unwrap().1;
81-
let mut buffer: Vec<u8> = vec![0; content_length_str.trim().parse::<usize>().unwrap()];
82-
buf_reader.read_exact(&mut buffer).unwrap();
83-
84-
//let result = content_parser::post_content(buffer.clone(), "aa");
85-
86-
ReferaResponse::new(StatusCode::Ok, None, Vec::new())
87-
}
88-
8979
fn determine_headers(vector: &Vec<String>) -> (HashMap<String, String>, String) {
9080
let mut header_map = HashMap::new();
9181

@@ -104,16 +94,7 @@ fn determine_headers(vector: &Vec<String>) -> (HashMap<String, String>, String)
10494

10595

10696

107-
/*fn read(stream: &mut TcpStream) {
108-
let mut buf = vec![0; 1024];
109-
println!("Received {} bytes", stream.read(&mut buf).unwrap());
110-
let resp = ReferaResponse::new(StatusCode::Ok, None, Vec::new());
111-
stream.write_all(resp.as_u8().as_slice()).unwrap();
112-
}
113-
114-
115-
116-
97+
/*
11798
11899
This causes the request to stack. Why?
119100
According to stackoverflow, "read_to_string reads into String until EOF which will not happen until you close the stream from the writer side."

static/index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>default</title>
6+
</head>
7+
<body>
8+
9+
<p>Serve your static website by adding it ./static. It can be a subfolder or a replacement of all contents. The server looks up for an index.html </p>
10+
<p><a href="malandrakisgeo-songs-site-example/index.html"><< Sample website</a></p>
11+
12+
</body>
13+
</html>

0 commit comments

Comments
 (0)