Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AndroidClientHandler doesn't support proxy servers #2123

Closed
dominik-weber opened this issue Aug 31, 2018 · 7 comments
Closed

AndroidClientHandler doesn't support proxy servers #2123

dominik-weber opened this issue Aug 31, 2018 · 7 comments
Assignees
Labels
enhancement Proposed change to current functionality.
Milestone

Comments

@dominik-weber
Copy link
Contributor

Please add support for HTTP proxy server configuration in AndroidClientHandler :)

@Cheesebaron
Copy link

This is the related line of code.

https://github.com/xamarin/xamarin-android/blob/3f743678d3e7c19fc6f6e8f6928bfe4ae7d55ff0/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs#L258

This should probably check whether Proxy was set on the HttpClientHandler and use that, then fallback to empty call to OpenConnection() if no proxy was set.

@JonDouglas JonDouglas added this to the d16-0 milestone Oct 30, 2018
@JonDouglas JonDouglas added the enhancement Proposed change to current functionality. label Oct 30, 2018
grendello added a commit to grendello/xamarin-android that referenced this issue Nov 5, 2018
Fixes: dotnet#2123

Implement proxy support for the Java HTTP client by "translating" the proxy
information specified in `HttpClientHandler.Proxy` to the format accepted and
expected by the Java client. Since the translation may involve a DNS lookup, it
is performed in a separate task. No caching is attempted in our client code.
jonpryor pushed a commit that referenced this issue Nov 8, 2018
Fixes: #2123

Implement proxy support for `AndroidClientHandler` by "translating"
the proxy information specified in `HttpClientHandler.Proxy` to the
format accepted and expected by the Java client.

Since the translation may involve a DNS lookup, it is performed in a
separate task.

No caching is attempted in our client code.
@dominik-weber
Copy link
Contributor Author

I know I'm a bit late here, but shouldn't the default behavior (if you just call new AndroidClientHandler() without modifying any properties) be to use the system default proxy? Currently these settings are ignored.
@grendello I think support for using the default proxy could be implemented fairly easily by adding something like this to the GetJavaProxy method:

            URI java_uri = new URI(EncodeUrl(destination));
            proxy = ProxySelector.Default?.Select(java_uri).FirstOrDefault();

@johnthiriet
Copy link

I totally agree with @dominik-weber and this is what we have implemented in our project in order to be able to debug network calls with Charles Proxy.

@mzekrallah
Copy link

mzekrallah commented May 31, 2019

Any update on this guys ? I find it extremely strange that something as basic and 'core' to any network layer implementation is missing and it has been almost 8 years since Xamarin started !

@grendello
Copy link
Contributor

The proxy should be used only if the Proxy property is not null (and configured for the specified URL) and the UseProxy property is true - which is what we implement.

@dominik-weber
Copy link
Contributor Author

dominik-weber commented May 5, 2022

@grendello Both NSUrlSessionHandler (iOS) and HttpClientHandler (.NET) respect the system default proxy, ie. they respect the proxy settings configured in the phone settings app.

See these code examples:

new HttpClient(new NSUrlSessionHandler()).GetStringAsync("https://google.com");
-> this will use the proxy settings configured in the iPhone settings app

new HttpClient(new AndroidMessageHandler()).GetStringAsync("https://google.com");
-> this will ignore the proxy settings configured in the Android settings app

I think the behavior of the different handlers should be consolidated, by default both should use the proxy settings that have been configured system wide.

Please see also the documentation for HttpClientHandler.Proxy (link), which states that:

If no proxy is specified in a config file and the Proxy property is unspecified, the handler uses the proxy settings inherited from Internet Explorer on the local computer. If there are no proxy settings in Internet Explorer, the request is sent directly to the server.

This conflicts with how you explained above the AndroidClientHandler / AndroidMessageHandler should work.

In the mean time, I'm using this code to manually implement support for the default proxy using the Java.Net.ProxySelector API:

var handler = new AndroidMessageHandler();

var selector = ProxySelector.Default;
if (selector != null)
    handler.Proxy = new JavaWebProxy(selector);

With the following JavaWebProxy class:

/// <summary>
/// <see cref="IWebProxy"/> implementation that uses the Java <see cref="ProxySelector"/> API to resolve the proxy information.
/// </summary>
internal class JavaWebProxy : IWebProxy
{
    private readonly ProxySelector _selector;

    public ICredentials Credentials { get; set; }

    public JavaWebProxy(ProxySelector selector)
    {
        _selector = selector ?? throw new ArgumentNullException(nameof(selector));
    }

    public Uri GetProxy(Uri destination)
    {
        var javaUri = new URI(EncodeUrl(destination));

        var proxy = _selector.Select(javaUri).FirstOrDefault();
        if (proxy != null && proxy != Proxy.NoProxy && proxy.Address() is InetSocketAddress address)
        {
            var host = address.HostString;
            var port = address.Port;
            return new UriBuilder("http", host, port).Uri;
        }

        return null;
    }

    public bool IsBypassed(Uri host)
    {
        return GetProxy(host) == null;
    }

    private string EncodeUrl(Uri url)
    {
        // Copied from https://github.com/xamarin/xamarin-android/blob/master/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs
        // Fixes an issue where urls with unencoded spaces are not recognized by the Java URI class.

        if (url == null)
            return string.Empty;

        // UriBuilder takes care of encoding everything properly
        var bldr = new UriBuilder(url);
        if (url.IsDefaultPort)
            bldr.Port = -1; // Avoids adding :80 or :443 to the host name in the result

        // bldr.Uri.ToString () would ruin the good job UriBuilder did
        return bldr.ToString();
    }
}

For reference, this is how you configure the system wide proxy settings on Android:
image

@dominik-weber
Copy link
Contributor Author

UPDATE: Please ignore my last post. This issue has actually been resolved in the past: #3058 #3060. The default proxy settings are used now when UseProxy == true and Proxy == null (which is the default configuration)

@ghost ghost locked as resolved and limited conversation to collaborators Jun 7, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement Proposed change to current functionality.
Projects
None yet
Development

No branches or pull requests

6 participants