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 { Response::Redirect(this) => match this {
Redirect::Background { source, target } => todo!(), // @TODO Redirect::Background(request) => todo!(), // @TODO
Redirect::Foreground { source, target } => navigation Redirect::Foreground(request) => {navigation
.request .request
.widget .widget
.entry .entry
.set_text(&target.to_string()) // @TODO .set_text(&request.uri().unwrap().to_string())} // @TODO handle
} }
Response::TextGemini { base, source, is_source_request } => { Response::TextGemini { base, source, is_source_request } => {
let widget = if is_source_request { let widget = if is_source_request {

View File

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

View File

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

View File

@ -1,9 +1,13 @@
pub mod feature; pub mod feature;
pub use feature::Feature; pub use feature::Feature;
use gtk::{gio::Cancellable, glib::Priority}; use gtk::{
gio::Cancellable,
glib::{Priority, Uri},
};
/// Request data wrapper for `Client` /// Request data wrapper for `Client`
#[derive(Clone)]
pub struct Request { pub struct Request {
pub feature: Feature, pub feature: Feature,
/// Requests chain in order to process redirection rules /// Requests chain in order to process redirection rules
@ -25,4 +29,17 @@ impl Request {
referrer: referrer.unwrap_or_default(), 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 mod protocol;
pub use protocol::Protocol; pub use protocol::Protocol;
use gtk::{gio::Cancellable, glib::Priority}; use gtk::{
gio::Cancellable,
glib::{Priority, Uri},
};
/// Feature wrapper for client `Request` /// Feature wrapper for client `Request`
#[derive(Clone)]
pub enum Feature { pub enum Feature {
Default(Protocol), Default(Protocol),
Download(Protocol), Download(Protocol),
@ -26,4 +30,14 @@ impl Feature {
Self::Default(Protocol::build(query, cancellable, priority)) 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}, glib::{Priority, Uri, UriFlags},
}; };
#[derive(Clone)]
pub enum Protocol { pub enum Protocol {
Gemini { Gemini {
uri: Uri, 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 { pub enum Redirect {
Foreground { source: Uri, target: Uri }, Foreground(Request),
Background { source: Uri, target: Uri }, Background(Request),
} }