XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
namespace XFESystem.Browser;

public readonly record struct BrowserResourceReceipt(string Address, string Kind, int Characters, string? RequestedAddress = null);

/// <summary>One navigation's bounded resource fetches, with the same TLS/mixed-content rules as its document.</summary>
internal sealed class BrowserResourceLoader(IWebTransport transport, IBrowserResourceSource resources, Uri documentAddress, BrowserLimits limits, bool bundledOnly = false)
{
    private int count;
    private long characters;
    public List<BrowserResourceReceipt> Receipts { get; } = [];
    public bool LastResponsePackaged { get; private set; }

    public WebResponse Get(Uri target, string kind)
    {
        BrowserService.ValidateWebAddress(target);
        if (bundledOnly && target.GetLeftPart(UriPartial.Authority) != PackagedBrowserResources.Origin)
            throw new InvalidOperationException("Local document subresources must be bundled with XFESystem.");
        if (++count > limits.MaximumExternalResources) throw new BrowserLimitException("The external resource count limit was reached.");
        if (documentAddress.Scheme == "https" && target.Scheme != "https")
            throw new InvalidOperationException("An HTTPS document cannot load an HTTP subresource.");
        var requestLimits = limits with { MaximumHtmlCharacters = Math.Min(8_388_608, limits.MaximumExternalResourceCharacters) };
        WebResponse response;
        LastResponsePackaged = resources.TryGet(target, out response!);
        if (!LastResponsePackaged)
        {
            if (bundledOnly || target.GetLeftPart(UriPartial.Authority) == PackagedBrowserResources.Origin)
                throw new FileNotFoundException("The packaged browser resource does not exist: " + target.AbsolutePath);
            response = transport.Get(target, requestLimits);
            if (BrowserService.IsReservedAddress(response.Address))
                throw new InvalidOperationException("A network subresource cannot impersonate a bundled resource.");
        }
        if (bundledOnly && response.Address.GetLeftPart(UriPartial.Authority) != PackagedBrowserResources.Origin)
            throw new InvalidOperationException("A local document resource cannot redirect outside its bundled origin.");
        BrowserService.ValidateWebAddress(response.Address);
        if ((target.Scheme == "https" || documentAddress.Scheme == "https") && response.Address.Scheme != "https")
            throw new InvalidOperationException("An HTTPS subresource cannot downgrade to HTTP.");
        if (response.Address.Scheme == "https" && !response.CertificateValidated)
            throw new InvalidOperationException("Subresource HTTPS certificate validation was not confirmed.");
        if (response.StatusCode is < 200 or >= 300) throw new IOException("Subresource HTTP status " + response.StatusCode + ".");
        int maximum = kind == "style" ? limits.MaximumStyleCharacters : limits.MaximumExternalResourceCharacters;
        if (response.Body.Length > maximum || (characters += response.Body.Length) > limits.MaximumTotalResourceCharacters)
            throw new BrowserLimitException("The external resources exceed the navigation's character budget.");
        var mime = response.ContentType.Split(';')[0].Trim().ToLowerInvariant();
        if (kind == "script" && mime is not ("text/javascript" or "application/javascript" or "application/ecmascript" or "text/ecmascript"))
            throw new NotSupportedException("The script response has an unsupported MIME type: " + mime);
        if (kind == "style" && mime != "text/css") throw new NotSupportedException("The stylesheet response is not text/css.");
        Receipts.Add(new(response.Address.AbsoluteUri, kind, response.Body.Length, target.AbsoluteUri));
        return response;
    }
}