From 7f7b412220822d1e6a396e0174d1f577cfce839f Mon Sep 17 00:00:00 2001 From: Mathijs van Veluw Date: Sun, 23 Nov 2025 21:50:31 +0100 Subject: [PATCH] Fix icon redirect caching (#6487) As reported in #6477, redirection of favicon's didn't allowed caching. This commit fixes this by adding the `Cached` wrapper around the response. It will use the same TTL's used for downloading icon's locally. Also removed `_` as valid domain character, these should not be used in FQDN's at all. Those only serve as special chars used in domain labels, mostly used in SRV or TXT records. Fixes #6477 Signed-off-by: BlackDex --- src/api/icons.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/api/icons.rs b/src/api/icons.rs index 4e2aef1c..5003a421 100644 --- a/src/api/icons.rs +++ b/src/api/icons.rs @@ -82,19 +82,19 @@ static ICON_SIZE_REGEX: LazyLock = LazyLock::new(|| Regex::new(r"(?x)(\d+ // It is used to prevent sending a specific header which breaks icon downloads. // If this function needs to be renamed, also adjust the code in `util.rs` #[get("//icon.png")] -fn icon_external(domain: &str) -> Option { +fn icon_external(domain: &str) -> Cached> { if !is_valid_domain(domain) { warn!("Invalid domain: {domain}"); - return None; + return Cached::ttl(None, CONFIG.icon_cache_negttl(), true); } if should_block_address(domain) { warn!("Blocked address: {domain}"); - return None; + return Cached::ttl(None, CONFIG.icon_cache_negttl(), true); } let url = CONFIG._icon_service_url().replace("{}", domain); - match CONFIG.icon_redirect_code() { + let redir = match CONFIG.icon_redirect_code() { 301 => Some(Redirect::moved(url)), // legacy permanent redirect 302 => Some(Redirect::found(url)), // legacy temporary redirect 307 => Some(Redirect::temporary(url)), @@ -103,7 +103,8 @@ fn icon_external(domain: &str) -> Option { error!("Unexpected redirect code {}", CONFIG.icon_redirect_code()); None } - } + }; + Cached::ttl(redir, CONFIG.icon_cache_ttl(), true) } #[get("//icon.png")] @@ -141,7 +142,7 @@ async fn icon_internal(domain: &str) -> Cached<(ContentType, Vec)> { /// This does some manual checks and makes use of Url to do some basic checking. /// domains can't be larger then 63 characters (not counting multiple subdomains) according to the RFC's, but we limit the total size to 255. fn is_valid_domain(domain: &str) -> bool { - const ALLOWED_CHARS: &str = "_-."; + const ALLOWED_CHARS: &str = "-."; // If parsing the domain fails using Url, it will not work with reqwest. if let Err(parse_error) = url::Url::parse(format!("https://{domain}").as_str()) {