From dd171dabe9c58f961dbe5c722063b8ebe9371882 Mon Sep 17 00:00:00 2001 From: yggverse Date: Sun, 19 Jan 2025 03:15:03 +0200 Subject: [PATCH] use current `Request` as `referrer` --- .../window/tab/item/page/client/request.rs | 32 +++--- .../tab/item/page/client/request/gemini.rs | 99 +++++++++---------- 2 files changed, 65 insertions(+), 66 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 f9d10924..a5232d66 100644 --- a/src/app/browser/window/tab/item/page/client/request.rs +++ b/src/app/browser/window/tab/item/page/client/request.rs @@ -27,7 +27,7 @@ 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) { @@ -40,15 +40,18 @@ 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: referrer.map(Box::new), + uri, + }), + "titan" => Ok(Self::Titan { + referrer: referrer.map(Box::new), uri, }), - "titan" => Ok(Self::Titan { referrer, uri }), _ => Err(Error::Unsupported), } } @@ -62,16 +65,9 @@ impl Request { cancellable: Cancellable, callback: impl FnOnce(Response) + 'static, ) { - match self { - Self::Gemini { - feature, - referrer, - uri, - } => gemini::request(client, feature, uri, referrer, cancellable, callback), - Self::Titan { - referrer: _, - uri: _, - } => todo!(), + match &self { + Self::Gemini { .. } => gemini::request(client, self, cancellable, callback), + Self::Titan { .. } => todo!(), } } @@ -89,6 +85,14 @@ impl Request { } } + /// Get `Feature` reference for `Self` + pub fn feature(&self) -> &Feature { + match self { + Request::Gemini { feature, .. } => feature, + Request::Titan { .. } => &Feature::Default, + } + } + /// Recursively count referrers of `Self` /// * useful to apply redirection rules by protocol driver selected pub fn referrers(&self) -> usize { 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 739d2a0f..af1ea035 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 @@ -7,18 +7,16 @@ use gtk::{ pub fn request( client: &Client, - feature: Feature, - uri: Uri, - referrer: Option>, + request: Request, cancellable: Cancellable, callback: impl FnOnce(Response) + 'static, ) { send( client, - uri.clone(), + request.as_uri().clone(), cancellable.clone(), move |result| match result { - Ok(response) => handle(response, uri, referrer, feature, cancellable, callback), + Ok(response) => handle(request, response, cancellable, callback), Err(e) => callback(Response::Failure(Failure::Error { message: e.to_string(), })), @@ -54,10 +52,8 @@ fn send( /// Shared handler for Gemini `Result` /// * same implementation for Gemini and Titan protocols response fn handle( + request: Request, response: ggemini::client::connection::Response, - base: Uri, - referrer: Option>, - feature: Feature, cancellable: Cancellable, callback: impl FnOnce(Response) + 'static, ) { @@ -65,14 +61,14 @@ fn handle( match response.meta.status { // https://geminiprotocol.net/docs/protocol-specification.gmi#input-expected Status::Input => callback(Response::Input(Input::Response { - base, + base: request.as_uri().clone(), title: match response.meta.data { Some(data) => data.to_gstring(), None => "Input expected".into(), }, })), Status::SensitiveInput => callback(Response::Input(Input::Sensitive { - base, + base: request.as_uri().clone(), title: match response.meta.data { Some(data) => data.to_gstring(), None => "Input expected".into(), @@ -87,12 +83,12 @@ fn handle( cancellable.clone(), move |result| match result { Ok(text) => callback(Response::TextGemini { - base, + base: request.as_uri().clone(), source: text.data, - is_source_request: matches!(feature, Feature::Source), + is_source_request: matches!(request.feature(), Feature::Source), // @TODO return `Feature`? }), Err(e) => callback(Response::Failure(Failure::Mime { - base, + base: request.as_uri().clone(), mime: mime.to_string(), message: e.to_string(), })), @@ -100,14 +96,14 @@ fn handle( ), "image/png" | "image/gif" | "image/jpeg" | "image/webp" => { callback(Response::Stream { - base, + base: request.as_uri().clone(), mime: mime.to_string(), stream: response.connection.stream(), cancellable, }) } mime => callback(Response::Failure(Failure::Mime { - base, + base: request.as_uri().clone(), mime: mime.to_string(), message: format!("Content type `{mime}` yet not supported"), })), @@ -117,9 +113,9 @@ fn handle( })), }, // https://geminiprotocol.net/docs/protocol-specification.gmi#status-30-temporary-redirection - Status::Redirect => callback(redirect(response, feature, base, referrer, false)), + Status::Redirect => callback(redirect(request, response, false)), // https://geminiprotocol.net/docs/protocol-specification.gmi#status-31-permanent-redirection - Status::PermanentRedirect => callback(redirect(response, feature, base, referrer, true)), + Status::PermanentRedirect => callback(redirect(request, response, true)), // https://geminiprotocol.net/docs/protocol-specification.gmi#status-60 Status::CertificateRequest => callback(Response::Certificate(Certificate::Request { title: match response.meta.data { @@ -150,50 +146,49 @@ fn handle( /// `Response::Redirect` builder /// * [Redirect specification](https://geminiprotocol.net/docs/protocol-specification.gmi#redirection) fn redirect( + request: Request, response: ggemini::client::connection::Response, - feature: Feature, - base: Uri, // relative links conversion - referrer: Option>, // handles redirection rules is_permanent: bool, ) -> Response { // 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(), - }); - } + if request.referrers() > 5 { + return Response::Failure(Failure::Error { + message: "Max redirection count reached".to_string(), + }); } + // 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) => { - // Disallow external redirection - if base.scheme() != target.scheme() - || base.port() != target.port() - || base.host() != target.host() - { - return Response::Failure(Failure::Error { - message: "External redirects not allowed by protocol specification" - .to_string(), - }); // @TODO placeholder page with optional link open button - } - // 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(), - }), + Some(target) => { + match Uri::parse_relative(request.as_uri(), target.as_str(), UriFlags::NONE) { + Ok(target) => { + // Disallow external redirection + if request.as_uri().scheme() != target.scheme() + || request.as_uri().port() != target.port() + || request.as_uri().host() != target.host() + { + return Response::Failure(Failure::Error { + message: "External redirects not allowed by protocol specification" + .to_string(), + }); // @TODO placeholder page with optional link open button + } + // Build new request + match Request::from_uri(target, None, Some(request)) { + 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: e.to_string(), + }), } - Err(e) => Response::Failure(Failure::Error { - message: e.to_string(), - }), - }, + } None => Response::Failure(Failure::Error { message: "Target address not found".to_string(), }),