diff --git a/extensions/Sisk.SslProxy/DnsUtil.cs b/extensions/Sisk.SslProxy/DnsUtil.cs
index a534fd5..687e14d 100644
--- a/extensions/Sisk.SslProxy/DnsUtil.cs
+++ b/extensions/Sisk.SslProxy/DnsUtil.cs
@@ -33,9 +33,8 @@ public static IPEndPoint ResolveEndpoint(ListeningPort port, bool onlyUseIPv4 =
else
{
resolvedAddress =
- // try to return the last IPv6, or the last IPv4 if no IPv6 was found (#16)
- hostEntry.AddressList.LastOrDefault(a => a.AddressFamily == AddressFamily.InterNetworkV6)
- ?? hostEntry.AddressList.LastOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork);
+ // try to return the last IPv6 or IPv4
+ hostEntry.AddressList.LastOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork || a.AddressFamily == AddressFamily.InterNetworkV6);
}
if (resolvedAddress is null)
diff --git a/src/Helpers/PathHelper.cs b/src/Helpers/PathHelper.cs
index bf61d69..b2af734 100644
--- a/src/Helpers/PathHelper.cs
+++ b/src/Helpers/PathHelper.cs
@@ -24,6 +24,17 @@ public static string CombinePaths(params string[] paths)
{
return PathUtility.CombinePaths(paths);
}
+
+ ///
+ /// Normalizes and combines the specified file-system paths into one.
+ ///
+ /// Specifies if relative paths should be merged and ".." returns should be respected.
+ /// Specifies the path separator character.
+ /// Specifies the array of paths to combine.
+ public static string FilesystemCombinePaths(bool allowRelativeReturn, char separator, params string[] paths)
+ {
+ return PathUtility.NormalizedCombine(allowRelativeReturn, separator, paths);
+ }
///
/// Normalizes and combines the specified file-system paths into one.
diff --git a/src/Http/HttpServer__Core.cs b/src/Http/HttpServer__Core.cs
index f22c171..4820a90 100644
--- a/src/Http/HttpServer__Core.cs
+++ b/src/Http/HttpServer__Core.cs
@@ -357,7 +357,9 @@ private void ProcessRequest(HttpListenerContext context)
if (response.Content is ByteArrayContent barrayContent)
{
ApplyHttpContentHeaders(baseResponse, barrayContent.Headers);
- ReadOnlySpan contentBytes = ByteArrayAccessors.UnsafeGetContent(barrayContent);
+ byte[] contentBytes = ByteArrayAccessors.UnsafeGetContent(barrayContent);
+ int offset = ByteArrayAccessors.UnsafeGetOffset(barrayContent);
+ int count = ByteArrayAccessors.UnsafeGetCount(barrayContent);
if (response.SendChunked)
{
@@ -366,10 +368,10 @@ private void ProcessRequest(HttpListenerContext context)
else
{
baseResponse.SendChunked = false;
- baseResponse.ContentLength64 = contentBytes.Length;
+ baseResponse.ContentLength64 = count;
}
- baseResponse.OutputStream.Write(contentBytes);
+ baseResponse.OutputStream.Write(contentBytes, offset, count);
}
else if (response.Content is HttpContent httpContent)
{
diff --git a/src/Http/Streams/HttpStreamPingPolicy.cs b/src/Http/Streams/HttpStreamPingPolicy.cs
index e00c404..9aceede 100644
--- a/src/Http/Streams/HttpStreamPingPolicy.cs
+++ b/src/Http/Streams/HttpStreamPingPolicy.cs
@@ -44,6 +44,19 @@ public void Start()
{
this._timer = new Timer(new TimerCallback(this.OnCallback), null, 0, (int)this.Interval.TotalMilliseconds);
}
+
+ ///
+ /// Configures and starts sending periodic pings to the client.
+ ///
+ /// The payload message that is sent to the server as a ping message.
+ /// The sending interval for each ping message.
+ public void Start(string dataMessage, TimeSpan interval)
+ {
+ this.DataMessage = dataMessage;
+ this.Interval = interval;
+
+ this.Start();
+ }
private void OnCallback(object? state)
{
diff --git a/src/Http/Streams/HttpWebSocket.cs b/src/Http/Streams/HttpWebSocket.cs
index 44a9fb5..082b4ae 100644
--- a/src/Http/Streams/HttpWebSocket.cs
+++ b/src/Http/Streams/HttpWebSocket.cs
@@ -9,7 +9,6 @@
using System.Net.WebSockets;
using System.Runtime.CompilerServices;
-using System.Text;
namespace Sisk.Core.Http.Streams
{
@@ -78,7 +77,7 @@ public sealed class HttpWebSocket
/// Represents the event which is called when this web socket receives an message from
/// remote origin.
///
- public event WebSocketMessageReceivedEventHandler? OnReceive = null;
+ public event WebSocketMessageReceivedEventHandler? OnReceive;
internal HttpWebSocket(HttpListenerWebSocketContext ctx, HttpRequest req, string? identifier)
{
@@ -170,7 +169,7 @@ internal async void ReceiveTask()
}
else
{
- if (OnReceive != null) OnReceive(this, message);
+ if (OnReceive != null) OnReceive.Invoke(this, message);
}
}
}
@@ -199,35 +198,61 @@ public HttpWebSocket WithPing(string probeMessage, TimeSpan interval)
}
///
- /// Sends an text message to the remote point.
+ /// Asynchronously sends an message to the remote point.
///
/// The target message which will be as an encoded UTF-8 string.
- public void Send(object? message)
+ public Task SendAsync(object message)
{
- string? t = message?.ToString();
- this.Send(t ?? string.Empty);
+ return Task.FromResult(this.Send(message));
+ }
+
+ ///
+ /// Asynchronously sends an text message to the remote point.
+ ///
+ /// The target message which will be as an encoded UTF-8 string.
+ public Task SendAsync(string message)
+ {
+ return Task.FromResult(this.Send(message));
+ }
+
+ ///
+ /// Asynchronously sends an binary message to the remote point.
+ ///
+ /// The target message which will be as an encoded UTF-8 string.
+ public Task SendAsync(byte[] buffer)
+ {
+ return Task.FromResult(this.Send(buffer));
}
///
/// Sends an text message to the remote point.
///
/// The target message which will be as an encoded UTF-8 string.
- public void Send(string message)
+ public bool Send(object message)
+ {
+ string? t = message.ToString();
+ if (t is null) throw new ArgumentNullException(nameof(message));
+
+ return this.Send(t);
+ }
+
+ ///
+ /// Sends an text message to the remote point.
+ ///
+ /// The target message which will be as an encoded using the request preferred encoding.
+ public bool Send(string message)
{
- byte[] messageBytes = Encoding.UTF8.GetBytes(message);
- ReadOnlyMemory span = new ReadOnlyMemory(messageBytes);
- this.SendInternal(span, WebSocketMessageType.Text);
+ ArgumentNullException.ThrowIfNull(message);
+
+ byte[] messageBytes = this.request.RequestEncoding.GetBytes(message);
+ return this.SendInternal(messageBytes, WebSocketMessageType.Text);
}
///
/// Sends an binary message to the remote point.
///
/// The target byte array.
- public void Send(byte[] buffer)
- {
- ReadOnlyMemory span = new ReadOnlyMemory(buffer);
- this.SendInternal(span, WebSocketMessageType.Binary);
- }
+ public bool Send(byte[] buffer) => this.Send(buffer, 0, buffer.Length);
///
/// Sends an binary message to the remote point.
@@ -235,19 +260,19 @@ public void Send(byte[] buffer)
/// The target byte array.
/// The index at which to begin the memory.
/// The number of items in the memory.
- public void Send(byte[] buffer, int start, int length)
+ public bool Send(byte[] buffer, int start, int length)
{
ReadOnlyMemory span = new ReadOnlyMemory(buffer, start, length);
- this.SendInternal(span, WebSocketMessageType.Binary);
+ return this.SendInternal(span, WebSocketMessageType.Binary);
}
///
/// Sends an binary message to the remote point.
///
/// The target byte memory.
- public void Send(ReadOnlyMemory buffer)
+ public bool Send(ReadOnlyMemory buffer)
{
- this.SendInternal(buffer, WebSocketMessageType.Binary);
+ return this.SendInternal(buffer, WebSocketMessageType.Binary);
}
///
@@ -290,9 +315,9 @@ public HttpResponse Close()
}
[MethodImpl(MethodImplOptions.Synchronized)]
- private void SendInternal(ReadOnlyMemory buffer, WebSocketMessageType msgType)
+ private bool SendInternal(ReadOnlyMemory buffer, WebSocketMessageType msgType)
{
- if (this.isClosed) { return; }
+ if (this.isClosed) { return false; }
if (this.closeTimeout.TotalMilliseconds > 0)
this.asyncListenerToken?.CancelAfter(this.closeTimeout);
@@ -323,9 +348,10 @@ private void SendInternal(ReadOnlyMemory buffer, WebSocketMessageType msgT
if (this.MaxAttempts >= 0 && this.attempt >= this.MaxAttempts)
{
this.Close();
- return;
+ return false;
}
}
+ return true;
}
///
@@ -419,18 +445,18 @@ public sealed class WebSocketMessage
///
/// Reads the message bytes as string using the specified encoding.
///
- /// The encoding which will be used to decode the message.
- public string GetString(System.Text.Encoding encoder)
+ /// The encoding which will be used to decode the message.
+ public string GetString(System.Text.Encoding encoding)
{
- return encoder.GetString(this.MessageBytes);
+ return encoding.GetString(this.MessageBytes);
}
///
- /// Reads the message bytes as string using the UTF-8 text encoding.
+ /// Reads the message bytes as string using the HTTP request encoding.
///
public string GetString()
{
- return this.GetString(Encoding.UTF8);
+ return this.GetString(this.Sender.HttpRequest.RequestEncoding);
}
internal WebSocketMessage(HttpWebSocket httpws, int bufferLen)
diff --git a/src/Internal/ByteArrayAccessors.cs b/src/Internal/ByteArrayAccessors.cs
index 6ca1968..f685038 100644
--- a/src/Internal/ByteArrayAccessors.cs
+++ b/src/Internal/ByteArrayAccessors.cs
@@ -15,4 +15,10 @@ class ByteArrayAccessors
{
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_content")]
public extern static ref byte[] UnsafeGetContent(ByteArrayContent bcontent);
+
+ [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_offset")]
+ public extern static ref int UnsafeGetOffset(ByteArrayContent bcontent);
+
+ [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_count")]
+ public extern static ref int UnsafeGetCount(ByteArrayContent bcontent);
}
diff --git a/tcp/Sisk.ManagedHttpListener/HttpConnection.cs b/tcp/Sisk.ManagedHttpListener/HttpConnection.cs
index a2f91e0..3d4fb78 100644
--- a/tcp/Sisk.ManagedHttpListener/HttpConnection.cs
+++ b/tcp/Sisk.ManagedHttpListener/HttpConnection.cs
@@ -25,7 +25,7 @@ public int HandleConnectionEvents()
{
//try
//{
- using var bufferedStreamSession = new Streams.HttpBufferedStream(_connectionStream);
+ using var bufferedStreamSession = new Streams.HttpBufferedReadStream(_connectionStream);
if (!HttpRequestSerializer.TryReadHttp1Request(
bufferedStreamSession,
diff --git a/tcp/Sisk.ManagedHttpListener/HttpSerializer/HttpRequestSerializer.cs b/tcp/Sisk.ManagedHttpListener/HttpSerializer/HttpRequestSerializer.cs
index 0991200..ce804fd 100644
--- a/tcp/Sisk.ManagedHttpListener/HttpSerializer/HttpRequestSerializer.cs
+++ b/tcp/Sisk.ManagedHttpListener/HttpSerializer/HttpRequestSerializer.cs
@@ -5,7 +5,7 @@ namespace Sisk.ManagedHttpListener.HttpSerializer;
internal static class HttpRequestSerializer
{
- static ReadOnlySpan ReadUntil(Span readBuffer, Streams.HttpBufferedStream bufferStream, byte intercept, out bool found)
+ static ReadOnlySpan ReadUntil(Span readBuffer, Streams.HttpBufferedReadStream bufferStream, byte intercept, out bool found)
{
int accumulatedPosition = (int)bufferStream.Position;
while (bufferStream.Read(readBuffer) > 0)
@@ -29,7 +29,7 @@ static ReadOnlySpan ReadUntil(Span readBuffer, Streams.HttpBufferedS
}
public static bool TryReadHttp1Request(
- Streams.HttpBufferedStream inboundStream,
+ Streams.HttpBufferedReadStream inboundStream,
scoped Span lineMemory,
[NotNullWhen(true)] out string? method,
[NotNullWhen(true)] out string? path,
diff --git a/tcp/Sisk.ManagedHttpListener/Streams/HttpBufferedStream.cs b/tcp/Sisk.ManagedHttpListener/Streams/HttpBufferedReadStream.cs
similarity index 87%
rename from tcp/Sisk.ManagedHttpListener/Streams/HttpBufferedStream.cs
rename to tcp/Sisk.ManagedHttpListener/Streams/HttpBufferedReadStream.cs
index 20358b8..53837a2 100644
--- a/tcp/Sisk.ManagedHttpListener/Streams/HttpBufferedStream.cs
+++ b/tcp/Sisk.ManagedHttpListener/Streams/HttpBufferedReadStream.cs
@@ -2,20 +2,22 @@
namespace Sisk.ManagedHttpListener.Streams;
-internal class HttpBufferedStream : Stream
+internal class HttpBufferedReadStream : Stream
{
+ private const int INITIAL_BUFFER = 4096;
+
private readonly Stream _stream;
private readonly byte[] _b;
private long _position;
private int _read;
- public HttpBufferedStream(Stream stream)
+ public HttpBufferedReadStream(Stream stream)
{
_stream = stream;
_position = 0;
_read = 0;
- _b = ArrayPool.Shared.Rent(4096);
+ _b = ArrayPool.Shared.Rent(INITIAL_BUFFER);
}
public Span BufferedBytes => _b;
@@ -100,4 +102,10 @@ public void Move(int toPosition)
ArgumentOutOfRangeException.ThrowIfGreaterThan(toPosition, _read);
_position = toPosition;
}
+
+ protected override void Dispose(bool disposing)
+ {
+ ArrayPool.Shared.Return(_b);
+ base.Dispose(disposing);
+ }
}