138 lines
4.5 KiB
C#
138 lines
4.5 KiB
C#
using System.Text.RegularExpressions;
|
|
|
|
namespace Uwaa.HTTP.Routing;
|
|
|
|
/// <summary>
|
|
/// Special router which serves static files from a directory in the filesystem.
|
|
/// </summary>
|
|
public partial class FileEndpoint : RouterBase
|
|
{
|
|
static MIMEType GuessMIMEType(string extension)
|
|
{
|
|
return extension switch
|
|
{
|
|
".txt" => new("text", "plain"),
|
|
".htm" or ".html" => new("text", "html"),
|
|
".js" => new("text", "javascript"),
|
|
".css" => new("text", "css"),
|
|
".csv" => new("text", "csv"),
|
|
|
|
".bin" => new("application", "octet-stream"),
|
|
".zip" => new("application", "zip"),
|
|
".7z" => new("application", "x-7z-compressed"),
|
|
".gz" => new("application", "gzip"),
|
|
".xml" => new("application", "xml"),
|
|
".pdf" => new("application", "pdf"),
|
|
".json" => new("application", "json"),
|
|
|
|
".bmp" => new("image", "bmp"),
|
|
".png" => new("image", "png"),
|
|
".jpg" or "jpeg" => new("image", "jpeg"),
|
|
".gif" => new("image", "gif"),
|
|
".webp" => new("image", "webp"),
|
|
".svg" => new("image", "svg+xml"),
|
|
".ico" => new("image", "vnd.microsoft.icon"),
|
|
|
|
".mid" or ".midi" => new("audio", "midi"),
|
|
".mp3" => new("audio", "mpeg"),
|
|
".ogg" or ".oga" or ".opus" => new("audio", "ogg"),
|
|
".wav" => new("audio", "wav"),
|
|
".weba" => new("audio", "webm"),
|
|
|
|
".webm" => new("video", "webm"),
|
|
".mp4" => new("video", "mp4"),
|
|
".mpeg" => new("video", "mpeg"),
|
|
".ogv" => new("video", "ogg"),
|
|
|
|
".otf" => new("font", "otf"),
|
|
".ttf" => new("font", "ttf"),
|
|
".woff" => new("font", "woff"),
|
|
".woff2" => new("font", "woff2"),
|
|
|
|
_ => new("application", "octet-stream"), //Unknown
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// The source directory from which assets should be served.
|
|
/// </summary>
|
|
public string Directory;
|
|
|
|
/// <summary>
|
|
/// If true, an empty path will be interpreted as "index.html".
|
|
/// </summary>
|
|
public bool Index = true;
|
|
|
|
public override HttpMethod Method => HttpMethod.GET;
|
|
|
|
public override int Arguments => 1;
|
|
|
|
public FileEndpoint(string directory)
|
|
{
|
|
Directory = directory;
|
|
}
|
|
|
|
protected override async Task<HttpResponse?> GetResponseInner(HttpRequest req, HttpClientInfo info, ArraySegment<string> path)
|
|
{
|
|
string asset = path[0];
|
|
if (FilenameChecker().IsMatch(asset))
|
|
return HttpResponse.BadRequest("Illegal chars in asset path");
|
|
|
|
HttpContent? file = await GetFile(asset);
|
|
return file == null ? null : HttpResponse.OK(file);
|
|
}
|
|
|
|
protected async ValueTask<HttpContent?> GetFile(string asset)
|
|
{
|
|
if (Index && asset == "")
|
|
asset = "index.html";
|
|
|
|
string assetPath = $"{Directory}/{asset}";
|
|
FileInfo fileInfo = new FileInfo(assetPath);
|
|
MIMEType mime;
|
|
if (string.IsNullOrEmpty(fileInfo.Extension))
|
|
{
|
|
mime = new MIMEType("text", "html");
|
|
assetPath += ".html";
|
|
}
|
|
else
|
|
{
|
|
mime = GuessMIMEType(fileInfo.Extension);
|
|
}
|
|
|
|
if (!File.Exists(assetPath))
|
|
return null;
|
|
|
|
return new HttpContent(mime, await File.ReadAllBytesAsync(assetPath));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ensures filenames are legal.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Enforcing a set of legal characters in filenames reduces the potential attack surface against the server.
|
|
/// </remarks>
|
|
/// <returns>Returns a regular expression which checks for invalid characters.</returns>
|
|
[GeneratedRegex(@"[^a-zA-Z0-9_\-.()\[\] ]")]
|
|
protected static partial Regex FilenameChecker();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Special router which serves static files from a directory and its subdirectories.
|
|
/// </summary>
|
|
public partial class FileRecursiveEndpoint : FileEndpoint
|
|
{
|
|
public FileRecursiveEndpoint(string directory) : base(directory)
|
|
{
|
|
}
|
|
|
|
protected override async Task<HttpResponse?> GetResponseInner(HttpRequest req, HttpClientInfo info, ArraySegment<string> path)
|
|
{
|
|
foreach (string pathSeg in path)
|
|
if (FilenameChecker().IsMatch(pathSeg))
|
|
return HttpResponse.BadRequest("Illegal chars in asset path");
|
|
|
|
string asset = Path.Combine(path.ToArray());
|
|
return await GetFile(asset);
|
|
}
|
|
}
|