forked from cadey/maj
94 lines
2.6 KiB
Rust
94 lines
2.6 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.dangerous().set_certificate_verifier(Arc::new(NoCertificateVerification{}));
|
|
|
|
config
|
|
}
|
|
|
|
struct NoCertificateVerification {}
|
|
|
|
impl rustls::ServerCertVerifier for NoCertificateVerification {
|
|
fn verify_server_cert(&self,
|
|
_roots: &rustls::RootCertStore,
|
|
_presented_certs: &[rustls::Certificate],
|
|
_dns_name: webpki::DNSNameRef<'_>,
|
|
_ocsp: &[u8]) -> Result<rustls::ServerCertVerified, rustls::TLSError> {
|
|
Ok(rustls::ServerCertVerified::assertion())
|
|
}
|
|
}
|
|
|
|
#[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),
|
|
|
|
#[error("Invalid URL scheme {0:?}")]
|
|
InvalidScheme(String),
|
|
}
|
|
|
|
pub fn get<T>(u: T) -> Result<crate::Response, Error>
|
|
where
|
|
T: Into<String>,
|
|
{
|
|
let u = u.into();
|
|
let mut ur = Url::parse(&u.clone())?;
|
|
if ur.port().is_none() {
|
|
ur.set_port(Some(1965)).unwrap();
|
|
}
|
|
|
|
if ur.scheme() == "" {
|
|
let _ = ur.set_scheme("gemini");
|
|
}
|
|
|
|
if ur.scheme() != "gemini" {
|
|
return Err(Error::InvalidScheme(ur.scheme().to_string()));
|
|
}
|
|
|
|
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(())
|
|
}
|
|
}
|