Small amount of refactoring

This commit is contained in:
int 80h
2021-11-21 15:04:26 -05:00
parent e5c44c2c53
commit 4fb31bec38
6 changed files with 80 additions and 72 deletions

18
Cargo.lock generated
View File

@@ -140,7 +140,7 @@ dependencies = [
[[package]] [[package]]
name = "gemserv" name = "gemserv"
version = "0.4.7" version = "0.4.8"
dependencies = [ dependencies = [
"futures-util", "futures-util",
"log", "log",
@@ -193,9 +193,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.107" version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
@@ -326,9 +326,9 @@ dependencies = [
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.70" version = "0.9.71"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6517987b3f8226b5da3661dad65ff7f300cc59fb5ea8333ca191fc65fde3edf" checksum = "7df13d165e607909b363a4757a6f133f8a818a74e9d3a98d09c6128e15fa4c73"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"cc", "cc",
@@ -521,9 +521,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.13.0" version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "588b2d10a336da58d877567cd8fb8a14b463e2104910f8132cd054b4b96e29ee" checksum = "70e992e41e0d2fb9f755b37446f20900f64446ef54874f40a60c78f021ac6144"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"bytes", "bytes",
@@ -541,9 +541,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio-macros" name = "tokio-macros"
version = "1.5.1" version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "114383b041aa6212c579467afa0075fbbdd0718de036100bc0ba7961d8cb9095" checksum = "c9efc1aba077437943f7515666aa2b882dfabfbfdf89c819ea75a8d6e9eaba5e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "gemserv" name = "gemserv"
version = "0.4.7" version = "0.4.8"
authors = ["int 80h <int@80h.dev>"] authors = ["int 80h <int@80h.dev>"]
edition = "2018" edition = "2018"
description = "A gemini server" description = "A gemini server"
@@ -12,7 +12,7 @@ license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
tokio = { version = "1.13.0", features = [ "full" ] } tokio = { version = "1.14", features = [ "full" ] }
openssl = "0.10.38" openssl = "0.10.38"
tokio-openssl = "0.6.3" tokio-openssl = "0.6.3"
futures-util = "0.3.17" futures-util = "0.3.17"

View File

@@ -89,7 +89,6 @@ fn check(byt: u8, peer_addr: SocketAddr, u: &url::Url) -> bool {
#[cfg(feature = "cgi")] #[cfg(feature = "cgi")]
pub async fn cgi( pub async fn cgi(
con: &mut conn::Connection, con: &mut conn::Connection,
srv: &config::ServerCfg,
path: PathBuf, path: PathBuf,
url: &url::Url, url: &url::Url,
script_name: String, script_name: String,
@@ -97,7 +96,7 @@ pub async fn cgi(
) -> Result<(), io::Error> { ) -> Result<(), io::Error> {
let x509 = con.stream.ssl().peer_certificate(); let x509 = con.stream.ssl().peer_certificate();
let mut envs = envs(con.peer_addr, x509, srv, &url); let mut envs = envs(con.peer_addr, x509, &con.srv, &url);
envs.insert("SCRIPT_NAME".into(), script_name); envs.insert("SCRIPT_NAME".into(), script_name);
envs.insert("PATH_INFO".into(), path_info); envs.insert("PATH_INFO".into(), path_info);
@@ -148,7 +147,7 @@ pub async fn cgi(
} }
#[cfg(feature = "scgi")] #[cfg(feature = "scgi")]
pub async fn scgi(addr: String, u: url::Url, mut con: conn::Connection, srv: &config::ServerCfg) -> Result<(), io::Error> { pub async fn scgi(addr: String, u: url::Url, mut con: conn::Connection) -> Result<(), io::Error> {
let addr = addr let addr = addr
.to_socket_addrs()? .to_socket_addrs()?
.next() .next()
@@ -163,7 +162,7 @@ pub async fn scgi(addr: String, u: url::Url, mut con: conn::Connection, srv: &co
} }
}; };
let x509 = con.stream.ssl().peer_certificate(); let x509 = con.stream.ssl().peer_certificate();
let envs = envs(con.peer_addr, x509, srv, &u); let envs = envs(con.peer_addr, x509, &con.srv, &u);
let len = 0usize; let len = 0usize;
let mut byt = String::from(format!("CONTENT_LENGTH\x00{}\x00SCGI\x001\x00 let mut byt = String::from(format!("CONTENT_LENGTH\x00{}\x00SCGI\x001\x00
RQUEST_METHOD\x00POST\x00REQUEST_URI\x00{}\x00", len, u.path())); RQUEST_METHOD\x00POST\x00REQUEST_URI\x00{}\x00", len, u.path()));

View File

@@ -64,4 +64,4 @@ impl Config {
} }
map map
} }
} }

View File

@@ -12,6 +12,7 @@ use crate::status::Status;
pub struct Connection { pub struct Connection {
pub stream: SslStream<TcpStream>, pub stream: SslStream<TcpStream>,
pub peer_addr: SocketAddr, pub peer_addr: SocketAddr,
pub srv: crate::config::ServerCfg,
} }
impl Connection { impl Connection {

View File

@@ -64,7 +64,7 @@ async fn get_binary(mut con: conn::Connection, path: PathBuf, meta: String) -> i
Ok(()) Ok(())
} }
async fn get_content(path: PathBuf, u: url::Url) -> Result<String, io::Error> { async fn get_content(path: PathBuf, u: &url::Url) -> Result<String, io::Error> {
let meta = tokio::fs::metadata(&path).await?; let meta = tokio::fs::metadata(&path).await?;
if meta.is_file() { if meta.is_file() {
return Ok(tokio::fs::read_to_string(path).await?); return Ok(tokio::fs::read_to_string(path).await?);
@@ -119,12 +119,11 @@ async fn get_content(path: PathBuf, u: url::Url) -> Result<String, io::Error> {
#[cfg(feature = "cgi")] #[cfg(feature = "cgi")]
async fn handle_cgi( async fn handle_cgi(
con: &mut conn::Connection, con: &mut conn::Connection,
srv: &config::ServerCfg,
request: &str, request: &str,
url: &Url, url: &Url,
full_path: &PathBuf, full_path: &PathBuf,
) -> Result<bool, io::Error> { ) -> Result<bool, io::Error> {
if srv.server.cgi.unwrap_or(false) { if con.srv.server.cgi.unwrap_or(false) {
let mut path = full_path.clone(); let mut path = full_path.clone();
let mut segments = url.path_segments().unwrap(); let mut segments = url.path_segments().unwrap();
let mut path_info = "".to_string(); let mut path_info = "".to_string();
@@ -143,11 +142,11 @@ async fn handle_cgi(
let meta = tokio::fs::metadata(&path).await?; let meta = tokio::fs::metadata(&path).await?;
let perm = meta.permissions(); let perm = meta.permissions();
match &srv.server.cgipath { match &con.srv.server.cgipath {
Some(c) => { Some(c) => {
if path.starts_with(c) { if path.starts_with(c) {
if perm.mode() & 0o0111 == 0o0111 { if perm.mode() & 0o0111 == 0o0111 {
cgi::cgi(con, srv, path, url, script_name, path_info).await?; cgi::cgi(con, path, url, script_name, path_info).await?;
return Ok(true); return Ok(true);
} else { } else {
logger::logger(con.peer_addr, Status::CGIError, request); logger::logger(con.peer_addr, Status::CGIError, request);
@@ -158,7 +157,7 @@ async fn handle_cgi(
}, },
None => { None => {
if meta.is_file() && perm.mode() & 0o0111 == 0o0111 { if meta.is_file() && perm.mode() & 0o0111 == 0o0111 {
cgi::cgi(con, srv, path, url, script_name, path_info).await?; cgi::cgi(con, path, url, script_name, path_info).await?;
return Ok(true); return Ok(true);
} }
}, },
@@ -167,30 +166,22 @@ async fn handle_cgi(
Ok(false) Ok(false)
} }
// TODO Rewrite this monster. async fn get_request(mut con: conn::Connection) -> Result<(conn::Connection, url::Url), String> {
async fn handle_connection(
mut con: conn::Connection,
srv: &config::ServerCfg,
) -> Result<(), io::Error> {
let index = match &srv.server.index {
Some(i) => i.clone(),
None => "index.gemini".to_string(),
};
let mut buffer = [0; 1024]; let mut buffer = [0; 1024];
let len = match tokio::time::timeout(tokio::time::Duration::from_secs(5), con.stream.read(&mut buffer)).await { let len = match tokio::time::timeout(tokio::time::Duration::from_secs(5), con.stream.read(&mut buffer)).await {
Ok(result) => result.unwrap(), Ok(result) => result.unwrap(),
Err(_) => { Err(e) => {
logger::logger(con.peer_addr, Status::BadRequest, ""); logger::logger(con.peer_addr, Status::BadRequest, "");
con.send_status(Status::BadRequest, None).await?; con.send_status(Status::BadRequest, None).await.map_err(|e| e.to_string())?;
return Ok(()); return Err(e.to_string());
} }
}; };
let mut request = match String::from_utf8(buffer[..len].to_vec()) { let mut request = match String::from_utf8(buffer[..len].to_vec()) {
Ok(request) => request, Ok(request) => request,
Err(_) => { Err(e) => {
logger::logger(con.peer_addr, Status::BadRequest, ""); logger::logger(con.peer_addr, Status::BadRequest, "");
con.send_status(Status::BadRequest, None).await?; con.send_status(Status::BadRequest, None).await.map_err(|e| e.to_string())?;
return Ok(()); return Err(e.to_string());
} }
}; };
if request.starts_with("//") { if request.starts_with("//") {
@@ -208,35 +199,48 @@ async fn handle_connection(
Ok(url) => url, Ok(url) => url,
Err(_) => { Err(_) => {
logger::logger(con.peer_addr, Status::BadRequest, &request); logger::logger(con.peer_addr, Status::BadRequest, &request);
con.send_status(Status::BadRequest, None).await?; con.send_status(Status::BadRequest, None).await.map_err(|e| e.to_string())?;
return Ok(()); return Err("Bad url".to_string());
} }
}; };
if Some(srv.server.hostname.as_str()) != url.host_str() { if Some(con.srv.server.hostname.as_str()) != url.host_str() {
logger::logger(con.peer_addr, Status::ProxyRequestRefused, &request); logger::logger(con.peer_addr, Status::ProxyRequestRefused, &url.as_str());
con.send_status(Status::ProxyRequestRefused, None).await?; con.send_status(Status::ProxyRequestRefused, None).await.map_err(|e| e.to_string())?;
return Ok(()); return Err("Wrong host".to_string());
} }
match url.port() { match url.port() {
Some(p) => { Some(p) => {
if p != srv.port { if p != con.srv.port {
logger::logger(con.peer_addr, Status::ProxyRequestRefused, &request); logger::logger(con.peer_addr, Status::ProxyRequestRefused, &url.as_str());
con.send_status(status::Status::ProxyRequestRefused, None) con.send_status(status::Status::ProxyRequestRefused, None)
.await?; .await.map_err(|e| e.to_string())?;
} }
} }
None => {} None => {}
} }
if url.scheme() != "gemini" { if url.scheme() != "gemini" {
logger::logger(con.peer_addr, Status::ProxyRequestRefused, &request); logger::logger(con.peer_addr, Status::ProxyRequestRefused, &url.as_str());
con.send_status(Status::ProxyRequestRefused, None).await?; con.send_status(Status::ProxyRequestRefused, None).await.map_err(|e| e.to_string())?;
return Ok(()); return Err("scheme not gemini".to_string());
} }
return Ok((con, url))
}
match &srv.server.redirect { // TODO Rewrite this monster.
async fn handle_connection(
mut con: conn::Connection,
url: url::Url
) -> Result<(), io::Error> {
let index = match &con.srv.server.index {
Some(i) => i.clone(),
None => "index.gemini".to_string(),
};
match &con.srv.server.redirect.to_owned() {
Some(re) => { Some(re) => {
let u = match url.path() { let u = match url.path() {
"/" => "/", "/" => "/",
@@ -244,7 +248,7 @@ async fn handle_connection(
}; };
match re.get(u) { match re.get(u) {
Some(r) => { Some(r) => {
logger::logger(con.peer_addr, Status::RedirectTemporary, &request); logger::logger(con.peer_addr, Status::RedirectTemporary, &url.as_str());
con.send_status(Status::RedirectTemporary, Some(r)).await?; con.send_status(Status::RedirectTemporary, Some(r)).await?;
return Ok(()); return Ok(());
} }
@@ -255,7 +259,7 @@ async fn handle_connection(
} }
#[cfg(feature = "proxy")] #[cfg(feature = "proxy")]
if let Some(pr) = &srv.server.proxy_all { if let Some(pr) = con.srv.server.proxy_all.to_owned() {
let host_port: Vec<&str> = pr.splitn(2, ':').collect(); let host_port: Vec<&str> = pr.splitn(2, ':').collect();
let host = host_port[0]; let host = host_port[0];
let port: Option<u16>; let port: Option<u16>;
@@ -269,12 +273,12 @@ async fn handle_connection(
upstream_url.set_host(Some(host)).unwrap(); upstream_url.set_host(Some(host)).unwrap();
upstream_url.set_port(port).unwrap(); upstream_url.set_port(port).unwrap();
revproxy::proxy_all(pr, upstream_url, con).await?; revproxy::proxy_all(pr.as_str(), upstream_url, con).await?;
return Ok(()); return Ok(());
} }
#[cfg(feature = "proxy")] #[cfg(feature = "proxy")]
match &srv.server.proxy { match &con.srv.server.proxy {
Some(pr) => match url.path_segments().map(|c| c.collect::<Vec<_>>()) { Some(pr) => match url.path_segments().map(|c| c.collect::<Vec<_>>()) {
Some(s) => match pr.get(s[0]) { Some(s) => match pr.get(s[0]) {
Some(p) => { Some(p) => {
@@ -289,7 +293,7 @@ async fn handle_connection(
} }
#[cfg(feature = "scgi")] #[cfg(feature = "scgi")]
match &srv.server.scgi { match &con.srv.server.scgi {
Some(sc) => { Some(sc) => {
let u = match url.path() { let u = match url.path() {
"/" => "/", "/" => "/",
@@ -297,7 +301,7 @@ async fn handle_connection(
}; };
match sc.get(u) { match sc.get(u) {
Some(r) => { Some(r) => {
cgi::scgi(r.to_string(), url, con, srv).await?; cgi::scgi(r.to_string(), url, con).await?;
return Ok(()); return Ok(());
} }
None => {} None => {}
@@ -309,7 +313,7 @@ async fn handle_connection(
let mut path = PathBuf::new(); let mut path = PathBuf::new();
if url.path().starts_with("/~") && srv.server.usrdir.unwrap_or(false) { if url.path().starts_with("/~") && con.srv.server.usrdir.unwrap_or(false) {
let usr = url.path().trim_start_matches("/~"); let usr = url.path().trim_start_matches("/~");
let usr: Vec<&str> = usr.splitn(2, "/").collect(); let usr: Vec<&str> = usr.splitn(2, "/").collect();
if cfg!(target_os = "macos") { if cfg!(target_os = "macos") {
@@ -323,7 +327,7 @@ async fn handle_connection(
path.push(format!("{}/{}/", usr[0], "public_gemini")); path.push(format!("{}/{}/", usr[0], "public_gemini"));
} }
} else { } else {
path.push(&srv.server.dir); path.push(&con.srv.server.dir);
if url.path() != "" || url.path() != "/" { if url.path() != "" || url.path() != "/" {
let decoded = util::url_decode(url.path().trim_start_matches("/").as_bytes()); let decoded = util::url_decode(url.path().trim_start_matches("/").as_bytes());
path.push(decoded); path.push(decoded);
@@ -333,11 +337,11 @@ async fn handle_connection(
if !path.exists() { if !path.exists() {
// See if it's a subpath of a CGI script before returning NotFound // See if it's a subpath of a CGI script before returning NotFound
#[cfg(feature = "cgi")] #[cfg(feature = "cgi")]
if handle_cgi(&mut con, srv, &request, &url, &path).await? { if handle_cgi(&mut con, &url.as_str(), &url, &path).await? {
return Ok(()); return Ok(());
} }
logger::logger(con.peer_addr, Status::NotFound, &request); logger::logger(con.peer_addr, Status::NotFound, &url.as_str());
con.send_status(Status::NotFound, None).await?; con.send_status(Status::NotFound, None).await?;
return Ok(()); return Ok(());
} }
@@ -349,7 +353,7 @@ async fn handle_connection(
// This block is terrible // This block is terrible
if meta.is_dir() { if meta.is_dir() {
if !url.path().ends_with("/") { if !url.path().ends_with("/") {
logger::logger(con.peer_addr, Status::RedirectPermanent, &request); logger::logger(con.peer_addr, Status::RedirectPermanent, &url.as_str());
con.send_status( con.send_status(
Status::RedirectPermanent, Status::RedirectPermanent,
Some(format!("{}/", url).as_str()), Some(format!("{}/", url).as_str()),
@@ -372,35 +376,35 @@ async fn handle_connection(
} }
#[cfg(feature = "cgi")] #[cfg(feature = "cgi")]
if handle_cgi(&mut con, srv, &request, &url, &path).await? { if handle_cgi(&mut con, &url.as_str(), &url, &path).await? {
return Ok(()); return Ok(());
} }
if meta.is_file() && perm.mode() & 0o0111 == 0o0111 { if meta.is_file() && perm.mode() & 0o0111 == 0o0111 {
logger::logger(con.peer_addr, Status::NotFound, &request); logger::logger(con.peer_addr, Status::NotFound, &url.as_str());
con.send_status(Status::NotFound, None).await?; con.send_status(Status::NotFound, None).await?;
return Ok(()); return Ok(());
} }
if perm.mode() & 0o0444 != 0o0444 { if perm.mode() & 0o0444 != 0o0444 {
logger::logger(con.peer_addr, Status::NotFound, &request); logger::logger(con.peer_addr, Status::NotFound, &url.as_str());
con.send_status(Status::NotFound, None).await?; con.send_status(Status::NotFound, None).await?;
return Ok(()); return Ok(());
} }
let mut mime = get_mime(&path); let mut mime = get_mime(&path);
if mime == "text/gemini" && srv.server.lang.is_some() { if mime == "text/gemini" && con.srv.server.lang.is_some() {
mime += &("; lang=".to_string() + &srv.server.lang.to_owned().unwrap()); mime += &("; lang=".to_string() + &con.srv.server.lang.to_owned().unwrap());
} }
if !mime.starts_with("text/") { if !mime.starts_with("text/") {
logger::logger(con.peer_addr, Status::Success, &request); logger::logger(con.peer_addr, Status::Success, &url.as_str());
get_binary(con, path, mime).await?; get_binary(con, path, mime).await?;
return Ok(()); return Ok(());
} }
let content = get_content(path, url).await?; let content = get_content(path, &url).await?;
con.send_body(status::Status::Success, Some(&mime), Some(content)) con.send_body(status::Status::Success, Some(&mime), Some(content))
.await?; .await?;
logger::logger(con.peer_addr, Status::Success, &request); logger::logger(con.peer_addr, Status::Success, &url.as_str());
Ok(()) Ok(())
} }
@@ -484,10 +488,14 @@ fn main() -> io::Result<()> {
None => cmap.get(&default).unwrap(), None => cmap.get(&default).unwrap(),
}, },
None => cmap.get(&default).unwrap(), None => cmap.get(&default).unwrap(),
}; }.to_owned();
let con = conn::Connection { stream, peer_addr }; let con = conn::Connection { stream, peer_addr, srv };
handle_connection(con, srv).await?; let (con, url) = match get_request(con).await {
Ok((c, u)) => (c, u),
Err(_) => return Ok(()) as io::Result<()>,
};
handle_connection(con, url).await?;
Ok(()) as io::Result<()> Ok(()) as io::Result<()>
}; };