100 lines
3.5 KiB
C#
100 lines
3.5 KiB
C#
using System.IO;
|
|
using System.Reflection;
|
|
using Lskj.AgentPet.Host.Core;
|
|
|
|
namespace Lskj.AgentPet.Host;
|
|
|
|
internal sealed class EmbeddedPetResource
|
|
{
|
|
public EmbeddedPetResource(byte[] bytes, string contentType, bool document)
|
|
{
|
|
Bytes = bytes ?? throw new ArgumentNullException(nameof(bytes));
|
|
ContentType = contentType ?? throw new ArgumentNullException(nameof(contentType));
|
|
IsDocument = document;
|
|
}
|
|
|
|
public byte[] Bytes { get; }
|
|
public string ContentType { get; }
|
|
public bool IsDocument { get; }
|
|
}
|
|
|
|
internal static class EmbeddedPetResources
|
|
{
|
|
public const string PageUrl = "https://lserp-pet.local/index.html";
|
|
private const int MaximumResourceBytes = 2 * 1024 * 1024;
|
|
private const int MaximumTotalBytes = 4 * 1024 * 1024;
|
|
|
|
private static readonly ResourceSpec[] Specs =
|
|
{
|
|
new(PageUrl, "Lskj.AgentPet.Web.index.html", "text/html; charset=utf-8", true),
|
|
new("https://lserp-pet.local/pet.css", "Lskj.AgentPet.Web.pet.css", "text/css; charset=utf-8", false),
|
|
new("https://lserp-pet.local/pet-runtime.js", "Lskj.AgentPet.Web.pet-runtime.js", "text/javascript; charset=utf-8", false),
|
|
new("https://lserp-pet.local/bridge-client.js", "Lskj.AgentPet.Web.bridge-client.js", "text/javascript; charset=utf-8", false),
|
|
new("https://lserp-pet.local/pet-shell.js", "Lskj.AgentPet.Web.pet-shell.js", "text/javascript; charset=utf-8", false)
|
|
};
|
|
|
|
public static IReadOnlyDictionary<string, EmbeddedPetResource> Load()
|
|
{
|
|
Assembly assembly = typeof(EmbeddedPetResources).Assembly;
|
|
Dictionary<string, EmbeddedPetResource> result =
|
|
new(StringComparer.Ordinal);
|
|
int totalBytes = 0;
|
|
foreach (ResourceSpec spec in Specs)
|
|
{
|
|
using Stream? stream = assembly.GetManifestResourceStream(spec.Name);
|
|
if (stream is null
|
|
|| !stream.CanRead
|
|
|| stream.Length <= 0
|
|
|| stream.Length > MaximumResourceBytes)
|
|
{
|
|
throw new HostError(
|
|
"pet_web_resource_invalid",
|
|
"签名桌宠程序集缺少有效的嵌入式页面资源。");
|
|
}
|
|
totalBytes = checked(totalBytes + (int)stream.Length);
|
|
if (totalBytes > MaximumTotalBytes)
|
|
{
|
|
throw new HostError(
|
|
"pet_web_resource_invalid",
|
|
"签名桌宠页面资源超过安全上限。");
|
|
}
|
|
|
|
byte[] bytes = new byte[(int)stream.Length];
|
|
int offset = 0;
|
|
while (offset < bytes.Length)
|
|
{
|
|
int read = stream.Read(bytes, offset, bytes.Length - offset);
|
|
if (read <= 0)
|
|
throw new HostError(
|
|
"pet_web_resource_invalid",
|
|
"无法完整读取签名桌宠页面资源。");
|
|
offset += read;
|
|
}
|
|
result.Add(
|
|
spec.Url,
|
|
new EmbeddedPetResource(bytes, spec.ContentType, spec.Document));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private sealed class ResourceSpec
|
|
{
|
|
public ResourceSpec(
|
|
string url,
|
|
string name,
|
|
string contentType,
|
|
bool document)
|
|
{
|
|
Url = url;
|
|
Name = name;
|
|
ContentType = contentType;
|
|
Document = document;
|
|
}
|
|
|
|
public string Url { get; }
|
|
public string Name { get; }
|
|
public string ContentType { get; }
|
|
public bool Document { get; }
|
|
}
|
|
}
|