Skip to content

Commit e464f28

Browse files
committed
Merge branch 'develop' of https://github.com/BeyondDimension/SteamTools into develop
2 parents 68d1435 + d42f6ba commit e464f28

3 files changed

Lines changed: 184 additions & 1 deletion

File tree

src/BD.WTTS.Client.Plugins.Accelerator.ReverseProxy/Services.Implementation/HttpServer/Middleware/HttpReverseProxyMiddleware.FindScriptInjectInsertPosition.cs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,122 @@ internal static bool FindScriptInjectInsertPosition(byte[] buffer_, Encoding enc
9090
notfound: insertPosition = -1;
9191
return false;
9292
}
93+
94+
internal static bool FindScriptInjectInsertPositionForGithub(byte[] buffer_, Encoding encoding, out ReadOnlyMemory<byte> buffer, out int insertPosition)
95+
{
96+
buffer = buffer_.AsMemory();
97+
98+
ReadOnlySpan<byte> mark = "<script"u8;
99+
var lastScriptWithSrcStart = -1;
100+
var span = buffer_.AsSpan();
101+
102+
if (span.Length >= mark.Length)
103+
{
104+
for (var i = 0; i <= span.Length - mark.Length; i++)
105+
{
106+
if (!EqualsAsciiIgnoreCase(span.Slice(i, mark.Length), mark))
107+
{
108+
continue;
109+
}
110+
111+
var afterMarkIndex = i + mark.Length;
112+
if (afterMarkIndex < span.Length && IsHtmlAttributeNameChar(span[afterMarkIndex]))
113+
{
114+
continue;
115+
}
116+
117+
var tagEndOffset = span.Slice(afterMarkIndex).IndexOf((byte)'>');
118+
if (tagEndOffset < 0)
119+
{
120+
break;
121+
}
122+
123+
var tagEndIndex = afterMarkIndex + tagEndOffset;
124+
var scriptTag = span.Slice(i, tagEndIndex - i + 1);
125+
if (ScriptTagHasSrcAttribute(scriptTag))
126+
{
127+
lastScriptWithSrcStart = i;
128+
}
129+
130+
i = tagEndIndex;
131+
}
132+
}
133+
134+
if (lastScriptWithSrcStart >= 0)
135+
{
136+
insertPosition = lastScriptWithSrcStart;
137+
return true;
138+
}
139+
140+
return FindScriptInjectInsertPosition(buffer_, encoding, out buffer, out insertPosition);
141+
}
142+
143+
static bool ScriptTagHasSrcAttribute(ReadOnlySpan<byte> scriptTag)
144+
{
145+
ReadOnlySpan<byte> src = "src"u8;
146+
147+
if (scriptTag.Length < src.Length)
148+
return false;
149+
150+
for (var i = 0; i <= scriptTag.Length - src.Length; i++)
151+
{
152+
if (!EqualsAsciiIgnoreCase(scriptTag.Slice(i, src.Length), src))
153+
continue;
154+
155+
if (i > 0 && IsHtmlAttributeNameChar(scriptTag[i - 1]))
156+
continue;
157+
158+
var j = i + src.Length;
159+
while (j < scriptTag.Length && IsAsciiWhitespace(scriptTag[j]))
160+
{
161+
j++;
162+
}
163+
164+
if (j < scriptTag.Length && scriptTag[j] == (byte)'=')
165+
{
166+
return true;
167+
}
168+
}
169+
170+
return false;
171+
}
172+
173+
static bool EqualsAsciiIgnoreCase(ReadOnlySpan<byte> left, ReadOnlySpan<byte> right)
174+
{
175+
if (left.Length != right.Length)
176+
return false;
177+
178+
for (var i = 0; i < left.Length; i++)
179+
{
180+
var l = ToUpperAscii(left[i]);
181+
var r = ToUpperAscii(right[i]);
182+
if (l != r)
183+
return false;
184+
}
185+
186+
return true;
187+
}
188+
189+
static byte ToUpperAscii(byte value)
190+
{
191+
if (value is >= (byte)'a' and <= (byte)'z')
192+
return (byte)(value - 32);
193+
return value;
194+
}
195+
196+
static bool IsAsciiWhitespace(byte value) =>
197+
value == (byte)' ' ||
198+
value == (byte)'\t' ||
199+
value == (byte)'\r' ||
200+
value == (byte)'\n' ||
201+
value == (byte)'\f';
202+
203+
static bool IsHtmlAttributeNameChar(byte value) =>
204+
(value is >= (byte)'a' and <= (byte)'z') ||
205+
(value is >= (byte)'A' and <= (byte)'Z') ||
206+
(value is >= (byte)'0' and <= (byte)'9') ||
207+
value == (byte)'-' ||
208+
value == (byte)'_' ||
209+
value == (byte)':' ||
210+
value == (byte)'.';
93211
}

src/BD.WTTS.Client.Plugins.Accelerator.ReverseProxy/Services.Implementation/HttpServer/Middleware/HttpReverseProxyMiddleware.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,12 @@ async Task ResetBody()
327327
}
328328
}
329329

330-
if (FindScriptInjectInsertPosition(buffer_, encoding, out var buffer, out var position))
330+
var isGithubHost = IsGithubHost(context.Request.Host.Host);
331+
var isFindPosition = isGithubHost
332+
? FindScriptInjectInsertPositionForGithub(buffer_, encoding, out var buffer, out var position)
333+
: FindScriptInjectInsertPosition(buffer_, encoding, out buffer, out position);
334+
335+
if (isFindPosition)
331336
{
332337
using var bodyWriter = new MemoryStream();
333338
using Stream? bodyCompress = GetStreamByContentCompression(bodyWriter, contentCompression, CompressionMode.Compress, true);
@@ -398,6 +403,15 @@ async Task SetBodyAsync(Stream stream)
398403
context.Response.Body = originalBody;
399404
}
400405
}
406+
407+
static bool IsGithubHost(string? host)
408+
{
409+
if (string.IsNullOrWhiteSpace(host))
410+
return false;
411+
412+
return host.Equals("github.com", StringComparison.OrdinalIgnoreCase) ||
413+
host.EndsWith(".github.com", StringComparison.OrdinalIgnoreCase);
414+
}
401415
}
402416

403417
static Stream? GetStreamByContentCompression(Stream stream, string contentCompression, CompressionMode mode, bool leaveOpen) => contentCompression switch

src/BD.WTTS.UnitTest/HttpReverseProxyMiddlewareUnitTest.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,55 @@ public void TestFindScriptInjectInsertPosition()
4646
var new_html = encoding.GetString(s.ToArray());
4747
TestContext.WriteLine(new_html);
4848
}
49+
50+
[Test]
51+
public void TestFindScriptInjectInsertPositionForGithub_BeforeLastScript()
52+
{
53+
var html = """
54+
<!DOCTYPE html>
55+
<html>
56+
<head>
57+
<meta charset="utf-8">
58+
<title>demo</title>
59+
</head>
60+
<body>
61+
<script src="/first.js"></script>
62+
<script src="/runtime.js"></script>
63+
</body>
64+
</html>
65+
""";
66+
67+
var buffer = Encoding.UTF8.GetBytes(html);
68+
var ok = HttpReverseProxyMiddleware.FindScriptInjectInsertPositionForGithub(buffer, Encoding.UTF8, out _, out var position);
69+
70+
Assert.That(ok, Is.True);
71+
var expected = html.LastIndexOf("<script src=\"/runtime.js\">", StringComparison.OrdinalIgnoreCase);
72+
Assert.That(position, Is.EqualTo(expected));
73+
}
74+
75+
[Test]
76+
public void TestFindScriptInjectInsertPositionForGithub_BeforeLastScriptWithSrc()
77+
{
78+
var html = """
79+
<!DOCTYPE html>
80+
<html>
81+
<head>
82+
<meta charset="utf-8">
83+
</head>
84+
<body>
85+
<script src="/first.js"></script>
86+
<script>window.__INLINE__ = true;</script>
87+
<script src="/runtime.js" data-rspack="@github-ui/github-ui:runtime"></script>
88+
<script>window.__AFTER_RUNTIME__ = true;</script>
89+
</body>
90+
</html>
91+
""";
92+
93+
var buffer = Encoding.UTF8.GetBytes(html);
94+
var ok = HttpReverseProxyMiddleware.FindScriptInjectInsertPositionForGithub(buffer, Encoding.UTF8, out _, out var position);
95+
96+
Assert.That(ok, Is.True);
97+
var expected = html.LastIndexOf("<script src=\"/runtime.js\"", StringComparison.OrdinalIgnoreCase);
98+
Assert.That(position, Is.EqualTo(expected));
99+
}
49100
}

0 commit comments

Comments
 (0)