1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-05-08 02:34:06 +02:00

Add in-memory caching to td:// and tdp:// schemes

This commit is contained in:
chylex 2021-12-23 18:26:26 +01:00
parent c91f1d0e5e
commit 57b03baad9
Signed by: chylex
GPG Key ID: 4DE42C8F19A80548
4 changed files with 87 additions and 26 deletions

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
@ -14,27 +15,9 @@ private static ResourceHandler CreateHandler(byte[] bytes) {
return handler;
}
public IResourceHandler Status(HttpStatusCode code, string message) {
var handler = CreateHandler(Encoding.UTF8.GetBytes(message));
handler.StatusCode = (int) code;
return handler;
}
public IResourceHandler File(string path) {
try {
return FileContents(System.IO.File.ReadAllBytes(path), Path.GetExtension(path));
} catch (FileNotFoundException) {
return Status(HttpStatusCode.NotFound, "File not found.");
} catch (DirectoryNotFoundException) {
return Status(HttpStatusCode.NotFound, "Directory not found.");
} catch (Exception e) {
return Status(HttpStatusCode.InternalServerError, e.Message);
}
}
private IResourceHandler FileContents(byte[] bytes, string extension) {
private static IResourceHandler CreateFileContentsHandler(byte[] bytes, string extension) {
if (bytes.Length == 0) {
return Status(HttpStatusCode.NoContent, "File is empty."); // FromByteArray crashes CEF internals with no contents
return CreateStatusHandler(HttpStatusCode.NoContent, "File is empty."); // FromByteArray crashes CEF internals with no contents
}
else {
var handler = CreateHandler(bytes);
@ -42,5 +25,73 @@ private IResourceHandler FileContents(byte[] bytes, string extension) {
return handler;
}
}
private static IResourceHandler CreateStatusHandler(HttpStatusCode code, string message) {
var handler = CreateHandler(Encoding.UTF8.GetBytes(message));
handler.StatusCode = (int) code;
return handler;
}
private readonly Dictionary<string, ICachedResource> cache = new Dictionary<string, ICachedResource>();
public IResourceHandler Status(HttpStatusCode code, string message) {
return CreateStatusHandler(code, message);
}
public IResourceHandler File(string path) {
string key = new Uri(path).LocalPath;
if (cache.TryGetValue(key, out var cachedResource)) {
return cachedResource.GetResource();
}
cachedResource = FileWithCaching(path);
cache[key] = cachedResource;
return cachedResource.GetResource();
}
private ICachedResource FileWithCaching(string path) {
try {
return new CachedFile(System.IO.File.ReadAllBytes(path), Path.GetExtension(path));
} catch (FileNotFoundException) {
return new CachedStatus(HttpStatusCode.NotFound, "File not found.");
} catch (DirectoryNotFoundException) {
return new CachedStatus(HttpStatusCode.NotFound, "Directory not found.");
} catch (Exception e) {
return new CachedStatus(HttpStatusCode.InternalServerError, e.Message);
}
}
private interface ICachedResource {
IResourceHandler GetResource();
}
private sealed class CachedFile : ICachedResource {
private readonly byte[] bytes;
private readonly string extension;
public CachedFile(byte[] bytes, string extension) {
this.bytes = bytes;
this.extension = extension;
}
public IResourceHandler GetResource() {
return CreateFileContentsHandler(bytes, extension);
}
}
private sealed class CachedStatus : ICachedResource {
private readonly HttpStatusCode code;
private readonly string message;
public CachedStatus(HttpStatusCode code, string message) {
this.code = code;
this.message = message;
}
public IResourceHandler GetResource() {
return CreateStatusHandler(code, message);
}
}
}
}

View File

@ -6,7 +6,11 @@ namespace TweetDuck.Plugins {
sealed class PluginSchemeFactory : ISchemeHandlerFactory {
public const string Name = PluginSchemeHandler<IResourceHandler>.Name;
private readonly PluginSchemeHandler<IResourceHandler> handler = new PluginSchemeHandler<IResourceHandler>(new ResourceProvider());
private readonly PluginSchemeHandler<IResourceHandler> handler;
public PluginSchemeFactory(ResourceProvider resourceProvider) {
handler = new PluginSchemeHandler<IResourceHandler>(resourceProvider);
}
internal void Setup(PluginManager plugins) {
handler.Setup(plugins);

View File

@ -133,8 +133,9 @@ private static void Main() {
#endif
};
var resourceScheme = new ResourceSchemeFactory();
var pluginScheme = new PluginSchemeFactory();
var resourceProvider = new ResourceProvider();
var resourceScheme = new ResourceSchemeFactory(resourceProvider);
var pluginScheme = new PluginSchemeFactory(resourceProvider);
settings.SetupCustomScheme(ResourceSchemeFactory.Name, resourceScheme);
settings.SetupCustomScheme(PluginSchemeFactory.Name, pluginScheme);

View File

@ -2,7 +2,7 @@
using System.IO;
using System.Net;
using CefSharp;
using TweetDuck.Browser.Handling;
using TweetLib.Core.Browser;
using TweetLib.Core.Utils;
namespace TweetDuck.Resources {
@ -10,7 +10,12 @@ public class ResourceSchemeFactory : ISchemeHandlerFactory {
public const string Name = "td";
private static readonly string RootPath = Path.Combine(Program.ResourcesPath);
private static readonly ResourceProvider ResourceProvider = new ResourceProvider();
private readonly IResourceProvider<IResourceHandler> resourceProvider;
public ResourceSchemeFactory(IResourceProvider<IResourceHandler> resourceProvider) {
this.resourceProvider = resourceProvider;
}
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request) {
if (!Uri.TryCreate(request.Url, UriKind.Absolute, out var uri) || uri.Scheme != Name) {
@ -22,7 +27,7 @@ public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName
}
string filePath = FileUtils.ResolveRelativePathSafely(RootPath, uri.AbsolutePath.TrimStart('/'));
return filePath.Length == 0 ? ResourceProvider.Status(HttpStatusCode.Forbidden, "File path has to be relative to the resources root folder.") : ResourceProvider.File(filePath);
return filePath.Length == 0 ? resourceProvider.Status(HttpStatusCode.Forbidden, "File path has to be relative to the resources root folder.") : resourceProvider.File(filePath);
}
}
}