增加GithubReverseProxyOptions

This commit is contained in:
陈国伟 2021-07-14 12:36:36 +08:00
parent e060dbe57b
commit 4d181e413c
12 changed files with 127 additions and 54 deletions

View File

@ -22,11 +22,6 @@ namespace FastGithub.Dns
/// <summary> /// <summary>
/// 是否设置本机使用此dns /// 是否设置本机使用此dns
/// </summary> /// </summary>
public bool SetToLocalMachine { get; set; } = true; public bool SetToLocalMachine { get; set; } = true;
/// <summary>
/// 是否启用反向代理
/// </summary>
public bool UseReverseProxy { get; set; } = true;
} }
} }

View File

@ -5,6 +5,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\FastGithub.ReverseProxy\FastGithub.ReverseProxy.csproj" />
<ProjectReference Include="..\FastGithub.Scanner\FastGithub.Scanner.csproj" /> <ProjectReference Include="..\FastGithub.Scanner\FastGithub.Scanner.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,6 +1,7 @@
using DNS.Client.RequestResolver; using DNS.Client.RequestResolver;
using DNS.Protocol; using DNS.Protocol;
using DNS.Protocol.ResourceRecords; using DNS.Protocol.ResourceRecords;
using FastGithub.ReverseProxy;
using FastGithub.Scanner; using FastGithub.Scanner;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -21,6 +22,8 @@ namespace FastGithub.Dns
{ {
private readonly IGithubScanResults githubScanResults; private readonly IGithubScanResults githubScanResults;
private readonly IOptionsMonitor<DnsOptions> options; private readonly IOptionsMonitor<DnsOptions> options;
private readonly IOptionsMonitor<GithubLookupFactoryOptions> lookupOptions;
private readonly IOptionsMonitor<GithubReverseProxyOptions> reverseProxyOptions;
private readonly ILogger<GithubRequestResolver> logger; private readonly ILogger<GithubRequestResolver> logger;
/// <summary> /// <summary>
@ -32,10 +35,14 @@ namespace FastGithub.Dns
public GithubRequestResolver( public GithubRequestResolver(
IGithubScanResults githubScanResults, IGithubScanResults githubScanResults,
IOptionsMonitor<DnsOptions> options, IOptionsMonitor<DnsOptions> options,
IOptionsMonitor<GithubLookupFactoryOptions> lookupOptions,
IOptionsMonitor<GithubReverseProxyOptions> reverseProxyOptions,
ILogger<GithubRequestResolver> logger) ILogger<GithubRequestResolver> logger)
{ {
this.githubScanResults = githubScanResults; this.githubScanResults = githubScanResults;
this.options = options; this.options = options;
this.lookupOptions = lookupOptions;
this.reverseProxyOptions = reverseProxyOptions;
this.logger = logger; this.logger = logger;
} }
@ -50,36 +57,42 @@ namespace FastGithub.Dns
var response = Response.FromRequest(request); var response = Response.FromRequest(request);
var question = request.Questions.FirstOrDefault(); var question = request.Questions.FirstOrDefault();
if (question != null && question.Type == RecordType.A) if (question == null || question.Type != RecordType.A)
{ {
var domain = question.Name.ToString(); return response;
var address = this.githubScanResults.FindBestAddress(domain); }
var domain = question.Name.ToString();
if (this.lookupOptions.CurrentValue.Domains.Contains(domain) == false)
{
return response;
}
if (this.reverseProxyOptions.CurrentValue.Enable == false)
{
var address = this.githubScanResults.FindBestAddress(domain);
if (address != null) if (address != null)
{ {
if (this.options.CurrentValue.UseReverseProxy == false) var ttl = this.options.CurrentValue.GithubTTL;
var record = new IPAddressResourceRecord(question.Name, address, ttl);
response.AnswerRecords.Add(record);
this.logger.LogInformation(record.ToString());
}
}
else
{
var localhost = System.Net.Dns.GetHostName();
var addresses = await System.Net.Dns.GetHostAddressesAsync(localhost);
var ttl = TimeSpan.FromMinutes(1d);
foreach (var item in addresses)
{
if (item.AddressFamily == AddressFamily.InterNetwork)
{ {
var ttl = this.options.CurrentValue.GithubTTL; var record = new IPAddressResourceRecord(question.Name, item, ttl);
var record = new IPAddressResourceRecord(question.Name, address, ttl);
response.AnswerRecords.Add(record); response.AnswerRecords.Add(record);
this.logger.LogInformation(record.ToString()); this.logger.LogInformation(record.ToString());
} }
else
{
var localhost = System.Net.Dns.GetHostName();
var addresses = await System.Net.Dns.GetHostAddressesAsync(localhost);
var ttl = TimeSpan.FromMinutes(1d);
foreach (var item in addresses)
{
if (item.AddressFamily == AddressFamily.InterNetwork)
{
var record = new IPAddressResourceRecord(question.Name, item, ttl);
response.AnswerRecords.Add(record);
this.logger.LogInformation(record.ToString());
}
}
}
} }
} }

View File

@ -0,0 +1,26 @@
using Yarp.ReverseProxy.Forwarder;
namespace FastGithub.ReverseProxy
{
/// <summary>
/// 反向代理选项
/// </summary>
[Options("ReverseProxy")]
public class GithubReverseProxyOptions
{
/// <summary>
/// 是否启用
/// </summary>
public bool Enable { get; set; } = true;
/// <summary>
/// 每个服务的最大代理连接数
/// </summary>
public int MaxConnectionsPerServer { get; set; } = int.MaxValue;
/// <summary>
/// 请求配置
/// </summary>
public ForwarderRequestConfig ForwarderRequestConfig { get; set; } = new();
}
}

View File

@ -19,7 +19,7 @@ namespace FastGithub.ReverseProxy
/// <summary> /// <summary>
/// Token取消源 /// Token取消源
/// </summary> /// </summary>
private readonly CancellationTokenSource tokenSource = new CancellationTokenSource(); private readonly CancellationTokenSource tokenSource = new();
/// <summary> /// <summary>
/// 具有生命周期的HttpHandler /// 具有生命周期的HttpHandler

View File

@ -1,4 +1,5 @@
using System; using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
@ -11,6 +12,8 @@ namespace FastGithub.ReverseProxy
/// </summary> /// </summary>
sealed class LifetimeHttpHandlerCleaner sealed class LifetimeHttpHandlerCleaner
{ {
private readonly ILogger logger;
/// <summary> /// <summary>
/// 当前监视生命周期的记录的数量 /// 当前监视生命周期的记录的数量
/// </summary> /// </summary>
@ -27,6 +30,15 @@ namespace FastGithub.ReverseProxy
/// </summary> /// </summary>
public TimeSpan CleanupInterval { get; set; } = TimeSpan.FromSeconds(10d); public TimeSpan CleanupInterval { get; set; } = TimeSpan.FromSeconds(10d);
/// <summary>
/// LifetimeHttpHandler清理器
/// </summary>
/// <param name="logger"></param>
public LifetimeHttpHandlerCleaner(ILogger logger)
{
this.logger = logger;
}
/// <summary> /// <summary>
/// 添加要清除的httpHandler /// 添加要清除的httpHandler
/// </summary> /// </summary>
@ -52,18 +64,16 @@ namespace FastGithub.ReverseProxy
{ {
while (true) while (true)
{ {
await Task await Task.Delay(this.CleanupInterval);
.Delay(this.CleanupInterval)
.ConfigureAwait(false);
if (this.Cleanup() == true) if (this.Cleanup() == true)
{ {
break; break;
} }
} }
} }
catch (Exception) catch (Exception ex)
{ {
this.logger.LogError(ex, "清理HttpMessageHandler出现不可预期的异常");
// 这是应该不可能发生的 // 这是应该不可能发生的
} }
} }
@ -76,6 +86,8 @@ namespace FastGithub.ReverseProxy
private bool Cleanup() private bool Cleanup()
{ {
var cleanCount = this.trackingEntries.Count; var cleanCount = this.trackingEntries.Count;
this.logger.LogTrace($"尝试清理{cleanCount}条HttpMessageHandler");
for (var i = 0; i < cleanCount; i++) for (var i = 0; i < cleanCount; i++)
{ {
this.trackingEntries.TryDequeue(out var entry); this.trackingEntries.TryDequeue(out var entry);
@ -87,12 +99,14 @@ namespace FastGithub.ReverseProxy
continue; continue;
} }
this.logger.LogTrace($"释放了{entry.GetHashCode()}@HttpMessageHandler");
entry.Dispose(); entry.Dispose();
if (Interlocked.Decrement(ref this.trackingEntryCount) == 0) if (Interlocked.Decrement(ref this.trackingEntryCount) == 0)
{ {
return true; return true;
} }
} }
return false; return false;
} }
@ -116,10 +130,7 @@ namespace FastGithub.ReverseProxy
/// 获取是否可以释放资源 /// 获取是否可以释放资源
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public bool CanDispose public bool CanDispose => this.weakReference.IsAlive == false;
{
get => this.weakReference.IsAlive == false;
}
/// <summary> /// <summary>
/// 监视生命周期的记录 /// 监视生命周期的记录
@ -131,6 +142,9 @@ namespace FastGithub.ReverseProxy
this.weakReference = new WeakReference(handler); this.weakReference = new WeakReference(handler);
} }
/// <summary>
/// 释放资源
/// </summary>
public void Dispose() public void Dispose()
{ {
try try

View File

@ -1,6 +1,7 @@
using FastGithub.Scanner; using FastGithub.Scanner;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System; using System;
using System.Net.Http; using System.Net.Http;
using System.Net.Security; using System.Net.Security;
@ -15,6 +16,7 @@ namespace FastGithub.ReverseProxy
sealed class NoneSniHttpClientFactory sealed class NoneSniHttpClientFactory
{ {
private readonly IGithubScanResults githubScanResults; private readonly IGithubScanResults githubScanResults;
private readonly IOptionsMonitor<GithubReverseProxyOptions> options;
private readonly ILogger<NoneSniHttpClientFactory> logger; private readonly ILogger<NoneSniHttpClientFactory> logger;
/// <summary> /// <summary>
@ -25,12 +27,12 @@ namespace FastGithub.ReverseProxy
/// <summary> /// <summary>
/// 具有生命周期的httpHandler延时创建对象 /// 具有生命周期的httpHandler延时创建对象
/// </summary> /// </summary>
private Lazy<LifetimeHttpHandler> lifeTimeHttpHandlerLazy; private volatile Lazy<LifetimeHttpHandler> lifeTimeHttpHandlerLazy;
/// <summary> /// <summary>
/// HttpHandler清理器 /// HttpHandler清理器
/// </summary> /// </summary>
private readonly LifetimeHttpHandlerCleaner httpHandlerCleaner = new LifetimeHttpHandlerCleaner(); private readonly LifetimeHttpHandlerCleaner httpHandlerCleaner;
/// <summary> /// <summary>
@ -39,11 +41,14 @@ namespace FastGithub.ReverseProxy
/// <param name="githubScanResults"></param> /// <param name="githubScanResults"></param>
public NoneSniHttpClientFactory( public NoneSniHttpClientFactory(
IGithubScanResults githubScanResults, IGithubScanResults githubScanResults,
IOptionsMonitor<GithubReverseProxyOptions> options,
ILogger<NoneSniHttpClientFactory> logger) ILogger<NoneSniHttpClientFactory> logger)
{ {
this.githubScanResults = githubScanResults; this.githubScanResults = githubScanResults;
this.options = options;
this.logger = logger; this.logger = logger;
this.lifeTimeHttpHandlerLazy = new Lazy<LifetimeHttpHandler>(this.CreateHttpHandler, true); this.lifeTimeHttpHandlerLazy = new Lazy<LifetimeHttpHandler>(this.CreateHttpHandler, true);
this.httpHandlerCleaner = new LifetimeHttpHandlerCleaner(logger);
} }
/// <summary> /// <summary>
@ -53,7 +58,7 @@ namespace FastGithub.ReverseProxy
public HttpMessageInvoker CreateHttpClient() public HttpMessageInvoker CreateHttpClient()
{ {
var handler = this.lifeTimeHttpHandlerLazy.Value; var handler = this.lifeTimeHttpHandlerLazy.Value;
return new HttpMessageInvoker(handler, disposeHandler: false); return new HttpMessageInvoker(handler);
} }
/// <summary> /// <summary>
@ -67,6 +72,7 @@ namespace FastGithub.ReverseProxy
Proxy = null, Proxy = null,
UseProxy = false, UseProxy = false,
AllowAutoRedirect = false, AllowAutoRedirect = false,
MaxConnectionsPerServer = this.options.CurrentValue.MaxConnectionsPerServer,
ConnectCallback = async (ctx, ct) => ConnectCallback = async (ctx, ct) =>
{ {
var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
@ -77,8 +83,12 @@ namespace FastGithub.ReverseProxy
return stream; return stream;
} }
var sslStream = new SslStream(stream, leaveInnerStreamOpen: false, delegate { return true; }); var sslStream = new SslStream(stream, leaveInnerStreamOpen: false);
await sslStream.AuthenticateAsClientAsync(string.Empty, null, false); await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions
{
TargetHost = string.Empty,
RemoteCertificateValidationCallback = delegate { return true; }
}, ct);
return sslStream; return sslStream;
} }
}; };

View File

@ -24,12 +24,13 @@ namespace FastGithub
{ {
var httpForwarder = app.ApplicationServices.GetRequiredService<IHttpForwarder>(); var httpForwarder = app.ApplicationServices.GetRequiredService<IHttpForwarder>();
var httpClientFactory = app.ApplicationServices.GetRequiredService<NoneSniHttpClientFactory>(); var httpClientFactory = app.ApplicationServices.GetRequiredService<NoneSniHttpClientFactory>();
var options = app.ApplicationServices.GetRequiredService<IOptionsMonitor<GithubLookupFactoryOptions>>(); var lookupOptions = app.ApplicationServices.GetRequiredService<IOptionsMonitor<GithubLookupFactoryOptions>>();
var options = app.ApplicationServices.GetRequiredService<IOptionsMonitor<GithubReverseProxyOptions>>();
app.Use(next => async context => app.Use(next => async context =>
{ {
var host = context.Request.Host.Host; var host = context.Request.Host.Host;
if (options.CurrentValue.Domains.Contains(host) == false) if (lookupOptions.CurrentValue.Domains.Contains(host) == false)
{ {
await context.Response.WriteAsJsonAsync(new { message = $"不支持以{host}访问" }); await context.Response.WriteAsJsonAsync(new { message = $"不支持以{host}访问" });
} }
@ -38,7 +39,8 @@ namespace FastGithub
var port = context.Request.Host.Port ?? 443; var port = context.Request.Host.Port ?? 443;
var destinationPrefix = $"http://{host}:{port}/"; var destinationPrefix = $"http://{host}:{port}/";
var httpClient = httpClientFactory.CreateHttpClient(); var httpClient = httpClientFactory.CreateHttpClient();
await httpForwarder.SendAsync(context, destinationPrefix, httpClient); var requestConfig = options.CurrentValue.ForwarderRequestConfig;
await httpForwarder.SendAsync(context, destinationPrefix, httpClient, requestConfig);
} }
}); });

View File

@ -39,7 +39,9 @@ namespace FastGithub.Scanner
{ {
var builder = new UriBuilder(uri) var builder = new UriBuilder(uri)
{ {
Host = address.ToString() Scheme = Uri.UriSchemeHttp,
Host = address.ToString(),
Port = 443
}; };
request.RequestUri = builder.Uri; request.RequestUri = builder.Uri;
request.Headers.Host = uri.Host; request.Headers.Host = uri.Host;

View File

@ -1,4 +1,4 @@
using System; using System.Collections.Generic;
namespace FastGithub.Scanner namespace FastGithub.Scanner
{ {
@ -11,6 +11,6 @@ namespace FastGithub.Scanner
/// <summary> /// <summary>
/// 反查的域名 /// 反查的域名
/// </summary> /// </summary>
public string[] Domains { get; set; } = Array.Empty<string>(); public HashSet<string> Domains { get; set; } = new();
} }
} }

View File

@ -50,8 +50,12 @@ namespace FastGithub
return stream; return stream;
} }
var sslStream = new SslStream(stream, leaveInnerStreamOpen: false, delegate { return true; }); var sslStream = new SslStream(stream, leaveInnerStreamOpen: false);
await sslStream.AuthenticateAsClientAsync(string.Empty, null, false); await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions
{
TargetHost = string.Empty,
RemoteCertificateValidationCallback = delegate { return true; }
}, ct);
return sslStream; return sslStream;
} }
}); });

View File

@ -2,8 +2,14 @@
"Dns": { "Dns": {
"UpStream": "114.114.114.114", // dns "UpStream": "114.114.114.114", // dns
"GithubTTL": "00:10:00", // github "GithubTTL": "00:10:00", // github
"SetToLocalMachine": true, // 使dns(windows) "SetToLocalMachine": true // 使dns(windows)
"UseReverseProxy": true // 使访github },
"ReverseProxy": {
"Enable": true, // 使访github,
"MaxConnectionsPerServer": 100, //
"ForwarderRequestConfig": {
"Timeout": "00:02:00" //
}
}, },
"Lookup": { // ip "Lookup": { // ip
"IPAddressComProvider": { "IPAddressComProvider": {