Compare commits

..

No commits in common. "1395bb04fff95dffdd60670ea060223da9602eda" and "053f042e00538aabf24a1e85d46e8ed2e8ae9d92" have entirely different histories.

6 changed files with 31 additions and 110 deletions

View file

@ -20,10 +20,6 @@ tokio-rustls = { version = "0.26.0", default-features = false, features = ["ring
url = "2.5.2" url = "2.5.2"
webpki-roots = "0.26.3" webpki-roots = "0.26.3"
[[example]]
name = "simple"
path = "examples/simple.rs"
[[example]] [[example]]
name = "main" name = "main"
path = "examples/main.rs" path = "examples/main.rs"

View file

@ -1,54 +0,0 @@
use tokio_gemini::{
certs::{
fingerprint::{generate_fingerprint, Algorithm},
verifier::SelfsignedCertVerifier,
},
Client, LibError,
};
// Much simpler than examples/main.rs
// Hardcoded URL, no cert check, always write to stdout
//
// cargo add tokio-gemini
// cargo add tokio -F macros,rt-multi-thread
//
const URL: &str = "gemini://geminiprotocol.net/docs/protocol-specification.gmi";
#[tokio::main]
async fn main() -> Result<(), LibError> {
let client = Client::builder()
.with_selfsigned_cert_verifier(CertVerifier)
.build();
match client.request(URL).await?.ensure_ok() {
Ok(mut resp) => {
println!("{}", resp.text().await?);
}
Err(resp) => {
println!("{} {}", resp.status().num(), resp.message());
}
}
Ok(())
}
struct CertVerifier;
impl SelfsignedCertVerifier for CertVerifier {
fn verify(
&self,
cert: &tokio_gemini::certs::verifier::CertificateDer,
host: &str,
_now: tokio_gemini::certs::verifier::UnixTime,
) -> Result<bool, tokio_rustls::rustls::Error> {
// For real verification example with known_hosts file
// see examples/main.rs
eprintln!(
"Host = {}\nFingerprint = {}",
host,
generate_fingerprint(cert, Algorithm::Sha512),
);
Ok(true)
}
}

View file

@ -9,11 +9,7 @@ pub struct AllowAllCertVerifier(std::sync::Arc<CryptoProvider>);
impl AllowAllCertVerifier { impl AllowAllCertVerifier {
pub fn yes_i_know_what_i_am_doing() -> Self { pub fn yes_i_know_what_i_am_doing() -> Self {
AllowAllCertVerifier( AllowAllCertVerifier(CryptoProvider::get_default().unwrap().clone())
CryptoProvider::get_default()
.map(|c| c.clone())
.unwrap_or_else(|| std::sync::Arc::new(rustls::crypto::ring::default_provider())),
)
} }
} }

View file

@ -85,25 +85,21 @@ impl Client {
stream.write_all(url_str.as_bytes()).await?; stream.write_all(url_str.as_bytes()).await?;
stream.write_all(b"\r\n").await?; stream.write_all(b"\r\n").await?;
let status = {
let mut buf: [u8; 3] = [0, 0, 0]; // 2 digits, space let mut buf: [u8; 3] = [0, 0, 0]; // 2 digits, space
stream.read_exact(&mut buf).await?; stream.read_exact(&mut buf).await?;
Status::parse_status(&buf)? let status = Status::parse_status(&buf)?;
};
let mut stream = tokio::io::BufReader::new(stream); let mut message: Vec<u8> = Vec::new();
let mut buf_reader = tokio::io::BufReader::new(&mut stream);
let message = { let mut buf: [u8; 1] = [0]; // buffer for LF (\n)
let mut result: Vec<u8> = Vec::new();
let mut buf = [0u8]; // buffer for LF (\n)
// reading message after status code // reading message after status code
// until CRLF (\r\n) // until CRLF (\r\n)
loop { loop {
// until CR // until CR
stream.read_until(b'\r', &mut result).await?; buf_reader.read_until(b'\r', &mut message).await?;
// now read next char... // now read next char...
stream.read_exact(&mut buf).await?; buf_reader.read_exact(&mut buf).await?;
if buf[0] == b'\n' { if buf[0] == b'\n' {
// ...and check if it's LF // ...and check if it's LF
break; break;
@ -111,18 +107,17 @@ impl Client {
// ...otherwise, CR is a part of message, not a CRLF terminator, // ...otherwise, CR is a part of message, not a CRLF terminator,
// so append that one byte that's supposed to be LF (but not LF) // so append that one byte that's supposed to be LF (but not LF)
// to the message buffer // to the message buffer
result.push(buf[0].into()); message.push(buf[0].into());
} }
} }
// trim last CR // trim last CR
if result.last().is_some_and(|c| c == &b'\r') { if message.last().is_some_and(|c| c == &b'\r') {
result.pop(); message.pop();
} }
// Vec<u8> -> ASCII or UTF-8 String // Vec<u8> -> ASCII or UTF-8 String
String::from_utf8(result)? let message = String::from_utf8(message)?;
};
Ok(Response::new(status, message, stream)) Ok(Response::new(status, message, stream))
} }

View file

@ -1,9 +1,9 @@
use crate::{status::Status, LibError, ReplyType}; use crate::{status::Status, LibError};
use bytes::Bytes; use bytes::Bytes;
use tokio::io::AsyncReadExt; use tokio::io::AsyncReadExt;
type BodyStream = tokio::io::BufReader<tokio_rustls::client::TlsStream<tokio::net::TcpStream>>; type BodyStream = tokio_rustls::client::TlsStream<tokio::net::TcpStream>;
#[derive(Debug)] #[derive(Debug)]
pub struct Response { pub struct Response {
@ -21,14 +21,6 @@ impl Response {
} }
} }
pub fn ensure_ok(self: Self) -> Result<Self, Self> {
if self.status.reply_type() == ReplyType::Success {
Ok(self)
} else {
Err(self)
}
}
pub fn status(self: &Self) -> Status { pub fn status(self: &Self) -> Status {
self.status self.status
} }

View file

@ -81,10 +81,6 @@ impl Status {
self.status_code self.status_code
} }
pub fn num(self: &Self) -> u8 {
self.status_code.into()
}
pub fn reply_type(self: &Self) -> ReplyType { pub fn reply_type(self: &Self) -> ReplyType {
self.reply_type self.reply_type
} }