XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEExtension

【DLL】XFE各类拓展是一个C#的DLL库,旨在优化C#代码中常用语句的使用,并提供更简洁的访问方式,同时提供Xunit测试框架,快速搭建服务器/客户端,免费ChatGPTAPI接口,免费通讯服务器,XFE下载器,新增格式等

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Collections.ObjectModel;
using System.Collections.Concurrent;
using System.Net;
using System.Text;

namespace XFEExtension.NetCore.CyberComm;

/// <summary>
/// 与具体 HTTP 监听实现无关的请求上下文。
/// </summary>
public sealed class CyberCommHttpRequestContext
{
    internal CyberCommHttpRequestContext(
        Uri requestUri,
        string method,
        IReadOnlyDictionary<string, IReadOnlyList<string>> headers,
        IReadOnlyDictionary<string, IReadOnlyList<string>> query,
        ReadOnlyMemory<byte> 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<string, IReadOnlyList<string>> Headers { get; }
    public IReadOnlyDictionary<string, IReadOnlyList<string>> Query { get; }
    public ReadOnlyMemory<byte> Body { get; }
    public string RequestBody => Encoding.UTF8.GetString(Body.Span);
    public string ClientIp { get; }
    /// <summary>实际接受当前连接的本地监听端口,不受 Host 请求头或反向代理公开端口影响。</summary>
    public int LocalPort { get; }
    public string CorrelationId { get; }
    public CyberCommHttpResponse Response { get; } = new();
}

/// <summary>
/// CyberComm 缓冲式 HTTP 响应。响应由服务端统一写出并关闭或复用连接。
/// </summary>
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<string, string> Headers { get; } = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
    public bool IsCompleted { get; private set; }
    internal ReadOnlyMemory<byte> 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<byte> 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;
        }
    }
}

/// <summary>
/// CyberComm HTTP 异步处理器。
/// </summary>
public interface ICyberCommHttpHandler
{
    ValueTask HandleAsync(CyberCommHttpRequestContext context, CancellationToken cancellationToken);
}

internal static class CyberCommHttpCollections
{
    public static IReadOnlyDictionary<string, IReadOnlyList<string>> Freeze(Dictionary<string, List<string>> source)
        => new ReadOnlyDictionary<string, IReadOnlyList<string>>(
            source.ToDictionary(pair => pair.Key, pair => (IReadOnlyList<string>)pair.Value.AsReadOnly(), source.Comparer));
}