HttpProxyMiddleware.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. using Microsoft.AspNetCore.Connections;
  2. using Microsoft.AspNetCore.Http;
  3. using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
  4. using System;
  5. using System.Buffers;
  6. using System.IO.Pipelines;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. namespace FastGithub.HttpServer.TcpMiddlewares
  10. {
  11. /// <summary>
  12. /// 正向代理中间件
  13. /// </summary>
  14. sealed class HttpProxyMiddleware
  15. {
  16. private readonly HttpParser<HttpRequestHandler> httpParser = new();
  17. private readonly byte[] http200 = Encoding.ASCII.GetBytes("HTTP/1.1 200 Connection Established\r\n\r\n");
  18. private readonly byte[] http400 = Encoding.ASCII.GetBytes("HTTP/1.1 400 Bad Request\r\n\r\n");
  19. /// <summary>
  20. /// 执行中间件
  21. /// </summary>
  22. /// <param name="next"></param>
  23. /// <param name="context"></param>
  24. /// <returns></returns>
  25. public async Task InvokeAsync(ConnectionDelegate next, ConnectionContext context)
  26. {
  27. var input = context.Transport.Input;
  28. var output = context.Transport.Output;
  29. var request = new HttpRequestHandler();
  30. while (context.ConnectionClosed.IsCancellationRequested == false)
  31. {
  32. var result = await input.ReadAsync();
  33. if (result.IsCanceled)
  34. {
  35. break;
  36. }
  37. try
  38. {
  39. if (this.ParseRequest(result, request, out var consumed))
  40. {
  41. if (request.ProxyProtocol == ProxyProtocol.TunnelProxy)
  42. {
  43. input.AdvanceTo(consumed);
  44. await output.WriteAsync(this.http200, context.ConnectionClosed);
  45. }
  46. else
  47. {
  48. input.AdvanceTo(result.Buffer.Start);
  49. }
  50. context.Features.Set<IHttpProxyFeature>(request);
  51. await next(context);
  52. break;
  53. }
  54. else
  55. {
  56. input.AdvanceTo(result.Buffer.Start, result.Buffer.End);
  57. }
  58. if (result.IsCompleted)
  59. {
  60. break;
  61. }
  62. }
  63. catch (Exception)
  64. {
  65. await output.WriteAsync(this.http400, context.ConnectionClosed);
  66. break;
  67. }
  68. }
  69. }
  70. /// <summary>
  71. /// 解析http请求
  72. /// </summary>
  73. /// <param name="result"></param>
  74. /// <param name="requestHandler"></param>
  75. /// <param name="consumed"></param>
  76. /// <returns></returns>
  77. private bool ParseRequest(ReadResult result, HttpRequestHandler request, out SequencePosition consumed)
  78. {
  79. var reader = new SequenceReader<byte>(result.Buffer);
  80. if (this.httpParser.ParseRequestLine(request, ref reader) &&
  81. this.httpParser.ParseHeaders(request, ref reader))
  82. {
  83. consumed = reader.Position;
  84. return true;
  85. }
  86. else
  87. {
  88. consumed = default;
  89. return false;
  90. }
  91. }
  92. /// <summary>
  93. /// 代理请求处理器
  94. /// </summary>
  95. private class HttpRequestHandler : IHttpRequestLineHandler, IHttpHeadersHandler, IHttpProxyFeature
  96. {
  97. private HttpMethod method;
  98. public HostString ProxyHost { get; private set; }
  99. public ProxyProtocol ProxyProtocol
  100. {
  101. get
  102. {
  103. if (this.ProxyHost.HasValue == false)
  104. {
  105. return ProxyProtocol.None;
  106. }
  107. if (this.method == HttpMethod.Connect)
  108. {
  109. return ProxyProtocol.TunnelProxy;
  110. }
  111. return ProxyProtocol.HttpProxy;
  112. }
  113. }
  114. void IHttpRequestLineHandler.OnStartLine(HttpVersionAndMethod versionAndMethod, TargetOffsetPathLength targetPath, Span<byte> startLine)
  115. {
  116. this.method = versionAndMethod.Method;
  117. var host = Encoding.ASCII.GetString(startLine.Slice(targetPath.Offset, targetPath.Length));
  118. if (versionAndMethod.Method == HttpMethod.Connect)
  119. {
  120. this.ProxyHost = HostString.FromUriComponent(host);
  121. }
  122. else if (Uri.TryCreate(host, UriKind.Absolute, out var uri))
  123. {
  124. this.ProxyHost = HostString.FromUriComponent(uri);
  125. }
  126. }
  127. void IHttpHeadersHandler.OnHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
  128. {
  129. }
  130. void IHttpHeadersHandler.OnHeadersComplete(bool endStream)
  131. {
  132. }
  133. void IHttpHeadersHandler.OnStaticIndexedHeader(int index)
  134. {
  135. }
  136. void IHttpHeadersHandler.OnStaticIndexedHeader(int index, ReadOnlySpan<byte> value)
  137. {
  138. }
  139. }
  140. }
  141. }