aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/environment.rs9
-rw-r--r--src/http09.rs100
-rw-r--r--src/main.rs5
3 files changed, 114 insertions, 0 deletions
diff --git a/src/environment.rs b/src/environment.rs
index dd10171..bd99081 100644
--- a/src/environment.rs
+++ b/src/environment.rs
@@ -17,6 +17,8 @@ pub struct Environment {
pub proxy_by_default: bool,
pub keep_gemini: Option<Vec<String>>,
pub embed_images: Option<String>,
+ pub http09: bool,
+ pub http09_port: u16,
}
impl Environment {
@@ -51,6 +53,13 @@ impl Environment {
.ok()
.map(|s| s.split(',').map(String::from).collect()),
embed_images: std::env::var("EMBED_IMAGES").ok(),
+ http09: std::env::var("HTTP09")
+ .map(|v| v.to_lowercase() == "true")
+ .unwrap_or(false),
+ http09_port: std::env::var("HTTP09_PORT")
+ .ok()
+ .and_then(|p| p.parse().ok())
+ .unwrap_or(90),
}
}
}
diff --git a/src/http09.rs b/src/http09.rs
new file mode 100644
index 0000000..b18cd96
--- /dev/null
+++ b/src/http09.rs
@@ -0,0 +1,100 @@
+use {
+ crate::{environment::ENVIRONMENT, url::from_path},
+ tokio::{
+ io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
+ net::TcpListener,
+ },
+};
+
+pub async fn serve() {
+ let address = format!("0.0.0.0:{}", ENVIRONMENT.http09_port);
+ let listener = match TcpListener::bind(&address).await {
+ Ok(listener) => {
+ info!("HTTP/0.9 server listening on {address}");
+
+ listener
+ }
+ Err(error) => {
+ error!("failed to bind HTTP/0.9 server to {address}: {error}");
+
+ return;
+ }
+ };
+
+ loop {
+ let (stream, peer) = match listener.accept().await {
+ Ok(connection) => connection,
+ Err(error) => {
+ warn!("HTTP/0.9 accept error: {error}");
+
+ continue;
+ }
+ };
+
+ tokio::spawn(async move {
+ if let Err(error) = handle(stream).await {
+ warn!("HTTP/0.9 error from {peer}: {error}");
+ }
+ });
+ }
+}
+
+async fn handle(
+ stream: tokio::net::TcpStream,
+) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
+ let (reader, mut writer) = stream.into_split();
+ let mut reader = BufReader::new(reader);
+ let mut request_line = String::new();
+
+ reader.read_line(&mut request_line).await?;
+
+ let path = parse_request(&request_line)?;
+ let mut configuration = crate::response::configuration::Configuration::new();
+ let url = from_path(&path, false, &mut configuration)?;
+ let mut response = germ::request::request(&url).await?;
+
+ if *response.status() == germ::request::Status::PermanentRedirect
+ || *response.status() == germ::request::Status::TemporaryRedirect
+ {
+ let redirect = if response.meta().starts_with('/') {
+ format!(
+ "gemini://{}{}",
+ url.domain().unwrap_or_default(),
+ response.meta()
+ )
+ } else {
+ response.meta().to_string()
+ };
+
+ response = germ::request::request(&url::Url::parse(&redirect)?).await?;
+ }
+
+ if response.meta().starts_with("image/") {
+ if let Some(bytes) = response.content_bytes() {
+ writer.write_all(bytes).await?;
+ }
+ } else if let Some(content) = response.content() {
+ writer.write_all(content.as_bytes()).await?;
+ }
+
+ writer.shutdown().await?;
+
+ Ok(())
+}
+
+fn parse_request(
+ line: &str,
+) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
+ let line = line.trim();
+
+ line.strip_prefix("GET ").map_or_else(
+ || {
+ if line.starts_with('/') {
+ Ok(line.to_string())
+ } else {
+ Err(format!("invalid HTTP/0.9 request: {line}").into())
+ }
+ },
+ |path| Ok(path.split_whitespace().next().unwrap_or("/").to_string()),
+ )
+}
diff --git a/src/main.rs b/src/main.rs
index a39e83e..c0e500c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -12,6 +12,7 @@
mod environment;
mod html;
+mod http09;
mod response;
mod url;
@@ -29,6 +30,10 @@ async fn main() -> std::io::Result<()> {
)
.init();
+ if environment::ENVIRONMENT.http09 {
+ tokio::spawn(http09::serve());
+ }
+
actix_web::HttpServer::new(move || {
actix_web::App::new()
.default_service(web::get().to(default))