Add basic URI parsing

It works with Lagrange, but gemget still causes the server to throw
"Operation would block"
This commit is contained in:
Skylar Hill 2024-06-23 03:02:27 -05:00
parent 591090c980
commit 0c46f27f89
3 changed files with 46 additions and 19 deletions

View file

@ -18,8 +18,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = rustls::ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certs, private_key)?;
let listener = TcpListener::bind(format!("[::]:{}", 1965)).unwrap();
let srv = server::Server {
listener: TcpListener::bind(format!("[::]:{}", 1965)).unwrap(),
listener,
tls_config: config,
};
match srv.listen() {

View file

@ -81,21 +81,39 @@ pub struct Request {
#[derive(Debug)]
pub enum RequestParseError {
NotTerminated,
Error(Box<dyn std::error::Error>),
UserInfoGiven,
TooLarge,
FragmentGiven,
BadScheme,
BadUrl(url::ParseError),
}
impl From<url::ParseError> for RequestParseError {
fn from(value: url::ParseError) -> Self {
RequestParseError::BadUrl(value)
}
}
impl From<std::io::Error> for RequestParseError {
fn from(value: std::io::Error) -> Self {
RequestParseError::Error(Box::new(value))
}
}
impl std::fmt::Display for RequestParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RequestParseError::NotTerminated => write!(f, "Request not terminated with CRLF"),
RequestParseError::Error(e) => write!(f, "Request read error: {}", e),
RequestParseError::UserInfoGiven => write!(f, "URI userinfo not permitted"),
RequestParseError::TooLarge => write!(f, "URI larger than permitted 1024 bytes"),
RequestParseError::FragmentGiven => write!(f, "URI fragments not permitted"),
RequestParseError::BadScheme => {
write!(f, "URI schemes besides 'gemini' not supported")
}
RequestParseError::BadUrl(e) => e.fmt(f),
}
}
}
@ -200,12 +218,3 @@ impl From<RequestParseError> for ErrorResponse {
}
}
}
impl From<url::ParseError> for ErrorResponse {
fn from(value: url::ParseError) -> Self {
ErrorResponse {
code: ErrorStatus::BadRequest,
error: value.to_string(),
}
}
}

View file

@ -1,6 +1,6 @@
use crate::protocol::{self, Response};
use crate::protocol::*;
use mime::Mime;
use std::io::{Read, Write};
use std::io::{self, BufRead, Read, Write};
use std::{net, str::FromStr, sync::Arc};
use url::Url;
@ -12,24 +12,41 @@ pub struct Server {
impl Server {
pub fn listen(&self) -> Result<(), Box<dyn std::error::Error>> {
let (mut stream, _) = self.listener.accept().expect("15");
let mut conn =
rustls::ServerConnection::new(Arc::new(self.tls_config.clone())).expect("17");
let (mut stream, _) = self.listener.accept()?;
let mut conn = rustls::ServerConnection::new(Arc::new(self.tls_config.clone()))?;
let response = protocol::Request::try_from(Url::from_str("gemini://localhost")?)?.process();
conn.complete_io(&mut stream)?;
let response = read_request(conn.reader())?.process();
conn.writer()
.write_all(response.serialize().as_slice())
.expect("23");
conn.complete_io(&mut stream).expect("24");
conn.complete_io(&mut stream)?;
Ok(())
}
}
impl protocol::Request {
fn read_request(stream: rustls::Reader) -> Result<Request, RequestParseError> {
let mut reader = io::BufReader::new(stream);
let mut buf = String::new();
// The URI can be 1024 bytes long, plus \r\n
if reader.read_line(&mut buf)? > 1026 {
return Err(RequestParseError::TooLarge);
}
match buf.char_indices().rev().nth(1) {
Some((i, _)) if &buf[i..] != "\r\n" => Err(RequestParseError::NotTerminated),
None => Err(RequestParseError::NotTerminated),
Some(_) => Ok(()),
}?;
let url = Url::from_str(buf.trim())?;
Request::try_from(url)
}
impl Request {
fn process(&self) -> impl Response {
protocol::SuccessResponse {
SuccessResponse {
mime: Mime::from_str("text/gemini").expect("Hardcoded mime value failed??"),
body: b"Hiiiiii im gay :>".to_vec(),
}