FastGithub/FastGithub.HttpServer/HttpReverseProxyMiddleware.cs
2021-10-11 16:18:09 +08:00

144 lines
5.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using FastGithub.Configuration;
using FastGithub.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Yarp.ReverseProxy.Forwarder;
namespace FastGithub.HttpServer
{
/// <summary>
/// 反向代理中间件
/// </summary>
sealed class HttpReverseProxyMiddleware
{
private const string LOOPBACK = "127.0.0.1";
private const string LOCALHOST = "localhost";
private const int HTTP_PORT = 80;
private const int HTTPS_PORT = 443;
private static readonly DomainConfig sniDomainConfig = new() { TlsSni = true };
private readonly IHttpForwarder httpForwarder;
private readonly IHttpClientFactory httpClientFactory;
private readonly FastGithubConfig fastGithubConfig;
private readonly ILogger<HttpReverseProxyMiddleware> logger;
public HttpReverseProxyMiddleware(
IHttpForwarder httpForwarder,
IHttpClientFactory httpClientFactory,
FastGithubConfig fastGithubConfig,
ILogger<HttpReverseProxyMiddleware> logger)
{
this.httpForwarder = httpForwarder;
this.httpClientFactory = httpClientFactory;
this.fastGithubConfig = fastGithubConfig;
this.logger = logger;
}
/// <summary>
/// 处理请求
/// </summary>
/// <param name="context"></param>
/// <param name="next"?
/// <returns></returns>
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var host = context.Request.Host;
if (this.TryGetDomainConfig(host, out var domainConfig) == false)
{
await next(context);
}
else if (domainConfig.Response == null)
{
var scheme = context.Request.Scheme;
var destinationPrefix = GetDestinationPrefix(scheme, host, domainConfig.Destination);
var httpClient = this.httpClientFactory.CreateHttpClient(host.Host, domainConfig);
var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient);
await HandleErrorAsync(context, error);
}
else
{
context.Response.StatusCode = domainConfig.Response.StatusCode;
context.Response.ContentType = domainConfig.Response.ContentType;
if (domainConfig.Response.ContentValue != null)
{
await context.Response.WriteAsync(domainConfig.Response.ContentValue);
}
}
}
/// <summary>
/// 获取域名的DomainConfig
/// </summary>
/// <param name="host"></param>
/// <param name="domainConfig"></param>
/// <returns></returns>
private bool TryGetDomainConfig(HostString host, [MaybeNullWhen(false)] out DomainConfig domainConfig)
{
if (this.fastGithubConfig.TryGetDomainConfig(host.Host, out domainConfig) == true)
{
return true;
}
// http(s)://127.0.0.1
// http(s)://localhost
if (host.Host == LOOPBACK || host.Host == LOCALHOST)
{
if (host.Port == null || host.Port == HTTPS_PORT || host.Port == HTTP_PORT)
{
return false;
}
}
// 未配置的域名但dns污染解析为127.0.0.1的域名
domainConfig = sniDomainConfig;
return true;
}
/// <summary>
/// 获取目标前缀
/// </summary>
/// <param name="scheme"></param>
/// <param name="host"></param>
/// <param name="destination"></param>
/// <returns></returns>
private string GetDestinationPrefix(string scheme, HostString host, Uri? destination)
{
var defaultValue = $"{scheme}://{host}/";
if (destination == null)
{
return defaultValue;
}
var baseUri = new Uri(defaultValue);
var result = new Uri(baseUri, destination).ToString();
this.logger.LogInformation($"{defaultValue} => {result}");
return result;
}
/// <summary>
/// 处理错误信息
/// </summary>
/// <param name="context"></param>
/// <param name="error"></param>
/// <returns></returns>
private static async Task HandleErrorAsync(HttpContext context, ForwarderError error)
{
if (error == ForwarderError.None || context.Response.HasStarted)
{
return;
}
await context.Response.WriteAsJsonAsync(new
{
error = error.ToString(),
message = context.GetForwarderErrorFeature()?.Exception?.Message
});
}
}
}