Skip to content

Commit

Permalink
Better live reload handling in malformed HTML files
Browse files Browse the repository at this point in the history
  • Loading branch information
daveaglick committed Dec 29, 2022
1 parent 445b769 commit 472906b
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 21 deletions.
1 change: 1 addition & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- Added a new `AdditionalInputFiles` setting that can be used to explicitly include input files that would otherwise be excluded by the `InputFiles` setting such as underscore files.
- Updated the `NetlifyRedirects` setting so that when `true`, any existing Netlify-style `_redirects` file will be output (underscore files are normally excluded by default).
- Added a new `NetlifyPrefixRedirects` setting (`true` by default) that will automatically create redirect entries to files and folders that are prefixed with a specific value (`^` by default) from requests without the prefix. This is helpful when needing to publish files or folder prefixed with a `.` since [Netlify deployment doesn't support them](https://answers.netlify.com/t/netlify-deploy-api-removes-files-and-directories-beginning-with-a-period/37728/9) (#1002).
- Improved script injection behavior (I.e. for live reload support) when the HTML document deviates from standards (I.e. directly after Markdown rendering).

# 1.0.0-beta.52

Expand Down
31 changes: 24 additions & 7 deletions src/Statiq.Web.Hosting/Middleware/ScriptInjectionMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,34 @@ public async Task InvokeAsync(HttpContext context)
if (string.Equals(context.Response.ContentType, "text/html", StringComparison.OrdinalIgnoreCase))
{
StreamReader reader = new StreamReader(interceptedBody);
string body = await reader.ReadToEndAsync();
int closingTag = body.LastIndexOf("</body>", StringComparison.Ordinal);
if (closingTag > -1)
{
interceptedBody = new MemoryStream(reader.CurrentEncoding.GetBytes(body.Insert(closingTag, _injectionCode)));
context.Response.ContentLength = interceptedBody.Length;
}
string document = await reader.ReadToEndAsync();
int injectionPosition = GetInjectionPosition(document);
interceptedBody = new MemoryStream(reader.CurrentEncoding.GetBytes(document.Insert(injectionPosition, _injectionCode)));
context.Response.ContentLength = interceptedBody.Length;
}
interceptedBody.Position = 0;
await interceptedBody.CopyToAsync(originalBody);
context.Response.Body = originalBody;
}

private int GetInjectionPosition(string document)
{
// Try for the </body> tag first
int position = document.LastIndexOf("</body", StringComparison.OrdinalIgnoreCase);

// If we didn't find a body, try for the </html> tag
if (position == -1)
{
position = document.LastIndexOf("</html", StringComparison.OrdinalIgnoreCase);
}

// If we didn't find a body or html, just inject at the end
if (position == -1)
{
position = document.Length;
}

return position;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ namespace Statiq.Web.Hosting.Tests.Middleware
[TestFixture]
public class ScriptInjectionMiddlewareTests
{
[TestCase("BasicHtmlDocument.html", true)]
[TestCase("BasicHtmlDocumentNoBodyEnd.html", false)]
[TestCase("NonHtmlDocument.css", false)]
public async Task InjectScriptWhenAppropriate(string filename, bool inject)
[TestCase("BasicHtmlDocument.html", "<script type=\"text/javascript\" src=\"/livereload.js\"></script></body>", 151)]
[TestCase("BasicHtmlDocumentNoBody.html", "<script type=\"text/javascript\" src=\"/livereload.js\"></script></html>", 146)]
[TestCase("BasicHtmlDocumentNoHtml.html", "<script type=\"text/javascript\" src=\"/livereload.js\"></script>", 89)]
public async Task ShouldInjectScriptAtCorrectLocation(string filename, string injected, int injectionPosition)
{
// Given
TestServer server = GetServer();
Expand All @@ -29,14 +29,21 @@ public async Task InjectScriptWhenAppropriate(string filename, bool inject)

// Then
response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK);
if (inject)
{
body.ShouldContain("<script type=\"text/javascript\" src=\"/livereload.js\"></script></body>");
}
else
{
body.ShouldBe(AssemblyHelper.ReadEmbeddedWebFile(filename));
}
body.LastIndexOf(injected).ShouldBe(injectionPosition);
}

[Test]
public async Task ShouldNotInjectNonHtmlContent()
{
// Given
TestServer server = GetServer();

// When
HttpResponseMessage response = await server.CreateRequest("NonHtmlDocument.css").GetAsync();
string body = await response.Content.ReadAsStringAsync();

// Then
body.ShouldBe(AssemblyHelper.ReadEmbeddedWebFile("NonHtmlDocument.css"));
}

private TestServer GetServer() => new TestServer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="wwwroot\BasicHtmlDocument.html" />
<EmbeddedResource Include="wwwroot\BasicHtmlDocumentNoBodyEnd.html" />
<EmbeddedResource Include="wwwroot\BasicHtmlDocumentNoBody.html" />
<EmbeddedResource Include="wwwroot\BasicHtmlDocumentNoHtml.html" />
<EmbeddedResource Include="wwwroot\NonHtmlDocument.css" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
<meta charset="utf-8"/>
<title></title>
</head>
<body>
foo
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!DOCTYPE html>

<head>
<meta charset="utf-8"/>
<title></title>
</head>
foo

0 comments on commit 472906b

Please sign in to comment.