maj/src/client.rs

67 lines
1.8 KiB
Rust

use crate::{Response};
use rustls::{ClientConfig, ClientSession, Stream, TLSError};
use std::{io::prelude::*, net::TcpStream, sync::Arc};
use url::Url;
fn config() -> ClientConfig {
let mut config = ClientConfig::new();
config
.root_store
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
config
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("TLS error: {0:?}")]
TLS(#[from] TLSError),
#[error("URL error: {0:?}")]
URL(#[from] url::ParseError),
#[error("Invalid DNS name: {0:?}")]
InvalidDNSName(#[from] webpki::InvalidDNSNameError),
#[error("IO error: {0:?}")]
IO(#[from] std::io::Error),
#[error("Response parsing error: {0:?}")]
ResponseParse(#[from] crate::ResponseError),
}
pub fn get(u: String) -> Result<crate::Response, Error> {
let mut ur = Url::parse(&u.clone())?;
if ur.port().is_none() {
ur.set_port(Some(1965)).unwrap();
}
let cfg = Arc::new(config());
let host = ur.host_str().unwrap();
let mut sock = TcpStream::connect(&format!("{}:{}", host, ur.port().unwrap()))?;
let name_ref = webpki::DNSNameRef::try_from_ascii_str(host)?;
let mut client = ClientSession::new(&cfg, name_ref);
let mut tls = Stream::new(&mut client, &mut sock);
let req = format!("{}\r\n", u);
log::trace!("writing request {:?}", req);
tls.write(req.as_bytes())?;
Ok(Response::parse(&mut tls)?)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::*;
#[test]
fn gemini_homepage() -> Result<(), Error> {
let _ = pretty_env_logger::try_init();
let resp = get("gemini://gemini.circumlunar.space/".to_string())?;
assert_eq!(resp.status, StatusCode::Success);
assert_eq!(resp.meta, "text/gemini");
assert_ne!(resp.body.len(), 0);
Ok(())
}
}