|
@@ -2,7 +2,11 @@ using System;
|
|
|
using System.Collections.Generic;
|
|
|
using System.Runtime.InteropServices;
|
|
|
using System.Text;
|
|
|
+using System.Text.Encodings.Web;
|
|
|
+using System.Text.Json;
|
|
|
+using System.Text.Unicode;
|
|
|
using System.Threading.Tasks;
|
|
|
+using appMpower.Objects;
|
|
|
using Microsoft.AspNetCore.Builder;
|
|
|
using Microsoft.AspNetCore.Http;
|
|
|
using Microsoft.Extensions.Primitives;
|
|
@@ -11,11 +15,21 @@ namespace appMpower;
|
|
|
|
|
|
public class FortunesMiddleware
|
|
|
{
|
|
|
+ static readonly HtmlEncoder htmlEncoder = CreateHtmlEncoder();
|
|
|
+ static HtmlEncoder CreateHtmlEncoder()
|
|
|
+ {
|
|
|
+ var settings = new TextEncoderSettings(UnicodeRanges.BasicLatin, UnicodeRanges.Katakana, UnicodeRanges.Hiragana);
|
|
|
+ settings.AllowCharacter('\u2014'); // allow EM DASH through
|
|
|
+ return HtmlEncoder.Create(settings);
|
|
|
+ }
|
|
|
+
|
|
|
private readonly static KeyValuePair<string, StringValues> _headerServer =
|
|
|
new KeyValuePair<string, StringValues>("Server", new StringValues("k"));
|
|
|
private readonly static KeyValuePair<string, StringValues> _headerContentType =
|
|
|
new KeyValuePair<string, StringValues>("Content-Type", new StringValues("text/html; charset=UTF-8"));
|
|
|
|
|
|
+ private static readonly byte[] _delimiter = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF };
|
|
|
+
|
|
|
private readonly RequestDelegate _next;
|
|
|
|
|
|
public FortunesMiddleware(RequestDelegate next)
|
|
@@ -27,6 +41,66 @@ public class FortunesMiddleware
|
|
|
{
|
|
|
if (httpContext.Request.Path.StartsWithSegments("/fortunes", StringComparison.Ordinal))
|
|
|
{
|
|
|
+ int payloadLength;
|
|
|
+ IntPtr handlePointer;
|
|
|
+
|
|
|
+ IntPtr bytePointer = NativeMethods.Fortunes(out payloadLength, out handlePointer);
|
|
|
+
|
|
|
+ /*
|
|
|
+ byte[] json = new byte[payloadLength];
|
|
|
+ Marshal.Copy(bytePointer, json, 0, payloadLength);
|
|
|
+ NativeMethods.FreeHandlePointer(handlePointer);
|
|
|
+
|
|
|
+ string s = Encoding.UTF8.GetString(json, 0, json.Length);
|
|
|
+
|
|
|
+ var options = new JsonSerializerOptions
|
|
|
+ {
|
|
|
+ PropertyNameCaseInsensitive = true
|
|
|
+ };
|
|
|
+
|
|
|
+ List<Fortune> fortunes = JsonSerializer.Deserialize<List<Fortune>>(s, options);
|
|
|
+
|
|
|
+ var response = httpContext.Response;
|
|
|
+ response.Headers.Add(_headerServer);
|
|
|
+
|
|
|
+ var result = Results.Extensions.RazorSlice<Slices.Fortunes, List<Fortune>>(fortunes);
|
|
|
+ result.HtmlEncoder = htmlEncoder;
|
|
|
+
|
|
|
+ return result.ExecuteAsync(httpContext);
|
|
|
+ */
|
|
|
+
|
|
|
+ byte[] byteArray = new byte[payloadLength];
|
|
|
+ Marshal.Copy(bytePointer, byteArray, 0, payloadLength);
|
|
|
+
|
|
|
+ List<Fortune> fortunes = new List<Fortune>();
|
|
|
+
|
|
|
+ // Convert the byte array into segments split by the delimiter
|
|
|
+ int delimiterLength = _delimiter.Length;
|
|
|
+ int start = 0;
|
|
|
+ int index;
|
|
|
+
|
|
|
+ while ((index = FindDelimiterIndex(byteArray, _delimiter, start)) >= 0)
|
|
|
+ {
|
|
|
+ // Use a span over the segment of bytes for the current object
|
|
|
+ var objectDataSpan = new ReadOnlySpan<byte>(byteArray, start, index - start);
|
|
|
+ Fortune fortune = ConvertBytesToObject(objectDataSpan);
|
|
|
+ fortunes.Add(fortune);
|
|
|
+
|
|
|
+ // Move past the delimiter
|
|
|
+ start = index + delimiterLength;
|
|
|
+ }
|
|
|
+
|
|
|
+ NativeMethods.FreeHandlePointer(handlePointer);
|
|
|
+
|
|
|
+ var response = httpContext.Response;
|
|
|
+ response.Headers.Add(_headerServer);
|
|
|
+
|
|
|
+ var result = Results.Extensions.RazorSlice<Slices.Fortunes, List<Fortune>>(fortunes);
|
|
|
+ result.HtmlEncoder = htmlEncoder;
|
|
|
+
|
|
|
+ return result.ExecuteAsync(httpContext);
|
|
|
+
|
|
|
+ /*
|
|
|
var response = httpContext.Response;
|
|
|
response.Headers.Add(_headerServer);
|
|
|
response.Headers.Add(_headerContentType);
|
|
@@ -43,10 +117,51 @@ public class FortunesMiddleware
|
|
|
new KeyValuePair<string, StringValues>("Content-Length", payloadLength.ToString()));
|
|
|
|
|
|
return response.Body.WriteAsync(json, 0, payloadLength);
|
|
|
+ */
|
|
|
}
|
|
|
|
|
|
return _next(httpContext);
|
|
|
}
|
|
|
+
|
|
|
+ private static int FindDelimiterIndex(byte[] array, byte[] delimiter, int startIndex)
|
|
|
+ {
|
|
|
+ int endIndex = array.Length - delimiter.Length;
|
|
|
+
|
|
|
+ for (int i = startIndex; i <= endIndex; i++)
|
|
|
+ {
|
|
|
+ bool isMatch = true;
|
|
|
+
|
|
|
+ for (int j = 0; j < delimiter.Length; j++)
|
|
|
+ {
|
|
|
+ if (array[i + j] != delimiter[j])
|
|
|
+ {
|
|
|
+ isMatch = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isMatch)
|
|
|
+ {
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Fortune ConvertBytesToObject(ReadOnlySpan<byte> data)
|
|
|
+ {
|
|
|
+ int offset = 0;
|
|
|
+
|
|
|
+ // Read Id
|
|
|
+ int id = BitConverter.ToInt32(data.Slice(offset, sizeof(int)));
|
|
|
+ offset += sizeof(int);
|
|
|
+
|
|
|
+ // Read Message (remaining bytes in the span)
|
|
|
+ string message = Encoding.UTF8.GetString(data.Slice(offset));
|
|
|
+
|
|
|
+ return new Fortune(id, message);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
public static class FortunesMiddlewareExtensions
|