113    """REST API of SearXNG's favicon proxy service 
  117        /favicon_proxy?authority=<...>&h=<...> 
  120      Domain name :rfc:`3986` / see :py:obj:`favicon_url` 
  123      HMAC :rfc:`2104`, build up from the :ref:`server.secret_key <settings 
  127    authority = sxng_request.args.get(
'authority')
 
  130    if not authority 
or "/" in authority:
 
  137        sxng_request.args.get(
'h', 
''),
 
  141    resolver = sxng_request.preferences.get_value(
'favicon_resolver')  
 
  143    if not resolver 
or resolver 
not in CFG.resolver_map.keys():
 
  148    if data 
is not None and mime 
is not None:
 
  149        resp = flask.Response(data, mimetype=mime)  
 
  150        resp.headers[
'Cache-Control'] = f
"max-age={CFG.max_age}" 
  154    theme = sxng_request.preferences.get_value(
"theme")  
 
  155    fav, mimetype = CFG.favicon(theme=theme)
 
  156    return flask.send_from_directory(fav.parent, fav.name, mimetype=mimetype)
 
 
  159def search_favicon(resolver: str, authority: str) -> tuple[
None | bytes, 
None | str]:
 
  160    """Sends the request to the favicon resolver and returns a tuple for the 
  161    favicon.  The tuple consists of ``(data, mime)``, if the resolver has not 
  162    determined a favicon, both values are ``None``. 
  165      Binary data of the favicon. 
  168      Mime type of the favicon. 
  172    data, mime = (
None, 
None)
 
  174    func = CFG.get_resolver(resolver)
 
  179    data_mime = cache.CACHE(resolver, authority)
 
  180    if data_mime 
is not None:
 
  184        data, mime = func(authority, timeout=CFG.resolver_timeout)
 
  185        if data 
is None or mime 
is None:
 
  186            data, mime = (
None, 
None)
 
  188    except (HTTPError, SearxEngineResponseException):
 
  191    cache.CACHE.set(resolver, authority, mime, data)
 
 
  196    """Function to generate the image URL used for favicons in SearXNG's result 
  197    lists.  The ``authority`` argument (aka netloc / :rfc:`3986`) is usually a 
  198    (sub-) domain name.  This function is used in the HTML (jinja) templates. 
  202       <div class="favicon"> 
  203          <img src="{{ favicon_url(result.parsed_url.netloc) }}"> 
  206    The returned URL is a route to :py:obj:`favicon_proxy` REST API. 
  208    If the favicon is already in the cache, the returned URL is a `data URL`_ 
  209    (something like ``data:image/png;base64,...``).  By generating a data url from 
  210    the :py:obj:`.cache.FaviconCache`, additional HTTP roundtripps via the 
  211    :py:obj:`favicon_proxy` are saved.  However, it must also be borne in mind 
  212    that data urls are not cached in the client (web browser). 
  214    .. _data URL: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs 
  218    resolver = sxng_request.preferences.get_value(
'favicon_resolver')  
 
  220    if not resolver 
or resolver 
not in CFG.resolver_map.keys():
 
  223    data_mime = cache.CACHE(resolver, authority)
 
  225    if data_mime == (
None, 
None):
 
  227        theme = sxng_request.preferences.get_value(
"theme")  
 
  228        return CFG.favicon_data_url(theme=theme)
 
  230    if data_mime 
is not None:
 
  231        data, mime = data_mime
 
  232        return f
"data:{mime};base64,{str(base64.b64encode(data), 'utf-8')}"   
  234    h = new_hmac(CFG.secret_key, authority.encode())
 
  235    proxy_url = flask.url_for(
'favicon_proxy')
 
  236    query = urllib.parse.urlencode({
"authority": authority, 
"h": h})
 
  237    return f
"{proxy_url}?{query}"