mirror of
https://github.com/YGGverse/Yoda.git
synced 2025-03-11 13:11:08 +00:00
implement 30,31 status codes
This commit is contained in:
parent
9f2d9ce110
commit
9818b3a998
@ -41,9 +41,9 @@ GTK 4 / Libadwaita client written in Rust
|
||||
* [x] Input
|
||||
* [x] `10` Input
|
||||
* [x] `11` Sensitive input
|
||||
* [ ] Redirection
|
||||
* [ ] `30` Temporary (partial)
|
||||
* [ ] `31` Permanent (partial)
|
||||
* [x] Redirection
|
||||
* [x] `30` Temporary
|
||||
* [x] `31` Permanent
|
||||
* [ ] Temporary failure
|
||||
* [ ] `40` Unspecified condition
|
||||
* [ ] `41` Server unavailable
|
||||
|
@ -155,20 +155,62 @@ impl Page {
|
||||
}
|
||||
}
|
||||
|
||||
// @TODO rename to `load`
|
||||
pub fn navigation_reload(&self) {
|
||||
/// Global limit to prevent infinitive redirects (ALL protocols)
|
||||
/// * every protocol implementation has own value checker, according to specification
|
||||
const DEFAULT_MAX_REDIRECT_COUNT: i8 = 10;
|
||||
|
||||
// Reset widgets
|
||||
self.input.unset();
|
||||
|
||||
// Init shared objects to not spawn a lot
|
||||
let request_text = self.navigation.request_text();
|
||||
// Create shared variant value
|
||||
let id = self.id.to_variant();
|
||||
|
||||
// Try **take** request value from Redirect holder first
|
||||
let request = if let Some(redirect) = self.meta.take_redirect() {
|
||||
// Increase redirect counter
|
||||
self.meta.set_redirect_count(self.meta.redirect_count() + 1);
|
||||
|
||||
// Prevent infinitive redirection by global settings
|
||||
if self.meta.redirect_count() > DEFAULT_MAX_REDIRECT_COUNT {
|
||||
todo!();
|
||||
}
|
||||
|
||||
// Update navigation on redirect `is_foreground`
|
||||
if redirect.is_foreground() {
|
||||
self.navigation
|
||||
.set_request_text(redirect.request().as_str());
|
||||
}
|
||||
|
||||
// Return value from redirection holder
|
||||
redirect.request()
|
||||
} else {
|
||||
// Add history record
|
||||
let value = self.navigation.request_text();
|
||||
|
||||
match self.navigation.history_current() {
|
||||
Some(current) => {
|
||||
if current != value {
|
||||
self.navigation.history_add(value);
|
||||
}
|
||||
}
|
||||
None => self.navigation.history_add(value),
|
||||
}
|
||||
|
||||
// Reset redirect counter as value taken from user input
|
||||
self.meta.set_redirect_count(0);
|
||||
|
||||
// Return value from navigation entry
|
||||
self.navigation.request_text()
|
||||
};
|
||||
|
||||
// Update
|
||||
self.meta.set_status(Status::Reload).set_title(&"Loading..");
|
||||
self.action_update.activate(Some(&id));
|
||||
|
||||
// Route by request
|
||||
match Uri::parse(&request_text, UriFlags::NONE) {
|
||||
match Uri::parse(&request, UriFlags::NONE) {
|
||||
Ok(uri) => {
|
||||
// Route by scheme
|
||||
match uri.scheme().as_str() {
|
||||
@ -199,17 +241,17 @@ impl Page {
|
||||
// Try interpret URI manually
|
||||
if Regex::match_simple(
|
||||
r"^[^\/\s]+\.[\w]{2,}",
|
||||
request_text.clone(),
|
||||
request.clone(),
|
||||
RegexCompileFlags::DEFAULT,
|
||||
RegexMatchFlags::DEFAULT,
|
||||
) {
|
||||
// Seems request contain some host, try append default scheme
|
||||
let request_text = gformat!("gemini://{request_text}");
|
||||
let request = gformat!("gemini://{request}");
|
||||
// Make sure new request conversable to valid URI
|
||||
match Uri::parse(&request_text, UriFlags::NONE) {
|
||||
match Uri::parse(&request, UriFlags::NONE) {
|
||||
Ok(_) => {
|
||||
// Update
|
||||
self.navigation.set_request_text(&request_text);
|
||||
self.navigation.set_request_text(&request);
|
||||
|
||||
// Reload page
|
||||
self.action_page_reload.activate(None);
|
||||
@ -220,13 +262,13 @@ impl Page {
|
||||
}
|
||||
} else {
|
||||
// Plain text given, make search request to default provider
|
||||
let request_text = gformat!(
|
||||
let request = gformat!(
|
||||
"gemini://tlgs.one/search?{}",
|
||||
Uri::escape_string(&request_text, None, false)
|
||||
Uri::escape_string(&request, None, false)
|
||||
);
|
||||
|
||||
// Update
|
||||
self.navigation.set_request_text(&request_text);
|
||||
self.navigation.set_request_text(&request);
|
||||
|
||||
// Reload page
|
||||
self.action_page_reload.activate(None);
|
||||
@ -366,44 +408,8 @@ impl Page {
|
||||
let id = self.id.to_variant();
|
||||
let input = self.input.clone();
|
||||
let meta = self.meta.clone();
|
||||
let navigation = self.navigation.clone();
|
||||
let url = uri.clone().to_str();
|
||||
|
||||
// Check for page redirect pending
|
||||
if meta.is_redirect() {
|
||||
// Check for protocol limits
|
||||
if meta.redirect_count().unwrap() > 5 {
|
||||
// Update meta
|
||||
meta.set_status(Status::Failure).set_title(&"Oops");
|
||||
// Show placeholder with confirmation request to continue
|
||||
content.to_text_gemini(
|
||||
&uri,
|
||||
&gformat!(
|
||||
// @TODO status page?
|
||||
"# Redirect issue\n\nRedirection limit reached\n\nContinue:\n\n=> {}",
|
||||
meta.redirect_target().unwrap().to_string()
|
||||
),
|
||||
);
|
||||
|
||||
return; // @TODO
|
||||
} else {
|
||||
action_page_open.activate(Some(
|
||||
&meta.redirect_target().unwrap().to_string().to_variant(),
|
||||
));
|
||||
// @TODO is_follow
|
||||
}
|
||||
}
|
||||
|
||||
// Add history record
|
||||
match navigation.history_current() {
|
||||
Some(current) => {
|
||||
if current != url {
|
||||
navigation.history_add(url.clone());
|
||||
}
|
||||
}
|
||||
None => navigation.history_add(url.clone()),
|
||||
}
|
||||
|
||||
// Init socket
|
||||
let client = SocketClient::new();
|
||||
|
||||
@ -681,8 +687,8 @@ impl Page {
|
||||
// Extract redirection URL from response data
|
||||
match response.data() {
|
||||
Some(unresolved_url) => {
|
||||
// New URL from server MAY to be relative (according to the protocol),
|
||||
// resolve to absolute URI using current request value as the base for parser
|
||||
// New URL from server MAY to be relative (according to the protocol specification),
|
||||
// resolve to absolute URI gobject using current request as the base for parser:
|
||||
// https://docs.gtk.org/glib/type_func.Uri.resolve_relative.html
|
||||
match Uri::resolve_relative(
|
||||
Some(&uri.to_string()),
|
||||
@ -690,60 +696,84 @@ impl Page {
|
||||
UriFlags::NONE,
|
||||
) {
|
||||
Ok(resolved_url) => {
|
||||
// Build valid URI (this conversion wanted to process query and fragment later)
|
||||
// Build valid URI from resolved URL string
|
||||
// this conversion wanted to simply exclude `query` and `fragment` later (as restricted by protocol specification)
|
||||
match Uri::parse(resolved_url.as_str(), UriFlags::NONE) {
|
||||
Ok(resolved_uri) => {
|
||||
// Client MUST prevent external redirects
|
||||
// Client MUST prevent external redirects (by protocol specification)
|
||||
if is_external_uri(&resolved_uri, &uri) {
|
||||
// Update meta
|
||||
meta.set_status(Status::Failure)
|
||||
.set_title(&"Oops");
|
||||
|
||||
// Show placeholder with confirmation request to continue
|
||||
// Show placeholder with manual confirmation to continue @TODO status page?
|
||||
content.to_text_gemini(
|
||||
&uri,
|
||||
&gformat!( // @TODO status page?
|
||||
&gformat!(
|
||||
"# Redirect issue\n\nExternal redirects not allowed by protocol\n\nContinue:\n\n=> {}",
|
||||
resolved_uri.to_string()
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// Client MUST limit the number of redirects they follow to 5 (by protocol specification)
|
||||
} else if meta.redirect_count() >= 5 {
|
||||
// Update meta
|
||||
meta.set_status(Status::Failure)
|
||||
.set_title(&"Oops");
|
||||
|
||||
// Show placeholder with manual confirmation to continue @TODO status page?
|
||||
content.to_text_gemini(
|
||||
&uri,
|
||||
&gformat!(
|
||||
"# Redirect issue\n\nLimit the number of redirects reached\n\nContinue:\n\n=> {}",
|
||||
resolved_uri.to_string()
|
||||
)
|
||||
);
|
||||
// Redirection value looks valid, create new redirect (stored in meta `Redirect` holder)
|
||||
// then call page reload action to apply it by the parental controller
|
||||
} else {
|
||||
meta.set_redirect(
|
||||
match meta.redirect_count() {
|
||||
Some(count) => count + 1,
|
||||
None => 0
|
||||
},
|
||||
// Skip query and fragment by protocol requirements
|
||||
// @TODO review fragment specification
|
||||
resolved_uri.to_string_partial(
|
||||
UriHideFlags::FRAGMENT | UriHideFlags::QUERY
|
||||
),
|
||||
// Set follow policy based on status code
|
||||
match response.status() {
|
||||
gemini::client::response::meta::Status::PermanentRedirect => true,
|
||||
_ => false
|
||||
},
|
||||
Uri::parse(
|
||||
resolved_uri.to_string_partial(
|
||||
UriHideFlags::FRAGMENT | UriHideFlags::QUERY // @TODO review fragment specification
|
||||
).as_str(),
|
||||
UriFlags::NONE
|
||||
).unwrap()
|
||||
)
|
||||
.set_status(Status::Redirect)
|
||||
.set_title(&"Redirect"); // @TODO is really wanted here?
|
||||
.set_status(Status::Redirect) // @TODO is this status really wanted?
|
||||
.set_title(&"Redirect");
|
||||
|
||||
// Reload page to apply redirect
|
||||
// Reload page to apply redirection
|
||||
action_page_reload.activate(None);
|
||||
}
|
||||
},
|
||||
Err(reason) => {
|
||||
meta.set_status(Status::Failure);
|
||||
let status = Status::Failure;
|
||||
let title = &"Oops";
|
||||
|
||||
meta.set_status(status)
|
||||
.set_title(title);
|
||||
|
||||
content
|
||||
.to_status_failure()
|
||||
.set_title(title)
|
||||
.set_description(Some(reason.message()));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(reason) => {
|
||||
meta.set_status(Status::Failure);
|
||||
let status = Status::Failure;
|
||||
let title = &"Oops";
|
||||
|
||||
meta.set_status(status)
|
||||
.set_title(title);
|
||||
|
||||
content
|
||||
.to_status_failure()
|
||||
.set_title(title)
|
||||
.set_description(Some(reason.message()));
|
||||
},
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
mod redirect;
|
||||
use redirect::Redirect;
|
||||
|
||||
use gtk::glib::{GString, Uri};
|
||||
use gtk::glib::GString;
|
||||
use std::{cell::RefCell, sync::Arc};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -27,6 +27,7 @@ pub struct Meta {
|
||||
status: RefCell<Status>,
|
||||
title: RefCell<GString>,
|
||||
redirect: RefCell<Option<Redirect>>,
|
||||
redirect_count: RefCell<i8>,
|
||||
}
|
||||
|
||||
impl Meta {
|
||||
@ -37,6 +38,7 @@ impl Meta {
|
||||
status: RefCell::new(status),
|
||||
title: RefCell::new(title),
|
||||
redirect: RefCell::new(None),
|
||||
redirect_count: RefCell::new(0),
|
||||
})
|
||||
}
|
||||
|
||||
@ -52,16 +54,22 @@ impl Meta {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_redirect(&self, count: i8, is_follow: bool, target: Uri) -> &Self {
|
||||
pub fn set_redirect(&self, request: GString, is_foreground: bool) -> &Self {
|
||||
self.redirect
|
||||
.replace(Some(Redirect::new(count, is_follow, target)));
|
||||
.replace(Some(Redirect::new(request, is_foreground)));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_redirect_count(&self, redirect_count: i8) -> &Self {
|
||||
self.redirect_count.replace(redirect_count);
|
||||
self
|
||||
}
|
||||
|
||||
/* @TODO not in use
|
||||
pub fn unset_redirect(&self) -> &Self {
|
||||
self.redirect.replace(None);
|
||||
self
|
||||
}
|
||||
} */
|
||||
|
||||
// Getters
|
||||
|
||||
@ -73,28 +81,14 @@ impl Meta {
|
||||
self.title.borrow().clone()
|
||||
}
|
||||
|
||||
pub fn is_redirect(&self) -> bool {
|
||||
self.redirect.borrow().is_some()
|
||||
pub fn redirect_count(&self) -> i8 {
|
||||
self.redirect_count.borrow().clone()
|
||||
}
|
||||
|
||||
pub fn redirect_count(&self) -> Option<i8> {
|
||||
match *self.redirect.borrow() {
|
||||
Some(ref redirect) => Some(redirect.count().clone()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn redirect_target(&self) -> Option<Uri> {
|
||||
match *self.redirect.borrow() {
|
||||
Some(ref redirect) => Some(redirect.target().clone()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn redirect_is_follow(&self) -> Option<bool> {
|
||||
match *self.redirect.borrow() {
|
||||
Some(ref redirect) => Some(redirect.is_follow().clone()),
|
||||
None => None,
|
||||
}
|
||||
/// WARNING!
|
||||
///
|
||||
/// This function **take** the `Redirect` without clone semantics
|
||||
pub fn take_redirect(&self) -> Option<Redirect> {
|
||||
self.redirect.take()
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use gtk::glib::Uri;
|
||||
use gtk::glib::GString;
|
||||
|
||||
/// # Redirection data holder
|
||||
///
|
||||
@ -8,33 +8,32 @@ use gtk::glib::Uri;
|
||||
///
|
||||
/// ## Members
|
||||
///
|
||||
/// * `count` - to limit redirect attempts
|
||||
/// * `is_follow` - indicates how to process this redirect exactly
|
||||
/// * `target` - destination address
|
||||
/// * `is_foreground` - indicates how to process this redirect
|
||||
/// * `request` - destination
|
||||
/// * currently, it's raw `GString` not [Uri](https://docs.gtk.org/glib/struct.Uri.html)
|
||||
/// because of compatibility with request field as it could contain any other, not parsable values
|
||||
pub struct Redirect {
|
||||
count: i8,
|
||||
is_follow: bool,
|
||||
target: Uri,
|
||||
is_foreground: bool,
|
||||
request: GString,
|
||||
}
|
||||
|
||||
impl Redirect {
|
||||
pub fn new(count: i8, is_follow: bool, target: Uri) -> Self {
|
||||
// Constructors
|
||||
|
||||
pub fn new(request: GString, is_foreground: bool) -> Self {
|
||||
Self {
|
||||
count,
|
||||
is_follow,
|
||||
target,
|
||||
is_foreground,
|
||||
request,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn count(&self) -> &i8 {
|
||||
&self.count
|
||||
// Getters
|
||||
|
||||
pub fn request(&self) -> GString {
|
||||
self.request.clone()
|
||||
}
|
||||
|
||||
pub fn is_follow(&self) -> &bool {
|
||||
&self.is_follow
|
||||
}
|
||||
|
||||
pub fn target(&self) -> &Uri {
|
||||
&self.target
|
||||
pub fn is_foreground(&self) -> bool {
|
||||
self.is_foreground
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user