Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Icons/IconsSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ public class IconsSettings
public virtual bool CacheEnabled { get; set; }
public virtual int CacheHours { get; set; }
public virtual long? CacheSizeLimit { get; set; }
public virtual bool GoogleFaviconEnabled { get; set; }
}
50 changes: 49 additions & 1 deletion src/Icons/Services/IconFetchingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,74 @@ namespace Bit.Icons.Services;

public class IconFetchingService : IIconFetchingService
{
private const int GoogleFaviconSize = 64;

private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<IIconFetchingService> _logger;
private readonly IHtmlParser _parser;
private readonly IUriService _uriService;
private readonly IconsSettings _iconsSettings;

public IconFetchingService(ILogger<IIconFetchingService> logger, IHttpClientFactory httpClientFactory, IHtmlParser parser, IUriService uriService)
public IconFetchingService(
ILogger<IIconFetchingService> logger,
IHttpClientFactory httpClientFactory,
IHtmlParser parser,
IUriService uriService,
IconsSettings iconsSettings)
{
_logger = logger;
_httpClientFactory = httpClientFactory;
_parser = parser;
_uriService = uriService;
_iconsSettings = iconsSettings;
}

public async Task<Icon?> GetIconAsync(string domain)
{
if (_iconsSettings.GoogleFaviconEnabled)
{
var googleIcon = await GetGoogleFaviconAsync(domain);
if (googleIcon != null)
{
return googleIcon;
}
}

var domainIcons = await DomainIcons.FetchAsync(domain, _logger, _httpClientFactory, _parser, _uriService);
var result = domainIcons.Where(result => result != null).FirstOrDefault();
return result ?? await GetFaviconAsync(domain);
}

private async Task<Icon?> GetGoogleFaviconAsync(string domain)
{
// Google's undocumented favicon endpoint. Returns a PNG, 301-redirects to
// t0.gstatic.com/faviconV2.
var googleUriBuilder = new UriBuilder
{
Scheme = "https",
Host = "www.google.com",
Path = "/s2/favicons",
Query = $"domain={Uri.EscapeDataString(domain)}&sz={GoogleFaviconSize}"
};

if (!googleUriBuilder.TryBuild(out var googleUri))
{
return null;
}

try
{
// A 404 from Google indicates no favicon exists for the domain; the existing
// IconLink/IconHttpRequest pipeline propagates that as a null return.
return await new IconLink(googleUri!).FetchAsync(_logger, _httpClientFactory, _uriService);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Google favicon lookup failed for {domain}.", domain);
return null;
}
Comment thread
kspearrin marked this conversation as resolved.
Dismissed
}

private async Task<Icon?> GetFaviconAsync(string domain)
{
// Fall back to favicon
Expand Down
3 changes: 3 additions & 0 deletions src/Icons/appsettings.SelfHosted.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,8 @@
"internalSso": null,
"internalScim": null
}
},
"iconsSettings": {
"googleFaviconEnabled": false
}
}
3 changes: 2 additions & 1 deletion src/Icons/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"iconsSettings": {
"cacheEnabled": true,
"cacheHours": 24,
"cacheSizeLimit": null
"cacheSizeLimit": null,
"googleFaviconEnabled": true
Comment thread
kspearrin marked this conversation as resolved.
Comment thread
kspearrin marked this conversation as resolved.
},
"changePasswordUriSettings": {
"cacheEnabled": true,
Expand Down
1 change: 1 addition & 0 deletions test/Icons.Test/Services/ServiceTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public ServiceTestBase()

_services.ConfigureHttpClients();
_services.AddHtmlParsing();
_services.AddSingleton(new IconsSettings());
_services.AddServices();

_provider = _services.BuildServiceProvider();
Expand Down
Loading