using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using System; using System.Buffers; using System.IO.Pipelines; using System.Text; using System.Threading.Tasks; namespace FastGithub.HttpServer.TcpMiddlewares { /// /// 正向代理中间件 /// sealed class HttpProxyMiddleware { private readonly HttpParser httpParser = new(); private readonly byte[] http200 = Encoding.ASCII.GetBytes("HTTP/1.1 200 Connection Established\r\n\r\n"); private readonly byte[] http400 = Encoding.ASCII.GetBytes("HTTP/1.1 400 Bad Request\r\n\r\n"); /// /// 执行中间件 /// /// /// /// public async Task InvokeAsync(ConnectionDelegate next, ConnectionContext context) { var input = context.Transport.Input; var output = context.Transport.Output; var request = new HttpRequestHandler(); while (context.ConnectionClosed.IsCancellationRequested == false) { var result = await input.ReadAsync(); if (result.IsCanceled) { break; } try { if (this.ParseRequest(result, request, out var consumed)) { if (request.ProxyProtocol == ProxyProtocol.TunnelProxy) { input.AdvanceTo(consumed); await output.WriteAsync(this.http200, context.ConnectionClosed); } else { input.AdvanceTo(result.Buffer.Start); } context.Features.Set(request); await next(context); break; } else { input.AdvanceTo(result.Buffer.Start, result.Buffer.End); } if (result.IsCompleted) { break; } } catch (Exception) { await output.WriteAsync(this.http400, context.ConnectionClosed); break; } } } /// /// 解析http请求 /// /// /// /// /// private bool ParseRequest(ReadResult result, HttpRequestHandler request, out SequencePosition consumed) { var reader = new SequenceReader(result.Buffer); if (this.httpParser.ParseRequestLine(request, ref reader) && this.httpParser.ParseHeaders(request, ref reader)) { consumed = reader.Position; return true; } else { consumed = default; return false; } } /// /// 代理请求处理器 /// private class HttpRequestHandler : IHttpRequestLineHandler, IHttpHeadersHandler, IHttpProxyFeature { private HttpMethod method; public HostString ProxyHost { get; private set; } public ProxyProtocol ProxyProtocol { get { if (this.ProxyHost.HasValue == false) { return ProxyProtocol.None; } if (this.method == HttpMethod.Connect) { return ProxyProtocol.TunnelProxy; } return ProxyProtocol.HttpProxy; } } void IHttpRequestLineHandler.OnStartLine(HttpVersionAndMethod versionAndMethod, TargetOffsetPathLength targetPath, Span startLine) { this.method = versionAndMethod.Method; var host = Encoding.ASCII.GetString(startLine.Slice(targetPath.Offset, targetPath.Length)); if (versionAndMethod.Method == HttpMethod.Connect) { this.ProxyHost = HostString.FromUriComponent(host); } else if (Uri.TryCreate(host, UriKind.Absolute, out var uri)) { this.ProxyHost = HostString.FromUriComponent(uri); } } void IHttpHeadersHandler.OnHeader(ReadOnlySpan name, ReadOnlySpan value) { } void IHttpHeadersHandler.OnHeadersComplete(bool endStream) { } void IHttpHeadersHandler.OnStaticIndexedHeader(int index) { } void IHttpHeadersHandler.OnStaticIndexedHeader(int index, ReadOnlySpan value) { } } } }