114 """REST API of SearXNG's favicon proxy service
118 /favicon_proxy?authority=<...>&h=<...>
121 Domain name :rfc:`3986` / see :py:obj:`favicon_url`
124 HMAC :rfc:`2104`, build up from the :ref:`server.secret_key <settings
128 authority = sxng_request.args.get(
'authority')
131 if not authority
or "/" in authority:
138 sxng_request.args.get(
'h',
''),
142 resolver = sxng_request.preferences.get_value(
'favicon_resolver')
144 if not resolver
or resolver
not in CFG.resolver_map.keys():
149 if data
is not None and mime
is not None:
150 resp = flask.Response(data, mimetype=mime)
151 resp.headers[
'Cache-Control'] = f
"max-age={CFG.max_age}"
155 theme = sxng_request.preferences.get_value(
"theme")
156 fav, mimetype = CFG.favicon(theme=theme)
157 return flask.send_from_directory(fav.parent, fav.name, mimetype=mimetype)
160def search_favicon(resolver: str, authority: str) -> tuple[
None | bytes,
None | str]:
161 """Sends the request to the favicon resolver and returns a tuple for the
162 favicon. The tuple consists of ``(data, mime)``, if the resolver has not
163 determined a favicon, both values are ``None``.
166 Binary data of the favicon.
169 Mime type of the favicon.
173 data, mime = (
None,
None)
175 func = CFG.get_resolver(resolver)
180 data_mime = cache.CACHE(resolver, authority)
181 if data_mime
is not None:
185 data, mime = func(authority, timeout=CFG.resolver_timeout)
186 if data
is None or mime
is None:
187 data, mime = (
None,
None)
189 except (HTTPError, SearxEngineResponseException):
192 cache.CACHE.set(resolver, authority, mime, data)
197 """Function to generate the image URL used for favicons in SearXNG's result
198 lists. The ``authority`` argument (aka netloc / :rfc:`3986`) is usually a
199 (sub-) domain name. This function is used in the HTML (jinja) templates.
203 <div class="favicon">
204 <img src="{{ favicon_url(result.parsed_url.netloc) }}">
207 The returned URL is a route to :py:obj:`favicon_proxy` REST API.
209 If the favicon is already in the cache, the returned URL is a `data URL`_
210 (something like ``data:image/png;base64,...``). By generating a data url from
211 the :py:obj:`.cache.FaviconCache`, additional HTTP roundtripps via the
212 :py:obj:`favicon_proxy` are saved. However, it must also be borne in mind
213 that data urls are not cached in the client (web browser).
215 .. _data URL: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs
219 resolver = sxng_request.preferences.get_value(
'favicon_resolver')
221 if not resolver
or resolver
not in CFG.resolver_map.keys():
224 data_mime = cache.CACHE(resolver, authority)
226 if data_mime == (
None,
None):
228 theme = sxng_request.preferences.get_value(
"theme")
229 return CFG.favicon_data_url(theme=theme)
231 if data_mime
is not None:
232 data, mime = data_mime
233 return f
"data:{mime};base64,{str(base64.b64encode(data), 'utf-8')}"
235 h = new_hmac(CFG.secret_key, authority.encode())
236 proxy_url = flask.url_for(
'favicon_proxy')
237 query = urllib.parse.urlencode({
"authority": authority,
"h": h})
238 return f
"{proxy_url}?{query}"