Compare commits
No commits in common. "1395bb04fff95dffdd60670ea060223da9602eda" and "053f042e00538aabf24a1e85d46e8ed2e8ae9d92" have entirely different histories.
1395bb04ff
...
053f042e00
6 changed files with 31 additions and 110 deletions
|
@ -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"
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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())),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,44 +85,39 @@ 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?;
|
let status = Status::parse_status(&buf)?;
|
||||||
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 mut buf: [u8; 1] = [0]; // buffer for LF (\n)
|
||||||
|
|
||||||
let message = {
|
// reading message after status code
|
||||||
let mut result: Vec<u8> = Vec::new();
|
// until CRLF (\r\n)
|
||||||
let mut buf = [0u8]; // buffer for LF (\n)
|
loop {
|
||||||
|
// until CR
|
||||||
// reading message after status code
|
buf_reader.read_until(b'\r', &mut message).await?;
|
||||||
// until CRLF (\r\n)
|
// now read next char...
|
||||||
loop {
|
buf_reader.read_exact(&mut buf).await?;
|
||||||
// until CR
|
if buf[0] == b'\n' {
|
||||||
stream.read_until(b'\r', &mut result).await?;
|
// ...and check if it's LF
|
||||||
// now read next char...
|
break;
|
||||||
stream.read_exact(&mut buf).await?;
|
} else {
|
||||||
if buf[0] == b'\n' {
|
// ...otherwise, CR is a part of message, not a CRLF terminator,
|
||||||
// ...and check if it's LF
|
// so append that one byte that's supposed to be LF (but not LF)
|
||||||
break;
|
// to the message buffer
|
||||||
} else {
|
message.push(buf[0].into());
|
||||||
// ...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)
|
|
||||||
// to the message buffer
|
|
||||||
result.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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue