From 86a6ad058aad727fbba18e6c8104cf96046f68ba Mon Sep 17 00:00:00 2001 From: yggverse Date: Sat, 18 Jan 2025 06:48:28 +0200 Subject: [PATCH] draft redirection referrer feature --- src/app/browser/window/tab/item/page.rs | 6 +- .../window/tab/item/page/client/driver.rs | 79 +++++++++++++------ .../tab/item/page/client/driver/gemini.rs | 39 +++++---- .../window/tab/item/page/client/request.rs | 19 ++++- .../tab/item/page/client/request/feature.rs | 16 +++- .../page/client/request/feature/protocol.rs | 19 +++++ .../tab/item/page/client/response/redirect.rs | 6 +- 7 files changed, 135 insertions(+), 49 deletions(-) diff --git a/src/app/browser/window/tab/item/page.rs b/src/app/browser/window/tab/item/page.rs index c750638d..29d320c6 100644 --- a/src/app/browser/window/tab/item/page.rs +++ b/src/app/browser/window/tab/item/page.rs @@ -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 { diff --git a/src/app/browser/window/tab/item/page/client/driver.rs b/src/app/browser/window/tab/item/page/client/driver.rs index 97c1ad95..afcbd257 100644 --- a/src/app/browser/window/tab/item/page/client/driver.rs +++ b/src/app/browser/window/tab/item/page/client/driver.rs @@ -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 { diff --git a/src/app/browser/window/tab/item/page/client/driver/gemini.rs b/src/app/browser/window/tab/item/page/client/driver/gemini.rs index 772eafd9..404983cd 100644 --- a/src/app/browser/window/tab/item/page/client/driver/gemini.rs +++ b/src/app/browser/window/tab/item/page/client/driver/gemini.rs @@ -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, client: &Rc, - uri: Uri, - cancellable: Cancellable, - priority: Priority, + uri: &Uri, + cancellable: &Cancellable, + priority: &Priority, callback: impl FnOnce(Result) + '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, 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}"), }), diff --git a/src/app/browser/window/tab/item/page/client/request.rs b/src/app/browser/window/tab/item/page/client/request.rs index a37c527a..82cb4e34 100644 --- a/src/app/browser/window/tab/item/page/client/request.rs +++ b/src/app/browser/window/tab/item/page/client/request.rs @@ -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 { + let mut referrer = self.referrer.to_vec(); + referrer.push(self.clone()); + referrer + } + + pub fn uri(&self) -> Option<&Uri> { + self.feature.uri() + } } diff --git a/src/app/browser/window/tab/item/page/client/request/feature.rs b/src/app/browser/window/tab/item/page/client/request/feature.rs index 39f40cd2..3667f1fc 100644 --- a/src/app/browser/window/tab/item/page/client/request/feature.rs +++ b/src/app/browser/window/tab/item/page/client/request/feature.rs @@ -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() + } + } + } } diff --git a/src/app/browser/window/tab/item/page/client/request/feature/protocol.rs b/src/app/browser/window/tab/item/page/client/request/feature/protocol.rs index e8b5e720..8759e139 100644 --- a/src/app/browser/window/tab/item/page/client/request/feature/protocol.rs +++ b/src/app/browser/window/tab/item/page/client/request/feature/protocol.rs @@ -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, + } + } } diff --git a/src/app/browser/window/tab/item/page/client/response/redirect.rs b/src/app/browser/window/tab/item/page/client/response/redirect.rs index f8784703..a4b8d7cd 100644 --- a/src/app/browser/window/tab/item/page/client/response/redirect.rs +++ b/src/app/browser/window/tab/item/page/client/response/redirect.rs @@ -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), }