From 76eac192272acf77763d0c619cd78118650748cf Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Tue, 29 Oct 2019 22:29:22 +0000 Subject: [PATCH] add internal query page for request timeouts --- content/fetchers/about.c | 153 +++++++++++++++++++++++++++++++++++++-- desktop/browser_window.c | 131 ++++++++++++++++++++++++++++----- resources/FatMessages | 9 +++ resources/internal.css | 38 ++++++++++ utils/corestringlist.h | 2 + 5 files changed, 309 insertions(+), 24 deletions(-) diff --git a/content/fetchers/about.c b/content/fetchers/about.c index df514108c..d8a5fa793 100644 --- a/content/fetchers/about.c +++ b/content/fetchers/about.c @@ -89,6 +89,11 @@ static const char *authentication_description_fallback = "The site %s is request */ static const char *privacy_description_fallback = "A privacy error occurred while communicating with %s this may be a site configuration error or an attempt to steal private information (passwords, messages or credit cards)"; +/** + * timeout query description if messages fails to retrieve usable text + */ +static const char *timeout_description_fallback = "A connection to %s could not be established. The site may be temporarily unavailable or too busy to respond."; + /** * issue fetch callbacks with locking */ @@ -939,15 +944,18 @@ fetch_about_query_auth_handler_aborted: /** - * generate the description of the privacy query + * generate a query description */ -static nserror get_privacy_description(struct nsurl *url, char **out_str) +static nserror +get_query_description(struct nsurl *url, + const char *key, + const char *fallback, + char **out_str) { nserror res; char *url_s; size_t url_l; char *str = NULL; - const char *key = "PrivacyDescription"; /* get the host in question */ res = nsurl_get(url, NSURL_HOST, &url_s, &url_l); @@ -967,10 +975,10 @@ static nserror get_privacy_description(struct nsurl *url, char **out_str) * fall back to basic english. */ int slen; - slen = snprintf(str, 0, privacy_description_fallback, url_s) + 1; + slen = snprintf(str, 0, fallback, url_s) + 1; str = malloc(slen); if (str != NULL) { - snprintf(str, slen, privacy_description_fallback, url_s); + snprintf(str, slen, fallback, url_s); } } @@ -1049,7 +1057,10 @@ static bool fetch_about_query_privacy_handler(struct fetch_about_context *ctx) goto fetch_about_query_ssl_handler_aborted; } - res = get_privacy_description(siteurl, &description); + res = get_query_description(siteurl, + "PrivacyDescription", + privacy_description_fallback, + &description); if (res == NSERROR_OK) { res = ssenddataf(ctx, "

%s

", description); free(description); @@ -1105,6 +1116,129 @@ fetch_about_query_ssl_handler_aborted: } +/** + * Handler to generate about scheme timeout query page + * + * \param ctx The fetcher context. + * \return true if handled false if aborted. + */ +static bool fetch_about_query_timeout_handler(struct fetch_about_context *ctx) +{ + nserror res; + char *url_s; + size_t url_l; + const char *reason = ""; + const char *title; + struct nsurl *siteurl = NULL; + char *description = NULL; + const struct fetch_multipart_data *curmd; /* mutipart data iterator */ + + /* extract parameters from multipart post data */ + curmd = ctx->multipart; + while (curmd != NULL) { + if (strcmp(curmd->name, "siteurl") == 0) { + res = nsurl_create(curmd->value, &siteurl); + if (res != NSERROR_OK) { + return fetch_about_srverror(ctx); + } + } else if (strcmp(curmd->name, "reason") == 0) { + reason = curmd->value; + } + curmd = curmd->next; + } + + if (siteurl == NULL) { + return fetch_about_srverror(ctx); + } + + /* content is going to return ok */ + fetch_set_http_code(ctx->fetchh, 200); + + /* content type */ + if (fetch_about_send_header(ctx, "Content-Type: text/html; charset=utf-8")) { + goto fetch_about_query_timeout_handler_aborted; + } + + title = messages_get("TimeoutTitle"); + res = ssenddataf(ctx, + "\n\n" + "%s\n" + "\n" + "\n" + "\n" + "

%s

\n", + title, title); + if (res != NSERROR_OK) { + goto fetch_about_query_timeout_handler_aborted; + } + + res = ssenddataf(ctx, + "
"); + if (res != NSERROR_OK) { + goto fetch_about_query_timeout_handler_aborted; + } + + res = get_query_description(siteurl, + "TimeoutDescription", + timeout_description_fallback, + &description); + if (res == NSERROR_OK) { + res = ssenddataf(ctx, "

%s

", description); + free(description); + if (res != NSERROR_OK) { + goto fetch_about_query_timeout_handler_aborted; + } + } + res = ssenddataf(ctx, "

%s

", reason); + if (res != NSERROR_OK) { + goto fetch_about_query_timeout_handler_aborted; + } + + res = ssenddataf(ctx, + "
" + "" + "" + "
", + messages_get("Backtoprevious"), + messages_get("TryAgain")); + if (res != NSERROR_OK) { + goto fetch_about_query_timeout_handler_aborted; + } + + res = nsurl_get(siteurl, NSURL_COMPLETE, &url_s, &url_l); + if (res != NSERROR_OK) { + url_s = strdup(""); + } + res = ssenddataf(ctx, + "", + url_s); + free(url_s); + if (res != NSERROR_OK) { + goto fetch_about_query_timeout_handler_aborted; + } + + res = ssenddataf(ctx, "
\n\n"); + if (res != NSERROR_OK) { + goto fetch_about_query_timeout_handler_aborted; + } + + fetch_about_send_finished(ctx); + + nsurl_unref(siteurl); + + return true; + +fetch_about_query_timeout_handler_aborted: + nsurl_unref(siteurl); + + return false; +} + + /* Forward declaration because this handler requires the handler table. */ static bool fetch_about_about_handler(struct fetch_about_context *ctx); @@ -1211,6 +1345,13 @@ struct about_handlers about_handler_list[] = { NULL, fetch_about_query_privacy_handler, true + }, + { + "query/timeout", + SLEN("query/timeout"), + NULL, + fetch_about_query_timeout_handler, + true } }; diff --git a/desktop/browser_window.c b/desktop/browser_window.c index a6b77b08b..5f7c2cc4b 100644 --- a/desktop/browser_window.c +++ b/desktop/browser_window.c @@ -1181,6 +1181,41 @@ browser_window__handle_bad_certs(struct browser_window *bw, } +/** + * Handle a timeout during a fetch + */ +static nserror +browser_window__handle_timeout(struct browser_window *bw, nsurl *url) +{ + struct browser_fetch_parameters params; + nserror err; + + memset(¶ms, 0, sizeof(params)); + + params.url = nsurl_ref(corestring_nsurl_about_query_timeout); + params.referrer = nsurl_ref(url); + params.flags = BW_NAVIGATE_HISTORY | BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE | BW_NAVIGATE_INTERNAL; + + err = fetch_multipart_data_new_kv(¶ms.post_multipart, + "siteurl", + nsurl_access(url)); + if (err != NSERROR_OK) { + goto out; + } + + /* Now we issue the fetch */ + bw->internal_nav = true; + err = browser_window__navigate_internal(bw, ¶ms); + if (err != NSERROR_OK) { + goto out; + } + + out: + browser_window__free_fetch_parameters(¶ms); + return err; +} + + /** * Handle errors during content fetch */ @@ -1208,6 +1243,9 @@ browser_window__handle_error(struct browser_window *bw, case NSERROR_BAD_REDIRECT: /* The message is already filled out */ break; + case NSERROR_TIMEOUT: + do_warning = false; + break; default: if (message == NULL) { message = messages_get_errorcode(code); @@ -1239,6 +1277,9 @@ browser_window__handle_error(struct browser_window *bw, case NSERROR_BAD_CERTS: res = browser_window__handle_bad_certs(bw, url); break; + case NSERROR_TIMEOUT: + res = browser_window__handle_timeout(bw, url); + break; default: break; } @@ -2368,6 +2409,8 @@ is_internal_navigate_url(nsurl *url) is_internal = true; } else if (path == corestring_lwc_query_ssl) { is_internal = true; + } else if (path == corestring_lwc_query_timeout) { + is_internal = true; } } lwc_string_unref(path); @@ -3338,12 +3381,16 @@ browser_window_navigate(struct browser_window *bw, return error; } + +/** + * Internal navigation handler for normal fetches + */ static nserror -browser_window__navigate_internal_real(struct browser_window *bw, - struct browser_fetch_parameters *params) +navigate_internal_real(struct browser_window *bw, + struct browser_fetch_parameters *params) { uint32_t fetch_flags = 0; - bool fetch_is_post = (params->post_urlenc != NULL || params->post_multipart != NULL); + bool fetch_is_post; llcache_post_data post; hlcache_child_context child; nserror res; @@ -3351,6 +3398,8 @@ browser_window__navigate_internal_real(struct browser_window *bw, NSLOG(netsurf, INFO, "Loading '%s'", nsurl_access(params->url)); + fetch_is_post = (params->post_urlenc != NULL || params->post_multipart != NULL); + /* Clear SSL info for load */ bw->loading_ssl_info.num = 0; @@ -3424,6 +3473,7 @@ browser_window__navigate_internal_real(struct browser_window *bw, return res; } + /** * Internal navigation handler for the authentication query handler * @@ -3431,8 +3481,8 @@ browser_window__navigate_internal_real(struct browser_window *bw, * then we deal with that, otherwise we pass it on to the about: handler */ static nserror -browser_window__navigate_internal_query_auth(struct browser_window *bw, - struct browser_fetch_parameters *params) +navigate_internal_query_auth(struct browser_window *bw, + struct browser_fetch_parameters *params) { char *userpass = NULL; const char *username, *password, *realm, *siteurl; @@ -3447,7 +3497,7 @@ browser_window__navigate_internal_query_auth(struct browser_window *bw, if (!(is_login || is_cancel)) { /* This is a request, so pass it on */ - return browser_window__navigate_internal_real(bw, params); + return navigate_internal_real(bw, params); } if (is_cancel) { @@ -3497,7 +3547,7 @@ browser_window__navigate_internal_query_auth(struct browser_window *bw, /* Finally navigate to the original loading parameters */ bw->internal_nav = false; - return browser_window__navigate_internal_real(bw, &bw->loading_parameters); + return navigate_internal_real(bw, &bw->loading_parameters); } @@ -3508,8 +3558,8 @@ browser_window__navigate_internal_query_auth(struct browser_window *bw, * then we deal with that, otherwise we pass it on to the about: handler */ static nserror -browser_window__navigate_internal_query_ssl(struct browser_window *bw, - struct browser_fetch_parameters *params) +navigate_internal_query_ssl(struct browser_window *bw, + struct browser_fetch_parameters *params) { bool is_proceed = false, is_back = false; @@ -3520,23 +3570,64 @@ browser_window__navigate_internal_query_ssl(struct browser_window *bw, if (!(is_proceed || is_back)) { /* This is a request, so pass it on */ - return browser_window__navigate_internal_real(bw, params); + return navigate_internal_real(bw, params); } return browser_window__handle_ssl_query_response(is_proceed, bw); } +/** + * Internal navigation handler for the timeout query page. + * + * If the parameters indicate we're processing a *response* from the handler + * then we deal with that, otherwise we pass it on to the about: handler + */ +static nserror +navigate_internal_query_timeout(struct browser_window *bw, + struct browser_fetch_parameters *params) +{ + bool is_retry = false, is_back = false; + + NSLOG(netsurf, INFO, "bw:%p params:%p", bw, params); + + assert(params->post_multipart != NULL); + + is_retry = fetch_multipart_data_find(params->post_multipart, "retry") != NULL; + is_back = fetch_multipart_data_find(params->post_multipart, "back") != NULL; + + if (is_back) { + /* do a rough-and-ready nav to the old 'current' + * parameters, with any post data stripped away + */ + return browser_window__reload_current_parameters(bw); + } + + if (is_retry) { + /* Finally navigate to the original loading parameters */ + bw->internal_nav = false; + return navigate_internal_real(bw, &bw->loading_parameters); + } + + return navigate_internal_real(bw, params); +} + + +/** + * dispatch to internal query handlers or normal navigation + * + * Here we determine if we're navigating to an internal query URI and + * if so, what we need to do about it. + * + * \note these check must match those in is_internal_navigate_url() + * + * If we're not, then we just move on to the real navigate. + */ nserror browser_window__navigate_internal(struct browser_window *bw, struct browser_fetch_parameters *params) { lwc_string *scheme, *path; - /* Here we determine if we're navigating to an internal query URI - * and if so, what we need to do about it. - * - * If we're not, then we just move on to the real navigate. - */ /* All our special URIs are in the about: scheme */ scheme = nsurl_get_component(params->url, NSURL_SCHEME); @@ -3550,18 +3641,22 @@ browser_window__navigate_internal(struct browser_window *bw, path = nsurl_get_component(params->url, NSURL_PATH); if (path == corestring_lwc_query_auth) { lwc_string_unref(path); - return browser_window__navigate_internal_query_auth(bw, params); + return navigate_internal_query_auth(bw, params); } if (path == corestring_lwc_query_ssl) { lwc_string_unref(path); - return browser_window__navigate_internal_query_ssl(bw, params); + return navigate_internal_query_ssl(bw, params); + } + if (path == corestring_lwc_query_timeout) { + lwc_string_unref(path); + return navigate_internal_query_timeout(bw, params); } lwc_string_unref(path); /* Fall through to a normal about: fetch */ normal_fetch: - return browser_window__navigate_internal_real(bw, params); + return navigate_internal_real(bw, params); } diff --git a/resources/FatMessages b/resources/FatMessages index d1ecd9dd1..aa2d7a2d1 100644 --- a/resources/FatMessages +++ b/resources/FatMessages @@ -1087,6 +1087,15 @@ en.all.SSLCertErrRevoked:The certificate has been revoked by the issuer. en.all.SSLCertErrHostnameMismatch:The certificate is for a different host than the server +# Timeout error interface +# ======================= +# +en.all.TimeoutTitle:Connection timed out +en.all.TimeoutDescription: A connection to %s could not be established. The site may be temporarily unavailable or too busy to respond. +en.all.Backtoprevious: Back +en.all.TryAgain: Try Again + + # SSL certificate viewer # ====================== # diff --git a/resources/internal.css b/resources/internal.css index e43d18309..14b47cfa9 100644 --- a/resources/internal.css +++ b/resources/internal.css @@ -343,3 +343,41 @@ body#privacy div#buttons { body#privacy div#buttons input#back { margin-right: 1em; } + +/* + * timeout query styling + */ + +body#timeout { + max-width: 45em; +} + +body#timeout h1 { + padding: 0.8em 0.4em 0.5em 0.4em; + border-bottom: 0.1em solid #444; + margin: 0 0 1.3em 0; + background: #c55; + color: white; +} + +body#timeout form { + /* Just to center the form on the page */ + margin: 0 auto; + /* To see the outline of the form */ + padding: 1em; + border: 1px solid #CCC; + border-radius: 1em; +} + +body#timeout form div + div { + margin-top: 1em; +} + +body#timeout div#buttons { + text-align: right; + margin-right: 1em; +} + +body#timeout div#buttons input#back { + margin-right: 1em; +} diff --git a/utils/corestringlist.h b/utils/corestringlist.h index f0d39c605..e7516b18a 100644 --- a/utils/corestringlist.h +++ b/utils/corestringlist.h @@ -148,6 +148,7 @@ CORESTRING_LWC_VALUE(no_cache, "no-cache"); CORESTRING_LWC_VALUE(no_store, "no-store"); CORESTRING_LWC_VALUE(query_auth, "query/auth"); CORESTRING_LWC_VALUE(query_ssl, "query/ssl"); +CORESTRING_LWC_VALUE(query_timeout, "query/timeout"); /* mime types */ CORESTRING_LWC_VALUE(multipart_form_data, "multipart/form-data"); @@ -360,6 +361,7 @@ CORESTRING_DOM_VALUE(html_namespace, "http://www.w3.org/1999/xhtml"); CORESTRING_NSURL(about_blank, "about:blank"); CORESTRING_NSURL(about_query_ssl, "about:query/ssl"); CORESTRING_NSURL(about_query_auth, "about:query/auth"); +CORESTRING_NSURL(about_query_timeout, "about:query/timeout"); #undef CORESTRING_LWC_STRING #undef CORESTRING_DOM_STRING