using System.Collections.ObjectModel;
using System.Collections.Concurrent;
using System.Net;
using System.Text;
namespace XFEExtension.NetCore.CyberComm;
///
/// 与具体 HTTP 监听实现无关的请求上下文。
///
public sealed class CyberCommHttpRequestContext
{
internal CyberCommHttpRequestContext(
Uri requestUri,
string method,
IReadOnlyDictionary> headers,
IReadOnlyDictionary> query,
ReadOnlyMemory body,
string clientIp,
int localPort,
string correlationId)
{
RequestUri = requestUri;
Method = method;
Headers = headers;
Query = query;
Body = body;
ClientIp = clientIp;
LocalPort = localPort;
CorrelationId = correlationId;
}
public Uri RequestUri { get; }
public string Method { get; }
public IReadOnlyDictionary> Headers { get; }
public IReadOnlyDictionary> Query { get; }
public ReadOnlyMemory Body { get; }
public string RequestBody => Encoding.UTF8.GetString(Body.Span);
public string ClientIp { get; }
/// 实际接受当前连接的本地监听端口,不受 Host 请求头或反向代理公开端口影响。
public int LocalPort { get; }
public string CorrelationId { get; }
public CyberCommHttpResponse Response { get; } = new();
}
///
/// CyberComm 缓冲式 HTTP 响应。响应由服务端统一写出并关闭或复用连接。
///
public sealed class CyberCommHttpResponse
{
private readonly object _syncRoot = new();
private byte[] _body = [];
public HttpStatusCode StatusCode { get; private set; } = HttpStatusCode.OK;
public string ContentType { get; private set; } = "text/plain; charset=utf-8";
public IDictionary Headers { get; } = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase);
public bool IsCompleted { get; private set; }
internal ReadOnlyMemory Body => _body;
public ValueTask WriteTextAsync(string text, HttpStatusCode statusCode = HttpStatusCode.OK, string contentType = "text/plain; charset=utf-8")
=> WriteAsync(Encoding.UTF8.GetBytes(text ?? string.Empty), statusCode, contentType);
public ValueTask WriteAsync(ReadOnlyMemory body, HttpStatusCode statusCode = HttpStatusCode.OK, string contentType = "application/octet-stream")
{
lock (_syncRoot)
{
ObjectDisposedException.ThrowIf(IsCompleted, this);
StatusCode = statusCode;
ContentType = contentType;
_body = body.ToArray();
}
return ValueTask.CompletedTask;
}
public void Complete(HttpStatusCode? statusCode = null)
{
lock (_syncRoot)
{
if (IsCompleted) return;
if (statusCode is not null)
StatusCode = statusCode.Value;
IsCompleted = true;
}
}
internal void EnsureCompleted(HttpStatusCode fallbackStatus)
{
lock (_syncRoot)
{
if (IsCompleted) return;
if (_body.Length == 0)
{
StatusCode = fallbackStatus;
_body = Encoding.UTF8.GetBytes(fallbackStatus == HttpStatusCode.NotFound ? "Not Found" : "No response was produced");
}
IsCompleted = true;
}
}
}
///
/// CyberComm HTTP 异步处理器。
///
public interface ICyberCommHttpHandler
{
ValueTask HandleAsync(CyberCommHttpRequestContext context, CancellationToken cancellationToken);
}
internal static class CyberCommHttpCollections
{
public static IReadOnlyDictionary> Freeze(Dictionary> source)
=> new ReadOnlyDictionary>(
source.ToDictionary(pair => pair.Key, pair => (IReadOnlyList)pair.Value.AsReadOnly(), source.Comparer));
}