chickensoft-games / GodotEnv

Manage Godot versions and addons from the command line on Windows, macOS, and Linux.
https://www.nuget.org/packages/Chickensoft.GodotEnv/
MIT License
323 stars 14 forks source link

Add explicit proxy support for godotenv #67

Open SydneyDrone opened 3 months ago

SydneyDrone commented 3 months ago

Trying to install godot using godotenv godot install 4.2.2 command, and after successfully download godot, here is what I get at verifying checksum stage:

Detailed Console Message

``` Shell ? Verifying Checksum ERROR System.Net.Http.HttpRequestException: The requested name is valid, but no data of the requested type was found. (raw.githubusercontent.com:443) System.Net.Sockets.SocketException: The requested name is valid, but no data of the requested type was found. at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken) at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token) at System.Net.Sockets.Socket.g__WaitForConnectWithCancellation|277_0(AwaitableSocketAsyncEventArgs saea, ValueTask connectTask, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.AddHttp11ConnectionAsync(HttpRequestMessage request) at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.WaitWithCancellationAsync(CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.GetHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpClient.g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken) at Chickensoft.GodotEnv.Common.Clients.NetworkClient.WebRequestGetAsync(String url) in \home\runner\work\GodotEnv\GodotEnv\GodotEnv\src\common\clients\NetworkClient.cs:49 at Chickensoft.GodotEnv.Features.Godot.Domain.GodotChecksumClient.GetExpectedChecksumForArchive(GodotCompressedArchive archive) in \home\runner\work\GodotEnv\GodotEnv\GodotEnv\src\features\godot\domain\GodotChecksumClient.cs:66 at Chickensoft.GodotEnv.Features.Godot.Domain.GodotChecksumClient.VerifyArchiveChecksum(GodotCompressedArchive archive) in \home\runner\work\GodotEnv\GodotEnv\GodotEnv\src\features\godot\domain\GodotChecksumClient.cs:57 at Chickensoft.GodotEnv.Features.Godot.Domain.GodotRepository.VerifyArchiveChecksum(ILog log, GodotCompressedArchive archive) in \home\runner\work\GodotEnv\GodotEnv\GodotEnv\src\features\godot\domain\GodotRepository.cs:337 at Chickensoft.GodotEnv.Features.Godot.Domain.GodotRepository.DownloadGodot(SemanticVersion version, Boolean isDotnetVersion, Boolean skipChecksumVerification, ILog log, CancellationToken token) in \home\runner\work\GodotEnv\GodotEnv\GodotEnv\src\features\godot\domain\GodotRepository.cs:319 at Chickensoft.GodotEnv.Features.Godot.Commands.GodotInstallCommand.ExecuteAsync(IConsole console) in \home\runner\work\GodotEnv\GodotEnv\GodotEnv\src\features\godot\commands\install\GodotInstallCommand.cs:77 at CliFx.CliApplication.RunAsync(ApplicationSchema applicationSchema, CommandInput commandInput) in \_\CliFx\CliApplication.cs:153 at CliFx.CliApplication.RunAsync(IReadOnlyList`1 commandLineArguments, IReadOnlyDictionary`2 environmentVariables) in \_\CliFx\CliApplication.cs:193 ```

I strongly doubt that cannot using proxy(which links to #49 ) is the reason of this error(I mean, I cannot direct reach github without the help of proxy in my current network envirionment). How can I further locate where goes wrong?

SydneyDrone commented 3 months ago

Turns out to be a local network environment issue. Closed.

SydneyDrone commented 3 months ago

Turns out to be a local network environment issue. Closed.

More specificly speaking, the DNS of raw.githubusercontent.com is polluted and it will point to localhost. By default the HttpClient.cs uses IE proxy settings, but the proxy setting will bypass localhost. In that case, we have to declare the proxy explicitly when instanciating HttpClient's creation to solve this problem. Now since the DNS Pollution is not my personal scienario but happens in the whole Mainland China, I think it's valuable to add proxy support for godotenv, hence reopening this issue.

SydneyDrone commented 3 months ago

Now after adding the explicit proxy support, the WebRequestGetAsync() function in NetworkClient well looks like this:

public async Task<HttpResponseMessage> WebRequestGetAsync(string url, string? proxyUrl = null) {
  var proxy = string.IsNullOrEmpty(proxyUrl) ? WebRequest.GetSystemWebProxy() : new WebProxy(proxyUrl);
  var factory = new ServiceCollection()
    .AddHttpClient(Options.DefaultName)
    .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler { Proxy = proxy })
    .Services
    .BuildServiceProvider()
    .GetService<IHttpClientFactory>();

  var client = factory.CreateClient();

  return await client.GetAsync(url);
}

Now we can see that there might be potential performance issue, since we cached the http client in the current version, but create a factory each time the function is executed. So what is the real situation?

I have used BenchmarkDotNet to get the same 3 pages in 3 ways: no cache, cache the factory, cache the client, and the result looks like that: Method Mean Error StdDev
TestNoCache 1,634.2 ms 32.55 ms 72.80 ms
TestCacheFactory 359.0 ms 6.82 ms 7.29 ms
TestCacheClient 351.5 ms 6.10 ms 5.41 ms

Need to mention that this is the result of the second time, the first time I run this test, the mean time of TestCacheClient goes to 600ms.

jolexxa commented 1 month ago

@SydneyDrone I'm all for improvements which benefit users in mainland China. I will happily accept reasonable improvements to allow proxies.