not-ilinked / Anarchy

The superior Discord API wrapper

Home Page:https://anarchyteam.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ApiConfig.Proxy seems redundant. Use HttpClient.DefaultProxy instead.

andreas-henning opened this issue · comments

I don't understand why Anarchy deals with proxies the way that it does. This is how I setup the proxy so fiddler can intercept the traffic sent between my Anarchy based software and the discord server:

var client = new DiscordSocketClient(new DiscordSocketConfig()
{
    Proxy = new() { Host = "127.0.0.1", Port = 8888 }
});

The instance being created above is an Discord.AnarchyProxy. There are two proxy objects used by Anarchy, each based on a different type:

  • Discord.AnarchyProxy
  • Leaf.xNet.ProxyClient

The latter is created using the configuration information of the former (see DiscordSocketClient.FinishConfig()), meaning any traffic routed via either proxy instance is sent to the same proxy server.

The latter is used to route all messages except for messages sent by the extension method SendFileAsync (signature below) which is the only place the former is used directly:

public static async Task<DiscordMessage> SendFileAsync(
    this DiscordClient client,
    ulong channelId,
    string fileName,
    byte[] fileData,
    string message = null, bool tts = false)

I'm not sure any of this is necessary. The .NET framework's HttpClient already has explicit support for setting up a proxy server that is used by anything sending data via HttpClient:

HttpClient.DefaultProxy = new WebProxy("127.0.0.1", 8888);

Configuring the static HttpClient.DefautProxy already has the same effect as configuring AnarchyProxy.

Why does Anarchy use this setup which requires two distinct proxy instances of different types? I don't know, but it seems using the framework's built in method to configure a client's proxy would be the preferable approach.

I'd be happy to submit a pull request which removes the usages of AnarchyProxy and Leaf.xNet.ProxyClient from the code base.

The abstraction exists because of the different between Leaf.xNet and the standard HTTP library. Leaf.xNet supports SOCKS proxies while the standard lib doesn't (i think this may have been changed in .NET 6 tho).

Hey @not-ilinked

Thank you! Got it. AnarchyProxy abstracts away differences between the two underlying proxy implementations.

This is from DiscordHttpClient.cs, which is what I think everything proxy-related boils down to:

if (_discordClient.Proxy == null || _discordClient.Proxy.Type == ProxyType.HTTP)
{
    HttpClient client = new HttpClient(new HttpClientHandler() { Proxy = _discordClient.Proxy == null ? null : new WebProxy(_discordClient.Proxy.Host, _discordClient.Proxy.Port) });
    if (_discordClient.Token != null)
        client.DefaultRequestHeaders.Add("Authorization", _discordClient.Token);

    if (_discordClient.User != null && _discordClient.User.Type == DiscordUserType.Bot)
        client.DefaultRequestHeaders.Add("User-Agent", "Anarchy/0.8.1.2");
    else
    {
        client.DefaultRequestHeaders.Add("User-Agent", _discordClient.Config.SuperProperties.UserAgent);
        client.DefaultRequestHeaders.Add("X-Super-Properties", _discordClient.Config.SuperProperties.ToBase64());
    }

    var response = await client.SendAsync(new HttpRequestMessage() 
    { 
        Content = hasData ? new System.Net.Http.StringContent(json, Encoding.UTF8, "application/json") : null, 
        Method = new System.Net.Http.HttpMethod(method.ToString()), 
        RequestUri = new Uri(endpoint) 
    });

    resp = new DiscordHttpResponse((int)response.StatusCode, response.Content.ReadAsStringAsync().Result);
}
else
{
    HttpRequest msg = new HttpRequest
    {
        IgnoreProtocolErrors = true,
        UserAgent = _discordClient.User != null && _discordClient.User.Type == DiscordUserType.Bot ? "Anarchy/0.8.1.2" : _discordClient.Config.SuperProperties.UserAgent,
        Authorization = _discordClient.Token
    };

    if (hasData)
        msg.AddHeader(HttpHeader.ContentType, "application/json");

    if (_discordClient.User == null || _discordClient.User.Type == DiscordUserType.User) msg.AddHeader("X-Super-Properties", _discordClient.Config.SuperProperties.ToBase64());
    if (_discordClient.Proxy != null) msg.Proxy = _discordClient.Proxy;

    var response = msg.Raw(method, endpoint, hasData ? new Leaf.xNet.StringContent(json) : null);

    resp = new DiscordHttpResponse((int)response.StatusCode, response.ToString());
}

I have a bunch of questions surrounding this. I know they are stupid questions. Why I really need you insights though:

.NET 6.0 has introduced support for SOCKS proxies. A global/static proxy for the entire application can be configured like so:

HttpClient.DefaultProxy = new WebProxy("socks5://127.0.0.1:9050") // SOCKS proxy

I'm currently just using a normal HTTP proxy which I've configured like so:

HttpClient.DefaultProxy = new WebProxy("127.0.0.1", 8888); // HTTP proxy

This already works just fine. This ignores everything proxy related in Anarchy. It slides the proxy in at the .NET framwork level, a level below the proxy support implemented by Anarchy.

In a .NET 6.0 port we can remove all of Anarchy's explicit proxy support, and the IF/ELSE branch above, as proxy support is now provided entirely by the .NET framework. Correct?

SOCKS support is the only reason we need Leaf. In a .NET 6.0 port we can remove the dependency on Leaf. Agreed?

Yeah, SOCKS support is the only reason we used Leaf, and a .NET 6 port sounds nice :)
Keep in mind that some applications may want to use multiple clients with different, so obviously we'll still need some way to set one for each specific client (something like client.HttpClient.HttpClient) (maybe also change DiscordHttpClient to DiscordRESTClient and change field names accordingly).

Yeah, SOCKS support is the only reason we used Leaf, and a .NET 6 port sounds nice :)
Keep in mind that some applications may want to use multiple clients with different, so obviously we'll still need some way to set one for each specific client (something like client.HttpClient.HttpClient) (maybe also change DiscordHttpClient to DiscordRESTClient and change field names accordingly).

Oh! Okay. I hadn't considered the possibility of using multiple clients in a single application. Makes sense. I can see where this is headed now.

For me @not-ilinked, the big one is PR #3318. That is the basis for everything else .NET 6.0 related. I'd like to see that one merged into master. That would also allow you to close quite a few of the other PRs.

PR #3318 deliberately changes the code base as little as possible, but it still touches almost every file. That's just the nature of such a port and a result of Visual Studio's code cleanup (removes unused using statements and reorders them).

I'd love to finish my .NET 6.0 proxy cleanup work, and getting PR #3318 merged into master would be a very helpful first step in that process. It's the only one I'd consider urgent right now. What do you say?

If we can do that I would delete my other PRs and create new ones based on the new HEAD.

The PR has been merged :)
I'm not 100% sure what we'll do about the NuGet issue, since i don't really have the tooling to run it but also don't want to completely hand over the project yet. Maybe you could send me the .nupkg file whenever the new version is ready and i could upload that? I don't really see what's beneficial in moving to a whole different package since that would create confusion. Thanks :)

Thank you @not-ilinked

I'm not 100% sure I know what "the NuGet issue" refers to.

Now that Anarchy officially targets .NET 6.0, I have no reason to publish a distinct .NET 6.0 variant of Anarchy on nuget.org. That will be removed.

If you're just not able to generate a nuget package I'd be happy to give you the binary. No problem at all.

Sounds great then. Think that clears everything up :)