update request api

This commit is contained in:
yggverse 2025-01-22 15:14:59 +02:00
parent 52141f3dca
commit 5e52e74870
6 changed files with 37 additions and 78 deletions

View file

@ -6,13 +6,15 @@ pub use error::Error;
pub use request::{Gemini, Request, Titan}; pub use request::{Gemini, Request, Titan};
pub use response::Response; pub use response::Response;
// Local dependencies
use gio::{ use gio::{
prelude::{IOStreamExt, OutputStreamExt, TlsConnectionExt}, prelude::{IOStreamExt, OutputStreamExtManual, TlsConnectionExt},
Cancellable, IOStream, NetworkAddress, SocketConnection, TlsCertificate, TlsClientConnection, Cancellable, IOStream, NetworkAddress, SocketConnection, TlsCertificate, TlsClientConnection,
}; };
use glib::{ use glib::{
object::{Cast, ObjectExt}, object::{Cast, ObjectExt},
Bytes, Priority, Priority,
}; };
pub struct Connection { pub struct Connection {
@ -58,24 +60,8 @@ impl Connection {
cancellable: Cancellable, cancellable: Cancellable,
callback: impl FnOnce(Result<Response, Error>) + 'static, callback: impl FnOnce(Result<Response, Error>) + 'static,
) { ) {
self.bytes_request_async(&request.to_bytes(), priority, cancellable, callback); self.stream().output_stream().write_async(
} request.header().into_bytes(),
/// Low-level shared method to send raw bytes array over
/// [Gemini](https://geminiprotocol.net/docs/protocol-specification.gmi) or
/// [Titan](gemini://transjovian.org/titan/page/The%20Titan%20Specification) protocol
/// * bytes array should include formatted header according to protocol selected
/// * for high-level requests see `gemini_request_async` and `titan_request_async` methods
/// * to construct multi-protocol request with single function, use `request_async` method
pub fn bytes_request_async(
self,
request: &Bytes,
priority: Priority,
cancellable: Cancellable,
callback: impl FnOnce(Result<Response, Error>) + 'static,
) {
self.stream().output_stream().write_bytes_async(
request,
priority, priority,
Some(&cancellable.clone()), Some(&cancellable.clone()),
move |result| match result { move |result| match result {
@ -88,7 +74,7 @@ impl Connection {
}) })
}) })
} }
Err(e) => callback(Err(Error::Stream(e))), Err((b, e)) => callback(Err(Error::Request((b, e)))),
}, },
); );
} }

View file

@ -2,16 +2,16 @@ use std::fmt::{Display, Formatter, Result};
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
Request((Vec<u8>, glib::Error)),
Response(crate::client::connection::response::Error), Response(crate::client::connection::response::Error),
Stream(glib::Error),
TlsClientConnection(glib::Error), TlsClientConnection(glib::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::Stream(e) => { Self::Request((_, e)) => {
write!(f, "TLS client connection error: {e}") write!(f, "Request error: {e}")
} }
Self::Response(e) => { Self::Response(e) => {
write!(f, "Response error: {e}") write!(f, "Response error: {e}")

View file

@ -7,7 +7,6 @@ pub use gemini::Gemini;
pub use titan::Titan; pub use titan::Titan;
use gio::NetworkAddress; use gio::NetworkAddress;
use glib::{Bytes, Uri};
/// Single `Request` implementation for different protocols /// Single `Request` implementation for different protocols
pub enum Request { pub enum Request {
@ -16,30 +15,13 @@ pub enum Request {
} }
impl Request { impl Request {
// Constructors
/// Create new `Self` for [Gemini protocol](https://geminiprotocol.net)
pub fn gemini(uri: Uri) -> Self {
Self::Gemini(Gemini { uri })
}
/// Create new `Self` for [Titan protocol](gemini://transjovian.org/titan/page/The%20Titan%20Specification)
pub fn titan(uri: Uri, data: Vec<u8>, mime: Option<String>, token: Option<String>) -> Self {
Self::Titan(Titan {
uri,
data,
mime,
token,
})
}
// Getters // Getters
/// Get [Bytes](https://docs.gtk.org/glib/struct.Bytes.html) for `Self` /// Get header string for `Self`
pub fn to_bytes(&self) -> Bytes { pub fn header(&self) -> String {
match self { match self {
Self::Gemini(ref request) => request.to_bytes(), Self::Gemini(ref this) => this.header(),
Self::Titan(ref request) => request.to_bytes(), Self::Titan(ref this) => this.header(),
} }
} }

View file

@ -1,4 +1,4 @@
use glib::{Bytes, Uri}; use glib::Uri;
/// [Gemini](https://geminiprotocol.net/docs/protocol-specification.gmi) protocol enum object for `Request` /// [Gemini](https://geminiprotocol.net/docs/protocol-specification.gmi) protocol enum object for `Request`
pub struct Gemini { pub struct Gemini {
@ -8,8 +8,8 @@ pub struct Gemini {
impl Gemini { impl Gemini {
// Getters // Getters
/// Copy `Self` to [Bytes](https://docs.gtk.org/glib/struct.Bytes.html) /// Get header string for `Self`
pub fn to_bytes(&self) -> Bytes { pub fn header(&self) -> String {
Bytes::from(format!("{}\r\n", self.uri).as_bytes()) format!("{}\r\n", self.uri)
} }
} }

View file

@ -3,7 +3,7 @@ use glib::{Bytes, Uri, UriHideFlags};
/// [Titan](gemini://transjovian.org/titan/page/The%20Titan%20Specification) protocol enum object for `Request` /// [Titan](gemini://transjovian.org/titan/page/The%20Titan%20Specification) protocol enum object for `Request`
pub struct Titan { pub struct Titan {
pub uri: Uri, pub uri: Uri,
pub data: Vec<u8>, pub data: Bytes,
pub mime: Option<String>, pub mime: Option<String>,
pub token: Option<String>, pub token: Option<String>,
} }
@ -11,8 +11,8 @@ pub struct Titan {
impl Titan { impl Titan {
// Getters // Getters
/// Copy `Self` to [Bytes](https://docs.gtk.org/glib/struct.Bytes.html) /// Get header string for `Self`
pub fn to_bytes(&self) -> Bytes { pub fn header(&self) -> String {
// Calculate data size // Calculate data size
let size = self.data.len(); let size = self.data.len();
@ -31,13 +31,6 @@ impl Titan {
header.push_str(&format!("?{query}")); header.push_str(&format!("?{query}"));
} }
header.push_str("\r\n"); header.push_str("\r\n");
header
// Build request
let mut bytes: Vec<u8> = Vec::with_capacity(size + 1024); // @TODO
bytes.extend(header.into_bytes());
bytes.extend(&self.data);
// Wrap result
Bytes::from(&bytes)
} }
} }

View file

@ -7,10 +7,10 @@ use ggemini::client::connection::Request;
fn client_connection_request_gemini() { fn client_connection_request_gemini() {
const REQUEST: &str = "gemini://geminiprotocol.net/"; const REQUEST: &str = "gemini://geminiprotocol.net/";
assert_eq!( assert_eq!(
std::str::from_utf8( Request::Gemini(ggemini::client::connection::Gemini {
&Request::gemini(Uri::parse(REQUEST, UriFlags::NONE).unwrap()).to_bytes() uri: Uri::parse(REQUEST, UriFlags::NONE).unwrap()
) })
.unwrap(), .header(),
format!("{REQUEST}\r\n") format!("{REQUEST}\r\n")
); );
} }
@ -20,23 +20,21 @@ fn client_connection_request_titan() {
const DATA: &[u8] = &[1, 2, 3]; const DATA: &[u8] = &[1, 2, 3];
const MIME: &str = "plain/text"; const MIME: &str = "plain/text";
const TOKEN: &str = "token"; const TOKEN: &str = "token";
const ARGUMENT: &str = "argument";
const REQUEST: &str = "titan://geminiprotocol.net/raw/Test";
assert_eq!( assert_eq!(
std::str::from_utf8( Request::Titan(ggemini::client::connection::Titan {
&Request::titan( uri: Uri::parse(
Uri::parse(&format!("{REQUEST}?argument={ARGUMENT}"), UriFlags::NONE).unwrap(), "titan://geminiprotocol.net/raw/Test?key=value",
DATA.to_vec(), UriFlags::NONE
Some(MIME.to_string()),
Some(TOKEN.to_string()),
)
.to_bytes()
) )
.unwrap(), .unwrap(),
data: Bytes::from(DATA),
mime: Some(MIME.to_string()),
token: Some(TOKEN.to_string())
})
.header(),
format!( format!(
"{REQUEST};size={};mime={MIME};token={TOKEN}?argument={ARGUMENT}\r\n{}", "titan://geminiprotocol.net/raw/Test;size={};mime={MIME};token={TOKEN}?key=value\r\n",
DATA.len(), DATA.len(),
std::str::from_utf8(DATA).unwrap(),
) )
); );
} }