<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>Hello Alessandro,</p>
    <p>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
      <a class="moz-txt-link-freetext" href="https://mail.openjdk.org/mailman/listinfo/net-dev">https://mail.openjdk.org/mailman/listinfo/net-dev</a>.</p>
    <p>-Jaikiran<br>
    </p>
    <div class="moz-cite-prefix">On 13/05/24 3:28 am, Alessandro Autiero
      wrote:<br>
    </div>
    <blockquote type="cite"
cite="mid:CAM0RScncCo+J2PMpEA71Ug8RXXL9QMP+KN86Y+h1x9kn_qm2AA@mail.gmail.com">
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <div dir="ltr">
        <div>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.<br>
        </div>
        <div><br>
        </div>
        <div>There are three classes in the <a href="http://java.net"
            target="_blank" moz-do-not-send="true">java.net</a> package
          that have proxy support:<br>
          <ul>
            <li>java.net.Socket <br>
              Introduced in Java 1.0, supports HTTP(S)/SOCKS proxies
              modelled by java.net.Proxy through the
              java.net.Socket(java.net.Proxy) constructor<br>
            </li>
            <li>java.net.HttpURLConnection <br>
              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<br>
            </li>
            <li>java.net.HttpClient (Introduced in Java 11)<br>
              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)</li>
          </ul>
          <div>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
            <a
href="https://stackoverflow.com/questions/70011046/how-to-use-a-socks-proxy-with-java-jdk-httpclient"
              moz-do-not-send="true">StackOverFlow</a> 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 <a
href="https://github.com/openjdk/jdk/blob/5053b70a7fc67ce9b73dbeecbdd88fbc34d45e04/src/java.base/share/classes/java/net/ProxySelector.java#L194"
              target="_blank" moz-do-not-send="true">java.net.ProxySelector$StaticProxySelector</a>,
            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.<br>
          </div>
          <div><br>
          </div>
          <div>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.<br>
          </div>
          <div>
            <div><br>
            </div>
            <div>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:<br>
              <ul>
                <li>java.net.Socket <br>
                  Not supported<br>
                </li>
                <li>java.net.HttpURLConnection <br>
                  Supported through the setAuthenticator(Authenticator)
                  method introduced in Java 9</li>
                <li>java.net.HttpClient<br>
                  Supported through the authenticator(Authenticator)
                  method in its builder</li>
              </ul>
            </div>
          </div>
          <div>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. <br>
            <br>
          </div>
          <div>I am happy to work on all, if any, of the enhancements
            that are considered feasible.<br>
          </div>
          <div>I'm also looking forward to any possible criticism and
            feedback.<br>
          </div>
          <div>Thanks in advance.<br>
          </div>
          <div><br>
          </div>
        </div>
        <div>I<br>
        </div>
      </div>
    </blockquote>
  </body>
</html>