draft redirection referrer feature

This commit is contained in:
yggverse 2025-01-18 06:48:28 +02:00
parent 4665a7ff6a
commit 86a6ad058a
7 changed files with 135 additions and 49 deletions

View File

@ -302,12 +302,12 @@ impl Page {
}
},
Response::Redirect(this) => match this {
Redirect::Background { source, target } => todo!(), // @TODO
Redirect::Foreground { source, target } => navigation
Redirect::Background(request) => todo!(), // @TODO
Redirect::Foreground(request) => {navigation
.request
.widget
.entry
.set_text(&target.to_string()) // @TODO
.set_text(&request.uri().unwrap().to_string())} // @TODO handle
}
Response::TextGemini { base, source, is_source_request } => {
let widget = if is_source_request {

View File

@ -64,6 +64,7 @@ impl Driver {
/// Make new async `Feature` request
/// * return `Response` in callback function
pub fn request_async(&self, request: Request, callback: impl FnOnce(Response) + 'static) {
let referrer = request.to_referrer();
match request.feature {
Feature::Download(protocol) => match protocol {
Protocol::Gemini {
@ -73,20 +74,24 @@ impl Driver {
} => gemini::request_async(
&self.profile,
&self.gemini,
uri.clone(),
cancellable.clone(),
priority,
move |result| {
callback(match result {
Ok(response) => Response::Download {
base: uri,
stream: response.connection.stream(),
cancellable,
},
Err(e) => Response::Failure(Failure::Error {
message: e.to_string(),
}),
})
&uri,
&cancellable,
&priority,
{
let base = uri.clone();
let cancellable = cancellable.clone();
move |result| {
callback(match result {
Ok(response) => Response::Download {
base,
stream: response.connection.stream(),
cancellable,
},
Err(e) => Response::Failure(Failure::Error {
message: e.to_string(),
}),
})
}
},
),
_ => callback(Response::Failure(Failure::Error {
@ -101,17 +106,30 @@ impl Driver {
} => gemini::request_async(
&self.profile,
&self.gemini,
uri.clone(),
cancellable.clone(),
priority,
move |result| {
gemini::handle(result, uri, cancellable, priority, false, callback)
&uri,
&cancellable,
&priority,
{
let cancellable = cancellable.clone();
let uri = uri.clone();
move |result| {
gemini::handle(
result,
uri,
cancellable,
priority,
referrer,
false,
callback,
)
}
},
),
Protocol::Titan { .. } => todo!(),
Protocol::Unsupported => todo!(),
},
Feature::Source(protocol) => match protocol {
Feature::Source(ref protocol) => match protocol {
Protocol::Gemini {
uri,
cancellable,
@ -119,11 +137,24 @@ impl Driver {
} => gemini::request_async(
&self.profile,
&self.gemini,
uri.clone(),
cancellable.clone(),
uri,
cancellable,
priority,
move |result| {
gemini::handle(result, uri, cancellable, priority, true, callback)
{
let cancellable = cancellable.clone();
let priority = *priority;
let uri = uri.clone();
move |result| {
gemini::handle(
result,
uri,
cancellable,
priority,
request.referrer.to_vec(),
true,
callback,
)
}
},
),
_ => callback(Response::Failure(Failure::Error {

View File

@ -1,6 +1,6 @@
use super::{
response::{Certificate, Failure, Input, Redirect},
Profile, Response,
Profile, Request, Response,
};
use gtk::{
gio::Cancellable,
@ -12,16 +12,16 @@ use std::rc::Rc;
pub fn request_async(
profile: &Rc<Profile>,
client: &Rc<ggemini::Client>,
uri: Uri,
cancellable: Cancellable,
priority: Priority,
uri: &Uri,
cancellable: &Cancellable,
priority: &Priority,
callback: impl FnOnce(Result<ggemini::client::Response, ggemini::client::Error>) + 'static,
) {
let request = uri.to_string();
client.request_async(
ggemini::client::Request::gemini(uri),
priority,
cancellable,
ggemini::client::Request::gemini(uri.clone()),
priority.clone(),
cancellable.clone(),
// Search for user certificate match request
// * @TODO this feature does not support multi-protocol yet
match profile.identity.gemini.match_scope(&request) {
@ -42,6 +42,7 @@ pub fn handle(
base: Uri,
cancellable: Cancellable,
priority: Priority,
referrer: Vec<Request>,
is_source_request: bool, // @TODO yet partial implementation
callback: impl FnOnce(Response) + 'static,
) {
@ -68,8 +69,8 @@ pub fn handle(
Some(mime) => match mime.as_str() {
"text/gemini" => Text::from_stream_async(
response.connection.stream(),
priority,
cancellable,
priority.clone(),
cancellable.clone(),
move |result| match result {
Ok(text) => callback(Response::TextGemini {
base,
@ -100,10 +101,12 @@ pub fn handle(
// https://geminiprotocol.net/docs/protocol-specification.gmi#status-30-temporary-redirection
Status::Redirect => callback(match response.meta.data {
Some(data) => match Uri::parse_relative(&base, data.as_str(), UriFlags::NONE) {
Ok(target) => Response::Redirect(Redirect::Foreground {
source: base,
target,
}),
Ok(target) => Response::Redirect(Redirect::Foreground(Request::build(
&target.to_string(),
Some(referrer),
cancellable,
priority,
))),
Err(e) => Response::Failure(Failure::Error {
message: format!("Could not parse target address: {e}"),
}),
@ -115,10 +118,12 @@ pub fn handle(
// https://geminiprotocol.net/docs/protocol-specification.gmi#status-31-permanent-redirection
Status::PermanentRedirect => callback(match response.meta.data {
Some(data) => match Uri::parse_relative(&base, data.as_str(), UriFlags::NONE) {
Ok(target) => Response::Redirect(Redirect::Background {
source: base,
target,
}),
Ok(target) => Response::Redirect(Redirect::Background(Request::build(
&target.to_string(),
Some(referrer),
cancellable,
priority,
))),
Err(e) => Response::Failure(Failure::Error {
message: format!("Could not parse target address: {e}"),
}),

View File

@ -1,9 +1,13 @@
pub mod feature;
pub use feature::Feature;
use gtk::{gio::Cancellable, glib::Priority};
use gtk::{
gio::Cancellable,
glib::{Priority, Uri},
};
/// Request data wrapper for `Client`
#[derive(Clone)]
pub struct Request {
pub feature: Feature,
/// Requests chain in order to process redirection rules
@ -25,4 +29,17 @@ impl Request {
referrer: referrer.unwrap_or_default(),
}
}
// Getters
/// Copy `Self` to new `referrer` vector
pub fn to_referrer(&self) -> Vec<Request> {
let mut referrer = self.referrer.to_vec();
referrer.push(self.clone());
referrer
}
pub fn uri(&self) -> Option<&Uri> {
self.feature.uri()
}
}

View File

@ -1,9 +1,13 @@
pub mod protocol;
pub use protocol::Protocol;
use gtk::{gio::Cancellable, glib::Priority};
use gtk::{
gio::Cancellable,
glib::{Priority, Uri},
};
/// Feature wrapper for client `Request`
#[derive(Clone)]
pub enum Feature {
Default(Protocol),
Download(Protocol),
@ -26,4 +30,14 @@ impl Feature {
Self::Default(Protocol::build(query, cancellable, priority))
}
// Getters
pub fn uri(&self) -> Option<&Uri> {
match self {
Self::Default(protocol) | Self::Download(protocol) | Self::Source(protocol) => {
protocol.uri()
}
}
}
}

View File

@ -4,6 +4,7 @@ use gtk::{
glib::{Priority, Uri, UriFlags},
};
#[derive(Clone)]
pub enum Protocol {
Gemini {
uri: Uri,
@ -55,4 +56,22 @@ impl Protocol {
},
}
}
// Getters
pub fn uri(&self) -> Option<&Uri> {
match self {
Self::Gemini {
uri,
cancellable: _,
priority: _,
}
| Self::Titan {
uri,
cancellable: _,
priority: _,
} => Some(&uri),
Self::Unsupported => None,
}
}
}

View File

@ -1,6 +1,6 @@
use gtk::glib::Uri;
use crate::app::browser::window::tab::item::page::client::Request;
pub enum Redirect {
Foreground { source: Uri, target: Uri },
Background { source: Uri, target: Uri },
Foreground(Request),
Background(Request),
}