diff --git a/Examples/AspNetPageEmitter/Program.cs b/Examples/AspNetPageEmitter/Program.cs
index d2bf5e3..896ac69 100644
--- a/Examples/AspNetPageEmitter/Program.cs
+++ b/Examples/AspNetPageEmitter/Program.cs
@@ -7,8 +7,17 @@
var app = builder.Build();
-app.MapFallback((HttpContext context) =>
- context.WriteDocumentAsync(new TestDocument()));
+app.Use((context, next) =>
+{
+ context.Response.GetTypedHeaders().CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
+ {
+ Private = true,
+ NoCache = true,
+ };
+
+ return next();
+});
+app.MapFallback(new TestDocument().WriteToAsync);
await app.RunAsync().ConfigureAwait(false);
@@ -21,20 +30,7 @@ class TestDocument : IHtmlDocument
Task IHtmlDocument.WriteBodyContentsAsync(HtmlWriter writer, CancellationToken cancellationToken)
{
writer.WriteElement("p", null, children => children.WriteText("Test bytes."));
+ writer.WriteScript(ValidatedScript.ForInlineSource("console.log('test')"));
return Task.CompletedTask;
}
}
-
-static class Extensions
-{
- public static Task WriteDocumentAsync(this HttpContext context, IHtmlDocument document)
- {
- var request = context.Request;
- var response = context.Response;
- response.ContentType = "text/html; charset=utf-8";
- var baseUri = $"{request.Scheme}://{request.Host}/";
- response.Headers.ContentSecurityPolicy = $"default-src {baseUri}; base-uri {baseUri}";
-
- return document.WriteAsync(response.BodyWriter, context.RequestAborted);
- }
-}
\ No newline at end of file
diff --git a/HtmlUtilities/HtmlDocumentExtensions.cs b/HtmlUtilities/HtmlDocumentExtensions.cs
index 5f18ad4..c6ea3b6 100644
--- a/HtmlUtilities/HtmlDocumentExtensions.cs
+++ b/HtmlUtilities/HtmlDocumentExtensions.cs
@@ -1,4 +1,5 @@
-using System.Buffers;
+using Microsoft.AspNetCore.Http;
+using System.Buffers;
namespace HtmlUtilities;
@@ -8,19 +9,25 @@ namespace HtmlUtilities;
public static class HtmlDocumentExtensions
{
///
- /// Writes the document to .
+ /// Writes the document to .
///
/// The document to write.
- /// Receives the written document.
- /// Cancels emission of document data.
- ///
- public static async Task WriteAsync(
- this IHtmlDocument document,
- IBufferWriter writer,
- CancellationToken cancellationToken = default)
+ /// Receives the written document.
+ /// A task that shows completion when the document is written.
+ public static Task WriteToAsync(this IHtmlDocument document, HttpContext context)
{
ArgumentNullException.ThrowIfNull(document, nameof(document));
- cancellationToken.ThrowIfCancellationRequested();
+ ArgumentNullException.ThrowIfNull(context, nameof(context));
+
+ var request = context.Request;
+ var response = context.Response;
+ response.ContentType = "text/html; charset=utf-8";
+ Span cspNonceUtf16 = stackalloc char[32];
+ System.Security.Cryptography.RandomNumberGenerator.GetHexString(cspNonceUtf16, true);
+ response.Headers.ContentSecurityPolicy = $"base-uri {request.Scheme}://{request.Host}/;default-src 'none';script-src 'unsafe-inline' 'nonce-{cspNonceUtf16}'";
+ // unsafe-inline only applies to browsers that don't support nonce. Can be removed someday.
+
+ var writer = context.Response.BodyWriter;
writer.Write(""u8);
- await document.WriteBodyContentsAsync(new HtmlWriter(writer), cancellationToken).ConfigureAwait(false);
+ return document.WriteBodyContentsAsync(new HtmlWriter(writer, new Validated.ValidatedAttribute("nonce", cspNonceUtf16)), context.RequestAborted);
- writer.Write(""u8);
+ // HTML5 spec doesn't require