mirror of
https://github.com/YGGverse/ggemini.git
synced 2026-04-02 01:55:35 +00:00
update request api
This commit is contained in:
parent
52141f3dca
commit
5e52e74870
6 changed files with 37 additions and 78 deletions
|
|
@ -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)))),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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}")
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
)
|
data: Bytes::from(DATA),
|
||||||
.unwrap(),
|
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(),
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue