Compare commits

..

No commits in common. "main" and "0.18.0" have entirely different histories.
main ... 0.18.0

8 changed files with 34 additions and 78 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "ggemini" name = "ggemini"
version = "0.20.1" version = "0.18.0"
edition = "2024" edition = "2024"
license = "MIT" license = "MIT"
readme = "README.md" readme = "README.md"
@ -11,10 +11,10 @@ repository = "https://github.com/YGGverse/ggemini"
[dependencies.gio] [dependencies.gio]
package = "gio" package = "gio"
version = "0.21.0" version = "0.20.9"
features = ["v2_70"] features = ["v2_70"]
[dependencies.glib] [dependencies.glib]
package = "glib" package = "glib"
version = "0.21.0" version = "0.20.9"
features = ["v2_66"] features = ["v2_66"]

View file

@ -55,8 +55,7 @@ fn main() -> ExitCode {
}, },
Priority::DEFAULT, Priority::DEFAULT,
Cancellable::new(), Cancellable::new(),
None, // optional auth `GTlsCertificate` None, // optional `GTlsCertificate`
None, // optional TOFU `GTlsCertificate` array
|result| match result { |result| match result {
Ok((response, _connection)) => match response { Ok((response, _connection)) => match response {
Response::Success(success) => match success.mime().unwrap().as_str() { Response::Success(success) => match success.mime().unwrap().as_str() {

View file

@ -59,8 +59,7 @@ impl Client {
request: Request, request: Request,
priority: Priority, priority: Priority,
cancellable: Cancellable, cancellable: Cancellable,
client_certificate: Option<TlsCertificate>, certificate: Option<TlsCertificate>,
server_certificates: Option<Vec<TlsCertificate>>,
callback: impl FnOnce(Result<(Response, Connection), Error>) + 'static, callback: impl FnOnce(Result<(Response, Connection), Error>) + 'static,
) { ) {
// Begin new connection // Begin new connection
@ -74,33 +73,30 @@ impl Client {
move |result| match result { move |result| match result {
Ok(socket_connection) => { Ok(socket_connection) => {
match Connection::build( match Connection::build(
socket_connection.clone(), socket_connection,
network_address, network_address,
client_certificate, certificate,
server_certificates,
is_session_resumption, is_session_resumption,
) { ) {
Ok(connection) => connection.clone().request_async( Ok(connection) => connection.request_async(
request, request,
priority, priority,
cancellable, cancellable,
move |result| { move |result| {
callback(match result { callback(match result {
Ok(response) => Ok(response), Ok(response) => Ok(response),
Err(e) => Err(Error::Request(connection, e)), Err(e) => Err(Error::Connection(e)),
}) })
}, },
), ),
Err(e) => { Err(e) => callback(Err(Error::Connection(e))),
callback(Err(Error::Connection(socket_connection, e)))
} }
} }
} Err(e) => callback(Err(Error::Connect(e))),
Err(e) => callback(Err(Error::Connect(network_address, e))),
} }
}) })
} }
Err(e) => callback(Err(Error::NetworkAddress(e))), Err(e) => callback(Err(Error::Request(e))),
} }
} }

View file

@ -6,16 +6,17 @@ pub use error::Error;
pub use request::{Mode, Request}; pub use request::{Mode, Request};
pub use response::Response; pub use response::Response;
// Local dependencies
use gio::{ use gio::{
Cancellable, IOStream, NetworkAddress, SocketConnection, TlsCertificate, TlsClientConnection, Cancellable, IOStream, NetworkAddress, SocketConnection, TlsCertificate, TlsClientConnection,
prelude::{IOStreamExt, OutputStreamExtManual, TlsCertificateExt, TlsConnectionExt}, prelude::{IOStreamExt, OutputStreamExtManual, TlsConnectionExt},
}; };
use glib::{ use glib::{
Bytes, Priority, Bytes, Priority,
object::{Cast, ObjectExt}, object::{Cast, ObjectExt},
}; };
#[derive(Debug, Clone)]
pub struct Connection { pub struct Connection {
pub network_address: NetworkAddress, pub network_address: NetworkAddress,
pub socket_connection: SocketConnection, pub socket_connection: SocketConnection,
@ -29,19 +30,17 @@ impl Connection {
pub fn build( pub fn build(
socket_connection: SocketConnection, socket_connection: SocketConnection,
network_address: NetworkAddress, network_address: NetworkAddress,
client_certificate: Option<TlsCertificate>, certificate: Option<TlsCertificate>,
server_certificates: Option<Vec<TlsCertificate>>,
is_session_resumption: bool, is_session_resumption: bool,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
Ok(Self { Ok(Self {
tls_client_connection: match new_tls_client_connection( tls_client_connection: match new_tls_client_connection(
&socket_connection, &socket_connection,
Some(&network_address), Some(&network_address),
server_certificates,
is_session_resumption, is_session_resumption,
) { ) {
Ok(tls_client_connection) => { Ok(tls_client_connection) => {
if let Some(ref c) = client_certificate { if let Some(ref c) = certificate {
tls_client_connection.set_certificate(c); tls_client_connection.set_certificate(c);
} }
tls_client_connection tls_client_connection
@ -137,7 +136,6 @@ impl Connection {
fn new_tls_client_connection( fn new_tls_client_connection(
socket_connection: &SocketConnection, socket_connection: &SocketConnection,
server_identity: Option<&NetworkAddress>, server_identity: Option<&NetworkAddress>,
server_certificates: Option<Vec<TlsCertificate>>,
is_session_resumption: bool, is_session_resumption: bool,
) -> Result<TlsClientConnection, Error> { ) -> Result<TlsClientConnection, Error> {
match TlsClientConnection::new(socket_connection, server_identity) { match TlsClientConnection::new(socket_connection, server_identity) {
@ -151,19 +149,9 @@ fn new_tls_client_connection(
// https://geminiprotocol.net/docs/protocol-specification.gmi#closing-connections // https://geminiprotocol.net/docs/protocol-specification.gmi#closing-connections
tls_client_connection.set_require_close_notify(true); tls_client_connection.set_require_close_notify(true);
// [TOFU](https://geminiprotocol.net/docs/protocol-specification.gmi#tls-server-certificate-validation) // @TODO validate
tls_client_connection.connect_accept_certificate(move |_, c, _| { // https://geminiprotocol.net/docs/protocol-specification.gmi#tls-server-certificate-validation
server_certificates tls_client_connection.connect_accept_certificate(|_, _, _| true);
.as_ref()
.is_none_or(|server_certificates| {
for server_certificate in server_certificates {
if server_certificate.is_same(c) {
return true;
}
}
false
})
});
Ok(tls_client_connection) Ok(tls_client_connection)
} }

View file

@ -2,30 +2,23 @@ use std::fmt::{Display, Formatter, Result};
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
Connect(gio::NetworkAddress, glib::Error), Connect(glib::Error),
Connection(gio::SocketConnection, crate::client::connection::Error), Connection(crate::client::connection::Error),
NetworkAddress(crate::client::connection::request::Error), Request(crate::client::connection::request::Error),
Request(
crate::client::connection::Connection,
crate::client::connection::Error,
),
} }
impl Display for Error { impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> Result { fn fmt(&self, f: &mut Formatter) -> Result {
match self { match self {
Self::Connect(_, e) => { Self::Connection(e) => {
write!(f, "Connect error: {e}")
}
Self::Connection(_, e) => {
write!(f, "Connection init error: {e}")
}
Self::NetworkAddress(e) => {
write!(f, "Network address error: {e}")
}
Self::Request(_, e) => {
write!(f, "Connection error: {e}") write!(f, "Connection error: {e}")
} }
Self::Connect(e) => {
write!(f, "Connect error: {e}")
}
Self::Request(e) => {
write!(f, "Request error: {e}")
}
} }
} }
} }

View file

@ -33,13 +33,13 @@ pub fn from_stream_async(
size.total += bytes.len(); size.total += bytes.len();
on_chunk(bytes.clone(), size.total); on_chunk(bytes.clone(), size.total);
if let Some(limit) = size.limit if let Some(limit) = size.limit {
&& size.total > limit if size.total > limit {
{
return on_complete(Err(Error::BytesTotal(size.total, limit))); return on_complete(Err(Error::BytesTotal(size.total, limit)));
} }
}
if bytes.is_empty() { if bytes.len() == 0 {
return on_complete(Ok((file_output_stream, size.total))); return on_complete(Ok((file_output_stream, size.total)));
} }

View file

@ -5,13 +5,3 @@ pub struct Size {
pub limit: Option<usize>, pub limit: Option<usize>,
pub total: usize, pub total: usize,
} }
impl Default for Size {
fn default() -> Self {
Self {
chunk: 0x10000, // 64KB
limit: None,
total: 0,
}
}
}

View file

@ -4,13 +4,3 @@ pub struct Size {
pub limit: usize, pub limit: usize,
pub total: usize, pub total: usize,
} }
impl Default for Size {
fn default() -> Self {
Self {
chunk: 0x10000, // 64KB
limit: 0xfffff, // 1 MB
total: 0,
}
}
}