From 3d7818fbd62b960a8be8b247ce4bdeb7d1bcdb08 Mon Sep 17 00:00:00 2001 From: yggverse Date: Sat, 22 Mar 2025 19:52:49 +0200 Subject: [PATCH] hold socket address info --- .../window/tab/item/client/driver/gemini.rs | 766 +++++++++--------- .../tab/item/page/navigation/request/info.rs | 32 +- .../page/navigation/request/info/socket.rs | 6 + 3 files changed, 414 insertions(+), 390 deletions(-) create mode 100644 src/app/browser/window/tab/item/page/navigation/request/info/socket.rs diff --git a/src/app/browser/window/tab/item/client/driver/gemini.rs b/src/app/browser/window/tab/item/client/driver/gemini.rs index 64240c33..ce193827 100644 --- a/src/app/browser/window/tab/item/client/driver/gemini.rs +++ b/src/app/browser/window/tab/item/client/driver/gemini.rs @@ -176,156 +176,181 @@ fn handle( let page = page.clone(); let redirects = redirects.clone(); move |result| match result { - Ok((response, connection)) => match response { - // https://geminiprotocol.net/docs/protocol-specification.gmi#input-expected - Response::Input(input) => { - let t = input.to_string(); - page.set_progress(0.0); - page.set_title(&t); - if is_snap_history { - page.snap_history(); - } - redirects.replace(0); // reset - update_page_info(&page, &uri, EVENT_COMPLETED); - match input { - // https://geminiprotocol.net/docs/protocol-specification.gmi#status-10 - Input::Default { message } => page.input.set_new_response( - page.item_action.clone(), - uri, - Some(message.as_ref().unwrap_or(&t)), - Some(1024), - ), - // https://geminiprotocol.net/docs/protocol-specification.gmi#status-11-sensitive-input - Input::Sensitive { message } => page.input.set_new_sensitive( - page.item_action.clone(), - uri, - Some(message.as_ref().unwrap_or(&t)), - Some(1024), - ) - } + Ok((response, connection)) => { + // Update socket info at the point, where the connection is active yet + { + use gtk::prelude::SocketConnectionExt; + let mut i = page.navigation.request.info.borrow_mut(); + i.set_socket( + connection.socket_connection.local_address().unwrap(), + connection.socket_connection.remote_address().unwrap() + ); + // * unwrap fails only on `connection.socket_connection.is_closed()` + // drop the panic as unexpected. } - // https://geminiprotocol.net/docs/protocol-specification.gmi#status-20 - Response::Success(success) => match *feature { - Feature::Download => { - // Init download widget - let s = page.content.to_status_download( - crate::tool::uri_to_title(&uri).trim_matches(MAIN_SEPARATOR), // grab default filename from base URI, - // format FS entities - &cancellable, - { - let cancellable = cancellable.clone(); - let stream = connection.stream(); - move |file, action| match file.replace( - None, - false, - gtk::gio::FileCreateFlags::NONE, - Some(&cancellable), - ) { - Ok(file_output_stream) => { - use crate::tool::Format; - // Asynchronously read [IOStream](https://docs.gtk.org/gio/class.IOStream.html) - // to local [MemoryInputStream](https://docs.gtk.org/gio/class.MemoryInputStream.html) - // show bytes count in loading widget, validate max size for incoming data - // * no dependency of Gemini library here, feel free to use any other `IOStream` processor - file_output_stream::from_stream_async( - stream.clone(), - file_output_stream, - cancellable.clone(), - Priority::DEFAULT, - ( - 0x100000, // 1M bytes per chunk - None, // unlimited - 0, // initial totals - ), - ( - // on chunk - { - let action = action.clone(); - move |_, total| action.update.activate(&format!( - "Received {}...", - total.bytes() - )) - }, - // on complete - { - let action = action.clone(); - move |result| match result { - Ok((_, total)) => { - action.complete.activate(&format!( - "Saved to {} ({} total)", - file.parse_name(), - total.bytes() - )) - } - Err(e) => action.cancel.activate(&e.to_string()) - } - }, - ), - ) - } - Err(e) => action.cancel.activate(&e.to_string()), - } - }, - ); + // Handle response + match response { + // https://geminiprotocol.net/docs/protocol-specification.gmi#input-expected + Response::Input(input) => { + let t = input.to_string(); page.set_progress(0.0); - page.set_title(&s.title()); + page.set_title(&t); if is_snap_history { page.snap_history(); } redirects.replace(0); // reset - }, - _ => match success.mime() { - "text/gemini" | "text/plain" => memory_input_stream::from_stream_async( - connection.stream(), - Priority::DEFAULT, - cancellable.clone(), - ( - 0x400, // 1024 chunk - 0xfffff, // 1M limit + update_page_info(&page, &uri, EVENT_COMPLETED); + match input { + // https://geminiprotocol.net/docs/protocol-specification.gmi#status-10 + Input::Default { message } => page.input.set_new_response( + page.item_action.clone(), + uri, + Some(message.as_ref().unwrap_or(&t)), + Some(1024), ), - ( - |_, _| {}, // on chunk (maybe nothing to count yet @TODO) - move |result| match result { // on complete - Ok((memory_input_stream, total)) => memory_input_stream.read_all_async( - vec![0; total], - Priority::DEFAULT, + // https://geminiprotocol.net/docs/protocol-specification.gmi#status-11-sensitive-input + Input::Sensitive { message } => page.input.set_new_sensitive( + page.item_action.clone(), + uri, + Some(message.as_ref().unwrap_or(&t)), + Some(1024), + ) + } + } + // https://geminiprotocol.net/docs/protocol-specification.gmi#status-20 + Response::Success(success) => match *feature { + Feature::Download => { + // Init download widget + let s = page.content.to_status_download( + crate::tool::uri_to_title(&uri).trim_matches(MAIN_SEPARATOR), // grab default filename from base URI, + // format FS entities + &cancellable, + { + let cancellable = cancellable.clone(); + let stream = connection.stream(); + move |file, action| match file.replace( + None, + false, + gtk::gio::FileCreateFlags::NONE, Some(&cancellable), - move |result| match result { - Ok((buffer, _ ,_)) => match std::str::from_utf8(&buffer) { - Ok(data) => { - let mut i = page.navigation.request.info.borrow_mut(); - i - .add_event("Parsing".to_string()) - .set_mime(Some(success.mime().to_string())) - .set_request(Some(uri.to_string())) - .set_size(Some(data.len())); - let w = if matches!(*feature, Feature::Source) { - page.content.to_text_source(data) - } else { - match success.mime() { - "text/gemini" => page.content.to_text_gemini(&uri, data), - "text/plain" => page.content.to_text_plain(data), - _ => panic!() // unexpected + ) { + Ok(file_output_stream) => { + use crate::tool::Format; + // Asynchronously read [IOStream](https://docs.gtk.org/gio/class.IOStream.html) + // to local [MemoryInputStream](https://docs.gtk.org/gio/class.MemoryInputStream.html) + // show bytes count in loading widget, validate max size for incoming data + // * no dependency of Gemini library here, feel free to use any other `IOStream` processor + file_output_stream::from_stream_async( + stream.clone(), + file_output_stream, + cancellable.clone(), + Priority::DEFAULT, + ( + 0x100000, // 1M bytes per chunk + None, // unlimited + 0, // initial totals + ), + ( + // on chunk + { + let action = action.clone(); + move |_, total| action.update.activate(&format!( + "Received {}...", + total.bytes() + )) + }, + // on complete + { + let action = action.clone(); + move |result| match result { + Ok((_, total)) => { + action.complete.activate(&format!( + "Saved to {} ({} total)", + file.parse_name(), + total.bytes() + )) + } + Err(e) => action.cancel.activate(&e.to_string()) } - }; - i.add_event("Parsed".to_string()); - page.search.set(Some(w.text_view)); - page.set_title(&match w.meta.title { - Some(t) => t.into(), // @TODO - None => crate::tool::uri_to_title(&uri), - }); - page.set_progress(0.0); - page.window_action - .find - .simple_action - .set_enabled(true); - if is_snap_history { - page.snap_history(); - } - redirects.replace(0); // reset - i.add_event(EVENT_COMPLETED.to_string()); + }, + ), + ) + } + Err(e) => action.cancel.activate(&e.to_string()), + } + }, + ); + page.set_progress(0.0); + page.set_title(&s.title()); + if is_snap_history { + page.snap_history(); + } + redirects.replace(0); // reset + }, + _ => match success.mime() { + "text/gemini" | "text/plain" => memory_input_stream::from_stream_async( + connection.stream(), + Priority::DEFAULT, + cancellable.clone(), + ( + 0x400, // 1024 chunk + 0xfffff, // 1M limit + ), + ( + |_, _| {}, // on chunk (maybe nothing to count yet @TODO) + move |result| match result { // on complete + Ok((memory_input_stream, total)) => memory_input_stream.read_all_async( + vec![0; total], + Priority::DEFAULT, + Some(&cancellable), + move |result| match result { + Ok((buffer, _ ,_)) => match std::str::from_utf8(&buffer) { + Ok(data) => { + let mut i = page.navigation.request.info.borrow_mut(); + i + .add_event("Parsing".to_string()) + .set_mime(Some(success.mime().to_string())) + .set_request(Some(uri.to_string())) + .set_size(Some(data.len())); + let w = if matches!(*feature, Feature::Source) { + page.content.to_text_source(data) + } else { + match success.mime() { + "text/gemini" => page.content.to_text_gemini(&uri, data), + "text/plain" => page.content.to_text_plain(data), + _ => panic!() // unexpected + } + }; + i.add_event("Parsed".to_string()); + page.search.set(Some(w.text_view)); + page.set_title(&match w.meta.title { + Some(t) => t.into(), // @TODO + None => crate::tool::uri_to_title(&uri), + }); + page.set_progress(0.0); + page.window_action + .find + .simple_action + .set_enabled(true); + if is_snap_history { + page.snap_history(); + } + redirects.replace(0); // reset + i.add_event(EVENT_COMPLETED.to_string()); + }, + Err(e) => { + let s = page.content.to_status_failure(); + s.set_description(Some(&e.to_string())); + page.set_progress(0.0); + page.set_title(&s.title()); + if is_snap_history { + page.snap_history(); + } + redirects.replace(0); // reset + update_page_info(&page, &uri, EVENT_COMPLETED); + }, }, - Err(e) => { + Err((_, e)) => { let s = page.content.to_status_failure(); s.set_description(Some(&e.to_string())); page.set_progress(0.0); @@ -335,237 +360,207 @@ fn handle( } redirects.replace(0); // reset update_page_info(&page, &uri, EVENT_COMPLETED); - }, - }, - Err((_, e)) => { - let s = page.content.to_status_failure(); - s.set_description(Some(&e.to_string())); - page.set_progress(0.0); - page.set_title(&s.title()); - if is_snap_history { - page.snap_history(); } - redirects.replace(0); // reset - update_page_info(&page, &uri, EVENT_COMPLETED); + } + ), + Err(e) => { + let s = page.content.to_status_failure(); + s.set_description(Some(&e.to_string())); + page.set_progress(0.0); + page.set_title(&s.title()); + if is_snap_history { + page.snap_history(); + } + redirects.replace(0); // reset + update_page_info(&page, &uri, EVENT_COMPLETED); + }, + } + ) + ), + "image/png" | "image/gif" | "image/jpeg" | "image/webp" => { + // Final image size unknown, show loading widget + let status = page.content.to_status_loading( + Some(Duration::from_secs(1)), // show if download time > 1 second + ); + + // Asynchronously read [IOStream](https://docs.gtk.org/gio/class.IOStream.html) + // to local [MemoryInputStream](https://docs.gtk.org/gio/class.MemoryInputStream.html) + // show bytes count in loading widget, validate max size for incoming data + // * no dependency of Gemini library here, feel free to use any other `IOStream` processor + memory_input_stream::from_stream_async( + connection.stream(), + Priority::DEFAULT, + cancellable.clone(), + ( + 0x400, // 1024 bytes per chunk, optional step for images download tracking + 0xA00000 // 10M bytes max to prevent memory overflow if server play with promises + ), + ( + move |_, total| status.set_description(Some(&format!("Download: {total} bytes"))), + { + move | result | match result { + Ok((memory_input_stream, _)) => { + Pixbuf::from_stream_async( + &memory_input_stream, + Some(&cancellable), + move |result| { + match result { + Ok(buffer) => { + page.set_title(&crate::tool::uri_to_title(&uri)); + page.content.to_image(&Texture::for_pixbuf(&buffer)); + { + let mut i = page.navigation.request.info.borrow_mut(); + i + .add_event(EVENT_COMPLETED.to_string()) + .set_mime(Some(success.mime().to_string())) + .set_request(Some(uri.to_string())) + .set_size(Some(buffer.byte_length())); + } + } + Err(e) => { + let s = page.content.to_status_failure(); + s.set_description(Some(e.message())); + page.set_title(&s.title()); + { + let mut i = page.navigation.request.info.borrow_mut(); + i + .add_event(EVENT_COMPLETED.to_string()) + .set_mime(Some(success.mime().to_string())) + .set_request(Some(uri.to_string())) + .set_size(None); + } + } + } + page.set_progress(0.0); + if is_snap_history { + page.snap_history(); + } + redirects.replace(0); // reset + }, + ) + } + Err(e) => { + let s = page.content.to_status_failure(); + s.set_description(Some(&e.to_string())); + page.set_progress(0.0); + page.set_title(&s.title()); + if is_snap_history { + page.snap_history(); + } + redirects.replace(0); // reset + { + let mut i = page.navigation.request.info.borrow_mut(); + i + .add_event(EVENT_COMPLETED.to_string()) + .set_mime(Some(success.mime().to_string())) + .set_request(Some(uri.to_string())) + .set_size(None); + } + } } } ), - Err(e) => { - let s = page.content.to_status_failure(); - s.set_description(Some(&e.to_string())); - page.set_progress(0.0); - page.set_title(&s.title()); - if is_snap_history { - page.snap_history(); - } - redirects.replace(0); // reset - update_page_info(&page, &uri, EVENT_COMPLETED); - }, + ) + } + mime => { + let s = page + .content + .to_status_mime(mime, Some((&page.item_action, &uri))); + s.set_description(Some(&format!("Content type `{mime}` yet not supported"))); + page.set_progress(0.0); + page.set_title(&s.title()); + if is_snap_history { + page.snap_history(); } - ) - ), - "image/png" | "image/gif" | "image/jpeg" | "image/webp" => { - // Final image size unknown, show loading widget - let status = page.content.to_status_loading( - Some(Duration::from_secs(1)), // show if download time > 1 second - ); + redirects.replace(0); // reset + { + let mut i = page.navigation.request.info.borrow_mut(); + i + .add_event(EVENT_COMPLETED.to_string()) + .set_mime(Some(mime.to_string())) + .set_request(Some(uri.to_string())) + .set_size(None); + } + }, + } + }, + // https://geminiprotocol.net/docs/protocol-specification.gmi#status-30-temporary-redirection + // https://geminiprotocol.net/docs/protocol-specification.gmi#status-31-permanent-redirection + Response::Redirect(redirect) => match redirect.to_uri(&uri) { + Ok(target) => { + // Increase client redirection counter + let total = redirects.take() + 1; + // > Client MUST limit the number of redirections they follow to 5 redirections + // > https://geminiprotocol.net/docs/protocol-specification.gmi#redirection + if total > 5 { + let s = page.content.to_status_failure(); + s.set_description(Some("Redirection limit reached")); + page.set_progress(0.0); + page.set_title(&s.title()); + redirects.replace(0); // reset + update_page_info(&page, &uri, EVENT_COMPLETED); + // Disallow external redirection by default as potentially unsafe + // even not specified, require follow confirmation @TODO optional + } else if uri.host() != target.host() { + let u = target.to_string(); + let s = page.content.to_status_failure(); + let t = "External redirection"; + s.set_title(t); + s.set_icon_name(Some("dialog-warning-symbolic")); + s.set_description(Some(&u)); + s.set_child(Some(&{ + let b = gtk::Button::builder() + .css_classes(["suggested-action"]) + .halign(gtk::Align::Center) + .label("Follow") + .build(); + b.connect_clicked({ + let p = page.clone(); + move |_| p.item_action.load.activate(Some(&u), false) + }); + b + })); + page.set_progress(0.0); + page.set_title(t); + redirects.replace(0); // reset + update_page_info(&page, &uri, EVENT_COMPLETED); + } else { + let t = target.to_string(); + if matches!(redirect, Redirect::Permanent { .. }) { + page.navigation.set_request(&t); + } + redirects.replace(total); + { + let mut i = page.navigation.request.info.take(); + i + .add_event(EVENT_COMPLETED.to_string()) + .set_mime(None) + .set_request(Some(uri.to_string())) + .set_size(None); - // Asynchronously read [IOStream](https://docs.gtk.org/gio/class.IOStream.html) - // to local [MemoryInputStream](https://docs.gtk.org/gio/class.MemoryInputStream.html) - // show bytes count in loading widget, validate max size for incoming data - // * no dependency of Gemini library here, feel free to use any other `IOStream` processor - memory_input_stream::from_stream_async( - connection.stream(), - Priority::DEFAULT, - cancellable.clone(), - ( - 0x400, // 1024 bytes per chunk, optional step for images download tracking - 0xA00000 // 10M bytes max to prevent memory overflow if server play with promises - ), - ( - move |_, total| status.set_description(Some(&format!("Download: {total} bytes"))), - { - move | result | match result { - Ok((memory_input_stream, _)) => { - Pixbuf::from_stream_async( - &memory_input_stream, - Some(&cancellable), - move |result| { - match result { - Ok(buffer) => { - page.set_title(&crate::tool::uri_to_title(&uri)); - page.content.to_image(&Texture::for_pixbuf(&buffer)); - { - let mut i = page.navigation.request.info.borrow_mut(); - i - .add_event(EVENT_COMPLETED.to_string()) - .set_mime(Some(success.mime().to_string())) - .set_request(Some(uri.to_string())) - .set_size(Some(buffer.byte_length())); - } - } - Err(e) => { - let s = page.content.to_status_failure(); - s.set_description(Some(e.message())); - page.set_title(&s.title()); - { - let mut i = page.navigation.request.info.borrow_mut(); - i - .add_event(EVENT_COMPLETED.to_string()) - .set_mime(Some(success.mime().to_string())) - .set_request(Some(uri.to_string())) - .set_size(None); - } - } - } - page.set_progress(0.0); - if is_snap_history { - page.snap_history(); - } - redirects.replace(0); // reset - }, - ) - } - Err(e) => { - let s = page.content.to_status_failure(); - s.set_description(Some(&e.to_string())); - page.set_progress(0.0); - page.set_title(&s.title()); - if is_snap_history { - page.snap_history(); - } - redirects.replace(0); // reset - { - let mut i = page.navigation.request.info.borrow_mut(); - i - .add_event(EVENT_COMPLETED.to_string()) - .set_mime(Some(success.mime().to_string())) - .set_request(Some(uri.to_string())) - .set_size(None); - } - } - } - } - ), - ) + page.navigation.request.info.replace(i.into_redirect()); + } + page.item_action.load.activate(Some(&t), false); + } } - mime => { - let s = page - .content - .to_status_mime(mime, Some((&page.item_action, &uri))); - s.set_description(Some(&format!("Content type `{mime}` yet not supported"))); - page.set_progress(0.0); - page.set_title(&s.title()); - if is_snap_history { - page.snap_history(); - } - redirects.replace(0); // reset - { - let mut i = page.navigation.request.info.borrow_mut(); - i - .add_event(EVENT_COMPLETED.to_string()) - .set_mime(Some(mime.to_string())) - .set_request(Some(uri.to_string())) - .set_size(None); - } - }, - } - }, - // https://geminiprotocol.net/docs/protocol-specification.gmi#status-30-temporary-redirection - // https://geminiprotocol.net/docs/protocol-specification.gmi#status-31-permanent-redirection - Response::Redirect(redirect) => match redirect.to_uri(&uri) { - Ok(target) => { - // Increase client redirection counter - let total = redirects.take() + 1; - // > Client MUST limit the number of redirections they follow to 5 redirections - // > https://geminiprotocol.net/docs/protocol-specification.gmi#redirection - if total > 5 { + Err(e) => { let s = page.content.to_status_failure(); - s.set_description(Some("Redirection limit reached")); + s.set_description(Some(&e.to_string())); page.set_progress(0.0); page.set_title(&s.title()); redirects.replace(0); // reset update_page_info(&page, &uri, EVENT_COMPLETED); - // Disallow external redirection by default as potentially unsafe - // even not specified, require follow confirmation @TODO optional - } else if uri.host() != target.host() { - let u = target.to_string(); - let s = page.content.to_status_failure(); - let t = "External redirection"; - s.set_title(t); - s.set_icon_name(Some("dialog-warning-symbolic")); - s.set_description(Some(&u)); - s.set_child(Some(&{ - let b = gtk::Button::builder() - .css_classes(["suggested-action"]) - .halign(gtk::Align::Center) - .label("Follow") - .build(); - b.connect_clicked({ - let p = page.clone(); - move |_| p.item_action.load.activate(Some(&u), false) - }); - b - })); - page.set_progress(0.0); - page.set_title(t); - redirects.replace(0); // reset - update_page_info(&page, &uri, EVENT_COMPLETED); - } else { - let t = target.to_string(); - if matches!(redirect, Redirect::Permanent { .. }) { - page.navigation.set_request(&t); - } - redirects.replace(total); - { - let mut i = page.navigation.request.info.take(); - i - .add_event(EVENT_COMPLETED.to_string()) - .set_mime(None) - .set_request(Some(uri.to_string())) - .set_size(None); - - page.navigation.request.info.replace(i.into_redirect()); - } - page.item_action.load.activate(Some(&t), false); } } - Err(e) => { - let s = page.content.to_status_failure(); - s.set_description(Some(&e.to_string())); - page.set_progress(0.0); - page.set_title(&s.title()); - redirects.replace(0); // reset - update_page_info(&page, &uri, EVENT_COMPLETED); - } - } - Response::Certificate(ref certificate) => match certificate { - // https://geminiprotocol.net/docs/protocol-specification.gmi#status-60 - Certificate::Required { message } | - // https://geminiprotocol.net/docs/protocol-specification.gmi#status-61-certificate-not-authorized - Certificate::NotAuthorized { message } | - // https://geminiprotocol.net/docs/protocol-specification.gmi#status-62-certificate-not-valid - Certificate::NotValid { message } => { - let s = page.content.to_status_identity(); - s.set_description(Some(message.as_ref().unwrap_or(&certificate.to_string()))); - page.set_progress(0.0); - page.set_title(&s.title()); - if is_snap_history { - page.snap_history(); - } - redirects.replace(0); // reset - update_page_info(&page, &uri, EVENT_COMPLETED); - } - } - Response::Failure(failure) => match failure { - Failure::Temporary(ref temporary) => match temporary { - Temporary::CgiError { message } | - Temporary::Default { message } | - Temporary::ProxyError { message } | - Temporary::ServerUnavailable { message } | - Temporary::SlowDown { message } => { - let s = page.content.to_status_failure(); - s.set_description(Some(message.as_ref().unwrap_or(&temporary.to_string()))); + Response::Certificate(ref certificate) => match certificate { + // https://geminiprotocol.net/docs/protocol-specification.gmi#status-60 + Certificate::Required { message } | + // https://geminiprotocol.net/docs/protocol-specification.gmi#status-61-certificate-not-authorized + Certificate::NotAuthorized { message } | + // https://geminiprotocol.net/docs/protocol-specification.gmi#status-62-certificate-not-valid + Certificate::NotValid { message } => { + let s = page.content.to_status_identity(); + s.set_description(Some(message.as_ref().unwrap_or(&certificate.to_string()))); page.set_progress(0.0); page.set_title(&s.title()); if is_snap_history { @@ -573,28 +568,47 @@ fn handle( } redirects.replace(0); // reset update_page_info(&page, &uri, EVENT_COMPLETED); - if let Some(callback) = on_failure { - callback() - } } } - Failure::Permanent(ref permanent) => match permanent { - Permanent::BadRequest { message } | - Permanent::Default { message } | - Permanent::Gone { message } | - Permanent::NotFound { message } | - Permanent::ProxyRequestRefused { message } => { - let s = page.content.to_status_failure(); - s.set_description(Some(message.as_ref().unwrap_or(&permanent.to_string()))); - page.set_progress(0.0); - page.set_title(&s.title()); - if is_snap_history { - page.snap_history(); + Response::Failure(failure) => match failure { + Failure::Temporary(ref temporary) => match temporary { + Temporary::CgiError { message } | + Temporary::Default { message } | + Temporary::ProxyError { message } | + Temporary::ServerUnavailable { message } | + Temporary::SlowDown { message } => { + let s = page.content.to_status_failure(); + s.set_description(Some(message.as_ref().unwrap_or(&temporary.to_string()))); + page.set_progress(0.0); + page.set_title(&s.title()); + if is_snap_history { + page.snap_history(); + } + redirects.replace(0); // reset + update_page_info(&page, &uri, EVENT_COMPLETED); + if let Some(callback) = on_failure { + callback() + } } - redirects.replace(0); // reset - update_page_info(&page, &uri, EVENT_COMPLETED); - if let Some(callback) = on_failure { - callback() + } + Failure::Permanent(ref permanent) => match permanent { + Permanent::BadRequest { message } | + Permanent::Default { message } | + Permanent::Gone { message } | + Permanent::NotFound { message } | + Permanent::ProxyRequestRefused { message } => { + let s = page.content.to_status_failure(); + s.set_description(Some(message.as_ref().unwrap_or(&permanent.to_string()))); + page.set_progress(0.0); + page.set_title(&s.title()); + if is_snap_history { + page.snap_history(); + } + redirects.replace(0); // reset + update_page_info(&page, &uri, EVENT_COMPLETED); + if let Some(callback) = on_failure { + callback() + } } } } diff --git a/src/app/browser/window/tab/item/page/navigation/request/info.rs b/src/app/browser/window/tab/item/page/navigation/request/info.rs index b79ea045..6bae0984 100644 --- a/src/app/browser/window/tab/item/page/navigation/request/info.rs +++ b/src/app/browser/window/tab/item/page/navigation/request/info.rs @@ -1,14 +1,11 @@ mod dialog; - -// Public dependencies - -pub mod event; -pub use event::Event; - -// Local dependencies +mod event; +mod socket; use dialog::Dialog; -use gtk::{gio::NetworkAddress, prelude::IsA}; +use event::Event; +use gtk::{gio::SocketAddress, prelude::IsA}; +use socket::Socket; /// Common, shared `Page` information holder /// * used for the Information dialog window on request indicator activate @@ -24,13 +21,13 @@ pub struct Info { /// Hold redirections chain with handled details /// * the `referrer` member name is reserved for other protocols redirect: Option>, - /// Optional remote host details - /// * useful also for geo-location feature - remote: Option, /// Key to relate data collected with the specific request request: Option, /// Hold page content size size: Option, + /// Optional socket details + /// * useful also for geo-location feature + socket: Option, } impl Info { @@ -43,9 +40,9 @@ impl Info { is_deprecated: false, mime: None, redirect: None, - remote: None, request: None, size: None, + socket: None, } } @@ -86,8 +83,15 @@ impl Info { self } - pub fn set_remote(&mut self, remote: Option) -> &mut Self { - self.remote = remote; + pub fn set_socket( + &mut self, + local_address: SocketAddress, + remote_address: SocketAddress, + ) -> &mut Self { + self.socket = Some(Socket { + local_address, + remote_address, + }); self.is_deprecated = false; self } diff --git a/src/app/browser/window/tab/item/page/navigation/request/info/socket.rs b/src/app/browser/window/tab/item/page/navigation/request/info/socket.rs new file mode 100644 index 00000000..db77eec2 --- /dev/null +++ b/src/app/browser/window/tab/item/page/navigation/request/info/socket.rs @@ -0,0 +1,6 @@ +use gtk::gio::SocketAddress; + +pub struct Socket { + pub local_address: SocketAddress, + pub remote_address: SocketAddress, +}