mirror of
https://github.com/YGGverse/Yoda.git
synced 2025-03-13 06:01:21 +00:00
use current Request
as referrer
This commit is contained in:
parent
4698d7dce1
commit
dd171dabe9
@ -27,7 +27,7 @@ impl Request {
|
|||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
/// Create new `Self` from featured string
|
/// Create new `Self` from featured string
|
||||||
pub fn parse(query: &str, referrer: Option<Box<Self>>) -> Result<Self, Error> {
|
pub fn parse(query: &str, referrer: Option<Self>) -> Result<Self, Error> {
|
||||||
let (feature, request) = Feature::parse(query);
|
let (feature, request) = Feature::parse(query);
|
||||||
|
|
||||||
match Uri::parse(request, UriFlags::NONE) {
|
match Uri::parse(request, UriFlags::NONE) {
|
||||||
@ -40,15 +40,18 @@ impl Request {
|
|||||||
pub fn from_uri(
|
pub fn from_uri(
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
feature: Option<Feature>,
|
feature: Option<Feature>,
|
||||||
referrer: Option<Box<Self>>,
|
referrer: Option<Self>,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
match uri.scheme().as_str() {
|
match uri.scheme().as_str() {
|
||||||
"gemini" => Ok(Self::Gemini {
|
"gemini" => Ok(Self::Gemini {
|
||||||
feature: feature.unwrap_or_default(),
|
feature: feature.unwrap_or_default(),
|
||||||
referrer,
|
referrer: referrer.map(Box::new),
|
||||||
|
uri,
|
||||||
|
}),
|
||||||
|
"titan" => Ok(Self::Titan {
|
||||||
|
referrer: referrer.map(Box::new),
|
||||||
uri,
|
uri,
|
||||||
}),
|
}),
|
||||||
"titan" => Ok(Self::Titan { referrer, uri }),
|
|
||||||
_ => Err(Error::Unsupported),
|
_ => Err(Error::Unsupported),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,16 +65,9 @@ impl Request {
|
|||||||
cancellable: Cancellable,
|
cancellable: Cancellable,
|
||||||
callback: impl FnOnce(Response) + 'static,
|
callback: impl FnOnce(Response) + 'static,
|
||||||
) {
|
) {
|
||||||
match self {
|
match &self {
|
||||||
Self::Gemini {
|
Self::Gemini { .. } => gemini::request(client, self, cancellable, callback),
|
||||||
feature,
|
Self::Titan { .. } => todo!(),
|
||||||
referrer,
|
|
||||||
uri,
|
|
||||||
} => gemini::request(client, feature, uri, referrer, cancellable, callback),
|
|
||||||
Self::Titan {
|
|
||||||
referrer: _,
|
|
||||||
uri: _,
|
|
||||||
} => 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`
|
/// Recursively count referrers of `Self`
|
||||||
/// * useful to apply redirection rules by protocol driver selected
|
/// * useful to apply redirection rules by protocol driver selected
|
||||||
pub fn referrers(&self) -> usize {
|
pub fn referrers(&self) -> usize {
|
||||||
|
@ -7,18 +7,16 @@ use gtk::{
|
|||||||
|
|
||||||
pub fn request(
|
pub fn request(
|
||||||
client: &Client,
|
client: &Client,
|
||||||
feature: Feature,
|
request: Request,
|
||||||
uri: Uri,
|
|
||||||
referrer: Option<Box<Request>>,
|
|
||||||
cancellable: Cancellable,
|
cancellable: Cancellable,
|
||||||
callback: impl FnOnce(Response) + 'static,
|
callback: impl FnOnce(Response) + 'static,
|
||||||
) {
|
) {
|
||||||
send(
|
send(
|
||||||
client,
|
client,
|
||||||
uri.clone(),
|
request.as_uri().clone(),
|
||||||
cancellable.clone(),
|
cancellable.clone(),
|
||||||
move |result| match result {
|
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 {
|
Err(e) => callback(Response::Failure(Failure::Error {
|
||||||
message: e.to_string(),
|
message: e.to_string(),
|
||||||
})),
|
})),
|
||||||
@ -54,10 +52,8 @@ fn send(
|
|||||||
/// Shared handler for Gemini `Result`
|
/// Shared handler for Gemini `Result`
|
||||||
/// * same implementation for Gemini and Titan protocols response
|
/// * same implementation for Gemini and Titan protocols response
|
||||||
fn handle(
|
fn handle(
|
||||||
|
request: Request,
|
||||||
response: ggemini::client::connection::Response,
|
response: ggemini::client::connection::Response,
|
||||||
base: Uri,
|
|
||||||
referrer: Option<Box<Request>>,
|
|
||||||
feature: Feature,
|
|
||||||
cancellable: Cancellable,
|
cancellable: Cancellable,
|
||||||
callback: impl FnOnce(Response) + 'static,
|
callback: impl FnOnce(Response) + 'static,
|
||||||
) {
|
) {
|
||||||
@ -65,14 +61,14 @@ fn handle(
|
|||||||
match response.meta.status {
|
match response.meta.status {
|
||||||
// https://geminiprotocol.net/docs/protocol-specification.gmi#input-expected
|
// https://geminiprotocol.net/docs/protocol-specification.gmi#input-expected
|
||||||
Status::Input => callback(Response::Input(Input::Response {
|
Status::Input => callback(Response::Input(Input::Response {
|
||||||
base,
|
base: request.as_uri().clone(),
|
||||||
title: match response.meta.data {
|
title: match response.meta.data {
|
||||||
Some(data) => data.to_gstring(),
|
Some(data) => data.to_gstring(),
|
||||||
None => "Input expected".into(),
|
None => "Input expected".into(),
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
Status::SensitiveInput => callback(Response::Input(Input::Sensitive {
|
Status::SensitiveInput => callback(Response::Input(Input::Sensitive {
|
||||||
base,
|
base: request.as_uri().clone(),
|
||||||
title: match response.meta.data {
|
title: match response.meta.data {
|
||||||
Some(data) => data.to_gstring(),
|
Some(data) => data.to_gstring(),
|
||||||
None => "Input expected".into(),
|
None => "Input expected".into(),
|
||||||
@ -87,12 +83,12 @@ fn handle(
|
|||||||
cancellable.clone(),
|
cancellable.clone(),
|
||||||
move |result| match result {
|
move |result| match result {
|
||||||
Ok(text) => callback(Response::TextGemini {
|
Ok(text) => callback(Response::TextGemini {
|
||||||
base,
|
base: request.as_uri().clone(),
|
||||||
source: text.data,
|
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 {
|
Err(e) => callback(Response::Failure(Failure::Mime {
|
||||||
base,
|
base: request.as_uri().clone(),
|
||||||
mime: mime.to_string(),
|
mime: mime.to_string(),
|
||||||
message: e.to_string(),
|
message: e.to_string(),
|
||||||
})),
|
})),
|
||||||
@ -100,14 +96,14 @@ fn handle(
|
|||||||
),
|
),
|
||||||
"image/png" | "image/gif" | "image/jpeg" | "image/webp" => {
|
"image/png" | "image/gif" | "image/jpeg" | "image/webp" => {
|
||||||
callback(Response::Stream {
|
callback(Response::Stream {
|
||||||
base,
|
base: request.as_uri().clone(),
|
||||||
mime: mime.to_string(),
|
mime: mime.to_string(),
|
||||||
stream: response.connection.stream(),
|
stream: response.connection.stream(),
|
||||||
cancellable,
|
cancellable,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
mime => callback(Response::Failure(Failure::Mime {
|
mime => callback(Response::Failure(Failure::Mime {
|
||||||
base,
|
base: request.as_uri().clone(),
|
||||||
mime: mime.to_string(),
|
mime: mime.to_string(),
|
||||||
message: format!("Content type `{mime}` yet not supported"),
|
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
|
// 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
|
// 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
|
// https://geminiprotocol.net/docs/protocol-specification.gmi#status-60
|
||||||
Status::CertificateRequest => callback(Response::Certificate(Certificate::Request {
|
Status::CertificateRequest => callback(Response::Certificate(Certificate::Request {
|
||||||
title: match response.meta.data {
|
title: match response.meta.data {
|
||||||
@ -150,50 +146,49 @@ fn handle(
|
|||||||
/// `Response::Redirect` builder
|
/// `Response::Redirect` builder
|
||||||
/// * [Redirect specification](https://geminiprotocol.net/docs/protocol-specification.gmi#redirection)
|
/// * [Redirect specification](https://geminiprotocol.net/docs/protocol-specification.gmi#redirection)
|
||||||
fn redirect(
|
fn redirect(
|
||||||
|
request: Request,
|
||||||
response: ggemini::client::connection::Response,
|
response: ggemini::client::connection::Response,
|
||||||
feature: Feature,
|
|
||||||
base: Uri, // relative links conversion
|
|
||||||
referrer: Option<Box<Request>>, // handles redirection rules
|
|
||||||
is_permanent: bool,
|
is_permanent: bool,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
// Validate redirection count
|
// Validate redirection count
|
||||||
if let Some(ref referrer) = referrer {
|
if request.referrers() > 5 {
|
||||||
if referrer.referrers() > 5 {
|
return Response::Failure(Failure::Error {
|
||||||
return Response::Failure(Failure::Error {
|
message: "Max redirection count reached".to_string(),
|
||||||
message: "Max redirection count reached".to_string(),
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Target URL expected from response meta data
|
// Target URL expected from response meta data
|
||||||
match response.meta.data {
|
match response.meta.data {
|
||||||
Some(target) => match Uri::parse_relative(&base, target.as_str(), UriFlags::NONE) {
|
Some(target) => {
|
||||||
Ok(target) => {
|
match Uri::parse_relative(request.as_uri(), target.as_str(), UriFlags::NONE) {
|
||||||
// Disallow external redirection
|
Ok(target) => {
|
||||||
if base.scheme() != target.scheme()
|
// Disallow external redirection
|
||||||
|| base.port() != target.port()
|
if request.as_uri().scheme() != target.scheme()
|
||||||
|| base.host() != target.host()
|
|| 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"
|
return Response::Failure(Failure::Error {
|
||||||
.to_string(),
|
message: "External redirects not allowed by protocol specification"
|
||||||
}); // @TODO placeholder page with optional link open button
|
.to_string(),
|
||||||
}
|
}); // @TODO placeholder page with optional link open button
|
||||||
// Build new request
|
}
|
||||||
match Request::from_uri(target, Some(feature), referrer) {
|
// Build new request
|
||||||
Ok(request) => Response::Redirect(if is_permanent {
|
match Request::from_uri(target, None, Some(request)) {
|
||||||
Redirect::Foreground(request)
|
Ok(request) => Response::Redirect(if is_permanent {
|
||||||
} else {
|
Redirect::Foreground(request)
|
||||||
Redirect::Background(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(),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
Err(e) => Response::Failure(Failure::Error {
|
}
|
||||||
message: e.to_string(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
None => Response::Failure(Failure::Error {
|
None => Response::Failure(Failure::Error {
|
||||||
message: "Target address not found".to_string(),
|
message: "Target address not found".to_string(),
|
||||||
}),
|
}),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user