Merge branch 'develop' of https://github.com/BeyondDimension/SteamTools into develop

This commit is contained in:
LY
2026-03-11 04:08:32 +08:00
3 changed files with 184 additions and 1 deletions
@@ -90,4 +90,122 @@ partial class HttpReverseProxyMiddleware
notfound: insertPosition = -1;
return false;
}
internal static bool FindScriptInjectInsertPositionForGithub(byte[] buffer_, Encoding encoding, out ReadOnlyMemory<byte> buffer, out int insertPosition)
{
buffer = buffer_.AsMemory();
ReadOnlySpan<byte> mark = "<script"u8;
var lastScriptWithSrcStart = -1;
var span = buffer_.AsSpan();
if (span.Length >= mark.Length)
{
for (var i = 0; i <= span.Length - mark.Length; i++)
{
if (!EqualsAsciiIgnoreCase(span.Slice(i, mark.Length), mark))
{
continue;
}
var afterMarkIndex = i + mark.Length;
if (afterMarkIndex < span.Length && IsHtmlAttributeNameChar(span[afterMarkIndex]))
{
continue;
}
var tagEndOffset = span.Slice(afterMarkIndex).IndexOf((byte)'>');
if (tagEndOffset < 0)
{
break;
}
var tagEndIndex = afterMarkIndex + tagEndOffset;
var scriptTag = span.Slice(i, tagEndIndex - i + 1);
if (ScriptTagHasSrcAttribute(scriptTag))
{
lastScriptWithSrcStart = i;
}
i = tagEndIndex;
}
}
if (lastScriptWithSrcStart >= 0)
{
insertPosition = lastScriptWithSrcStart;
return true;
}
return FindScriptInjectInsertPosition(buffer_, encoding, out buffer, out insertPosition);
}
static bool ScriptTagHasSrcAttribute(ReadOnlySpan<byte> scriptTag)
{
ReadOnlySpan<byte> src = "src"u8;
if (scriptTag.Length < src.Length)
return false;
for (var i = 0; i <= scriptTag.Length - src.Length; i++)
{
if (!EqualsAsciiIgnoreCase(scriptTag.Slice(i, src.Length), src))
continue;
if (i > 0 && IsHtmlAttributeNameChar(scriptTag[i - 1]))
continue;
var j = i + src.Length;
while (j < scriptTag.Length && IsAsciiWhitespace(scriptTag[j]))
{
j++;
}
if (j < scriptTag.Length && scriptTag[j] == (byte)'=')
{
return true;
}
}
return false;
}
static bool EqualsAsciiIgnoreCase(ReadOnlySpan<byte> left, ReadOnlySpan<byte> right)
{
if (left.Length != right.Length)
return false;
for (var i = 0; i < left.Length; i++)
{
var l = ToUpperAscii(left[i]);
var r = ToUpperAscii(right[i]);
if (l != r)
return false;
}
return true;
}
static byte ToUpperAscii(byte value)
{
if (value is >= (byte)'a' and <= (byte)'z')
return (byte)(value - 32);
return value;
}
static bool IsAsciiWhitespace(byte value) =>
value == (byte)' ' ||
value == (byte)'\t' ||
value == (byte)'\r' ||
value == (byte)'\n' ||
value == (byte)'\f';
static bool IsHtmlAttributeNameChar(byte value) =>
(value is >= (byte)'a' and <= (byte)'z') ||
(value is >= (byte)'A' and <= (byte)'Z') ||
(value is >= (byte)'0' and <= (byte)'9') ||
value == (byte)'-' ||
value == (byte)'_' ||
value == (byte)':' ||
value == (byte)'.';
}
@@ -327,7 +327,12 @@ Actual value was {actualValue}.
}
}
if (FindScriptInjectInsertPosition(buffer_, encoding, out var buffer, out var position))
var isGithubHost = IsGithubHost(context.Request.Host.Host);
var isFindPosition = isGithubHost
? FindScriptInjectInsertPositionForGithub(buffer_, encoding, out var buffer, out var position)
: FindScriptInjectInsertPosition(buffer_, encoding, out buffer, out position);
if (isFindPosition)
{
using var bodyWriter = new MemoryStream();
using Stream? bodyCompress = GetStreamByContentCompression(bodyWriter, contentCompression, CompressionMode.Compress, true);
@@ -398,6 +403,15 @@ Actual value was {actualValue}.
context.Response.Body = originalBody;
}
}
static bool IsGithubHost(string? host)
{
if (string.IsNullOrWhiteSpace(host))
return false;
return host.Equals("github.com", StringComparison.OrdinalIgnoreCase) ||
host.EndsWith(".github.com", StringComparison.OrdinalIgnoreCase);
}
}
static Stream? GetStreamByContentCompression(Stream stream, string contentCompression, CompressionMode mode, bool leaveOpen) => contentCompression switch
@@ -46,4 +46,55 @@ public sealed class HttpReverseProxyMiddlewareUnitTest
var new_html = encoding.GetString(s.ToArray());
TestContext.WriteLine(new_html);
}
[Test]
public void TestFindScriptInjectInsertPositionForGithub_BeforeLastScript()
{
var html = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>demo</title>
</head>
<body>
<script src="/first.js"></script>
<script src="/runtime.js"></script>
</body>
</html>
""";
var buffer = Encoding.UTF8.GetBytes(html);
var ok = HttpReverseProxyMiddleware.FindScriptInjectInsertPositionForGithub(buffer, Encoding.UTF8, out _, out var position);
Assert.That(ok, Is.True);
var expected = html.LastIndexOf("<script src=\"/runtime.js\">", StringComparison.OrdinalIgnoreCase);
Assert.That(position, Is.EqualTo(expected));
}
[Test]
public void TestFindScriptInjectInsertPositionForGithub_BeforeLastScriptWithSrc()
{
var html = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script src="/first.js"></script>
<script>window.__INLINE__ = true;</script>
<script src="/runtime.js" data-rspack="@github-ui/github-ui:runtime"></script>
<script>window.__AFTER_RUNTIME__ = true;</script>
</body>
</html>
""";
var buffer = Encoding.UTF8.GetBytes(html);
var ok = HttpReverseProxyMiddleware.FindScriptInjectInsertPositionForGithub(buffer, Encoding.UTF8, out _, out var position);
Assert.That(ok, Is.True);
var expected = html.LastIndexOf("<script src=\"/runtime.js\"", StringComparison.OrdinalIgnoreCase);
Assert.That(position, Is.EqualTo(expected));
}
}