From 4f7df8ea44b94cab658727f99d5ef580975f9c47 Mon Sep 17 00:00:00 2001 From: yggverse Date: Sun, 19 Jan 2025 02:33:58 +0200 Subject: [PATCH] implement gemini redirection handler --- .../window/tab/item/page/client/request.rs | 34 ++++++-- .../tab/item/page/client/request/gemini.rs | 78 +++++++------------ 2 files changed, 56 insertions(+), 56 deletions(-) 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 465c2256..e0882072 100644 --- a/src/app/browser/window/tab/item/page/client/request.rs +++ b/src/app/browser/window/tab/item/page/client/request.rs @@ -14,17 +14,20 @@ use gtk::{ pub enum Request { Gemini { feature: Feature, - referrer: Vec, + referrer: Option>, + uri: Uri, + }, + Titan { + referrer: Option>, uri: Uri, }, - Titan(Uri), } impl Request { // Constructors /// Create new `Self` from featured string - pub fn parse(query: &str, referrer: Option>) -> Result { + pub fn parse(query: &str, referrer: Option>) -> Result { let (feature, request) = Feature::parse(query); match Uri::parse(request, UriFlags::NONE) { @@ -37,15 +40,15 @@ impl Request { pub fn from_uri( uri: Uri, feature: Option, - referrer: Option>, + referrer: Option>, ) -> Result { match uri.scheme().as_str() { "gemini" => Ok(Self::Gemini { feature: feature.unwrap_or_default(), - referrer: referrer.unwrap_or_default(), + referrer, uri, }), - "titan" => Ok(Self::Titan(uri)), + "titan" => Ok(Self::Titan { referrer, uri }), _ => Err(Error::Unsupported), } } @@ -65,7 +68,10 @@ impl Request { referrer, uri, } => gemini::send(client, feature, uri, referrer, cancellable, callback), - Self::Titan(_) => todo!(), + Self::Titan { + referrer: _, + uri: _, + } => todo!(), } } @@ -79,7 +85,19 @@ impl Request { referrer: _, uri, } - | Self::Titan(uri) => uri, + | Self::Titan { referrer: _, uri } => uri, } } + + /// Recursively count referrers of `Self` + /// * useful to apply redirection rules by protocol driver selected + pub fn referrers(&self) -> usize { + let count = match self { + Request::Gemini { referrer, .. } => referrer, + Request::Titan { referrer, .. } => referrer, + } + .as_ref() + .map_or(0, |request| request.referrers()); + 1 + count + } } diff --git a/src/app/browser/window/tab/item/page/client/request/gemini.rs b/src/app/browser/window/tab/item/page/client/request/gemini.rs index 7051948f..ed74a7ee 100644 --- a/src/app/browser/window/tab/item/page/client/request/gemini.rs +++ b/src/app/browser/window/tab/item/page/client/request/gemini.rs @@ -9,7 +9,7 @@ pub fn send( client: &Client, feature: Feature, uri: Uri, - referrer: Vec, + referrer: Option>, cancellable: Cancellable, callback: impl FnOnce(Response) + 'static, ) { @@ -56,7 +56,7 @@ fn request( fn handle( response: ggemini::client::connection::Response, base: Uri, - referrer: Vec, + referrer: Option>, feature: Feature, cancellable: Cancellable, callback: impl FnOnce(Response) + 'static, @@ -117,23 +117,9 @@ fn handle( })), }, // https://geminiprotocol.net/docs/protocol-specification.gmi#status-30-temporary-redirection - Status::Redirect => callback(redirect( - response, - base, - referrer, - cancellable, - Priority::DEFAULT, - false, - )), + Status::Redirect => callback(redirect(response, feature, base, referrer, false)), // https://geminiprotocol.net/docs/protocol-specification.gmi#status-31-permanent-redirection - Status::PermanentRedirect => callback(redirect( - response, - base, - referrer, - cancellable, - Priority::DEFAULT, - true, - )), + Status::PermanentRedirect => callback(redirect(response, feature, base, referrer, true)), // https://geminiprotocol.net/docs/protocol-specification.gmi#status-60 Status::CertificateRequest => callback(Response::Certificate(Certificate::Request { title: match response.meta.data { @@ -161,26 +147,24 @@ fn handle( } } -/// Shared redirection `Response` builder +/// `Response::Redirect` builder +/// * [Redirect specification](https://geminiprotocol.net/docs/protocol-specification.gmi#redirection) fn redirect( - // Subject to parse response: ggemini::client::connection::Response, - // Wanted to process relative links - base: Uri, - // List of previous requests to handle redirection rules - referrer: Vec, - cancellable: Cancellable, - priority: Priority, - is_foreground: bool, + feature: Feature, + base: Uri, // relative links conversion + referrer: Option>, // handles redirection rules + is_permanent: bool, ) -> Response { - // Validate redirection attempt - // [Gemini protocol specifications](https://geminiprotocol.net/docs/protocol-specification.gmi#redirection) - if referrer.len() > 5 { - return Response::Failure(Failure::Error { - message: "Max redirection count reached".to_string(), - }); + // Validate redirection count + if let Some(ref referrer) = referrer { + if referrer.referrers() > 5 { + return Response::Failure(Failure::Error { + message: "Max redirection count reached".to_string(), + }); + } } - // Target URL expected from client response meta data + // Target URL expected from response meta data match response.meta.data { Some(target) => match Uri::parse_relative(&base, target.as_str(), UriFlags::NONE) { Ok(target) => { @@ -194,22 +178,20 @@ fn redirect( .to_string(), }); // @TODO placeholder page with optional link open button } - - // Build new `Request` for redirection `Response` - // * make sure that `referrer` already contain current `Request` - // (to validate redirection count in chain) - todo!() - /*let request = - Request::build(&target.to_string(), Some(referrer), cancellable, priority); - - Response::Redirect(if is_foreground { - Redirect::Foreground(request) - } else { - Redirect::Background(request) - })*/ + // Build new request + match Request::from_uri(target, Some(feature), referrer) { + Ok(request) => Response::Redirect(if is_permanent { + Redirect::Foreground(request) + } else { + Redirect::Background(request) + }), + Err(e) => Response::Failure(Failure::Error { + message: e.to_string(), + }), + } } Err(e) => Response::Failure(Failure::Error { - message: format!("Could not parse target address: {e}"), + message: e.to_string(), }), }, None => Response::Failure(Failure::Error {