Enhance proxy support in java.net core libraries

Jaikiran Pai jai.forums2013 at gmail.com
Mon May 13 01:24:54 UTC 2024


Hello Alessandro,

I've moved this discussion to net-dev mailing list which is more 
relevant for this discussion (and Bcced core-libs-dev). If you haven't 
already subscribed to net-dev, then you can do it here 
https://mail.openjdk.org/mailman/listinfo/net-dev.

-Jaikiran

On 13/05/24 3:28 am, Alessandro Autiero wrote:
> Hello, my name is Alessandro Autiero and I'd like to propose three 
> enhancements for the java core libraries to better support proxies in 
> network components of the JDK.
>
> There are three classes in the java.net <http://java.net> package that 
> have proxy support:
>
>   * java.net.Socket
>     Introduced in Java 1.0, supports HTTP(S)/SOCKS proxies modelled by
>     java.net.Proxy through the java.net.Socket(java.net.Proxy) constructor
>   * java.net.HttpURLConnection
>     Introduced in Java 1.1, supports HTTP(S)/SOCKS proxies modelled by
>     java.net.Proxy through the
>     java.net.URL#openConnection(java.net.Proxy) public method
>   * java.net.HttpClient (Introduced in Java 11)
>     Introduced in Java 11, supports HTTP(S) proxies modelled by
>     java.net.ProxySelector through the public
>     proxy(java.net.ProxySelector) method in its builder or the default
>     java.net.ProxySelector, which can be set by calling
>     java.net.ProxySelector#setDefault(java.net.ProxySelector)
>
> While most proxies provide support for both the HTTP and SOCKS scheme, 
> considering that the older HTTP client API had support for both, 
> developers might choose to use the older api, or to use an external 
> one, if they need or want to provide support for this feature. A quick 
> Google search for a recommendation on which Http Client to use on a 
> modern Java version yields many results that list SOCKS support as a 
> feature to keep in mind when making a choice. While this is not 
> necessarily indicative of the average Java developer sentiment about 
> the feature, I think that it should be considered, alongside a couple 
> of issues that were opened on StackOverFlow 
> <https://stackoverflow.com/questions/70011046/how-to-use-a-socks-proxy-with-java-jdk-httpclient> 
> asking about support for this feature. Accordingly, I propose adding 
> support for SOCKS proxies in java.net.HttpClient. If the change is 
> allowed, consider that the default java.net.ProxySelector is an 
> instance of sun.net.spi.DefaultProxySelector, which supports SOCKS 
> proxies, but this implementation cannot be initialized by the user as 
> it's not exposed by the module system. Starting from Java 9, 
> ProxySelector#of(InetSocketAddress) was introduced, which returns an 
> instance of java.net.ProxySelector$StaticProxySelector 
> <https://github.com/openjdk/jdk/blob/5053b70a7fc67ce9b73dbeecbdd88fbc34d45e04/src/java.base/share/classes/java/net/ProxySelector.java#L194>, 
> a static inner class of ProxySelector introduced in Java 9 which only 
> implements support for HTTP(S) proxies. StaticProxySelector's 
> constructor could be modified from 
> StaticProxySelector(java.net.InetSocketAddress) to 
> StaticProxySelector(java.net.Proxy$Type, java.net.InetSocketAddress) 
> to initialize the java.net.Proxy instance with a specified proxy type 
> instead of hard coding HTTP. Then we could overload the method 
> ProxySelector#of(InetSocketAddress) with 
> ProxySelector#of(java.net.Proxy$Type, InetSocketAddress) method which 
> would invoke the constructor we defined earlier. This change would not 
> be breaking as StaticProxySelector is package private, no public 
> methods would be deleted and the default scheme would still be HTTP. 
> jdk.internal.net.http.HttpRequestImpl uses the ProxySelector in its 
> retrieveProxy method, but checks if the proxy is an HTTP proxy: this 
> would need to be changed as well. Finally, considering that unlike 
> HttpURLConnection, HttpClient doesn't delegate the underlying 
> connection to java.net.Socket, the java.net.http module would need to 
> be enhanced to support SOCKS authentication, which could take more effort.
>
> Another observation that I've made is about authentication. If a proxy 
> requires basic authentication, that is authentication through a 
> username and optionally a password, a developer can implement the 
> java.net.Authenticator class and override the 
> getPasswordAuthentication method. While basic authentication is still 
> the norm for most proxies, it's disabled by default in the JDK since 
> Java 8. Though, it's possible to enable it by overriding the net 
> properties jdk.http.auth.proxying.disabledSchemes and 
> jdk.http.auth.tunneling.disabledSchemes using System.setProperty. I 
> couldn't find an explanation about why this change was implemented, so 
> I can only speculate that it was done to incentivize Java developers 
> to use an IP whitelist instead of basic auth to improve security, 
> assuming that the connection isn't secure(HTTP). The problem though is 
> that the net properties that need to be changed to allow basic proxy 
> authentication are only read only one time in the static initializer 
> of sun.net.www.protocol.http.HttpURLConnection class, the underlying 
> implementation of java.net.HttpURLConnection. So, if for example a 
> library loads this class before the developer calls 
> System.setProperty, the change will have no effect and the 
> authentication will subsequently fail. This may seem like an edge 
> case, but for example in a Spring Boot environment, this exact issue 
> will arise if the property isn't set before calling 
> SpringApplication.run. I think that the best solution would be to 
> remove the disabledTunnelingSchemes and disabledProxyingSchemes static 
> fields from sun.net.www.protocol.http.HttpURLConnection and read the 
> net properties when they are used instead of caching them. This 
> solution is not a breaking change and should be very easy to implement 
> as both fields are only referenced when initializing a 
> AuthenticationHeader in the same class. Additionally, if we can agree 
> on the fact that basic authentication is still the predominant way to 
> provide authentication capabilities for a proxy and/or that disabling 
> it doesn't provide a direct security benefit, I propose to also set 
> jdk.http.auth.proxying.disabledSchemes and 
> jdk.http.auth.tunneling.disabledSchemes to an empty String so no 
> schemes are disabled by default. This change could be breaking for 
> Applications developed starting from Java 8 that expect basic 
> authentication to be disabled, but I think that the scope of the 
> impact would be much smaller, if there would be any at all, than when 
> these flags were introduced breaking basic authentication for existing 
> applications upgrading to Java 8.
>
> The last issue I've noticed is also about authentication. If a 
> developer wants to set an instance of Authenticator instead of relying 
> on the default one, which can be set using 
> Authenticator.setAuthenticator, this may not be possible depending on 
> the implementation:
>
>   * java.net.Socket
>     Not supported
>   * java.net.HttpURLConnection
>     Supported through the setAuthenticator(Authenticator) method
>     introduced in Java 9
>   * java.net.HttpClient
>     Supported through the authenticator(Authenticator) method in its
>     builder
>
> The reason why a developer might want to provide an instance of 
> Authenticator instead of relying on the default one is that, for 
> example, in a concurrent environment where multiple sockets exist, 
> each using a different proxy, if the proxy host and port are the same, 
> but each Socket instance is expected to use a different pair of 
> username and passwords, the default Authenticator cannot determine 
> which pair of credentials should be assigned to a given authentication 
> request as it lacks the scope to make this decision. This change is 
> not breaking as the default authenticator of a Socket would still be 
> the default authenticator, which is the current behaviour. If the 
> change is allowed, a possible solution would be to add a private field 
> named authenticator and a public method 
> setAuthenticator(Authenticator) to java.net.Socket. Then 
> HttpConnectSocketImpl, the socket implementation for a socket using an 
> http(s) proxy, would need to use the authenticator of its delegating 
> socket instead of the default one in the doTunnel method: this is a 
> one line of code change as HttpURLConnection already has support for 
> setAuthenticator from Java 9. Finally, SocksSocketImpl, the socket 
> implementation for a socket using a socks4/5 proxy, would need to use 
> the authenticator of the delegating socket in the authenticate method. 
> instead of calling Authenticator.requestPasswordAuthentication, which 
> uses the default authenticator.
>
> I am happy to work on all, if any, of the enhancements that are 
> considered feasible.
> I'm also looking forward to any possible criticism and feedback.
> Thanks in advance.
>
> I
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/net-dev/attachments/20240513/d8636596/attachment-0001.htm>


More information about the net-dev mailing list