Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8197921

Fail to send authentication for HTTP proxy tunneling HTTPS via CONNECT

XMLWordPrintable

    • x86_64
    • os_x

      FULL PRODUCT VERSION :
      java version "1.8.0_172-ea"
      Java(TM) SE Runtime Environment (build 1.8.0_172-ea-b03)
      Java HotSpot(TM) 64-Bit Server VM (build 25.172-b03, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Darwin hostname 17.4.0 Darwin Kernel Version 17.4.0: Sun Dec 17 09:19:54 PST 2017; root:xnu-4570.41.2~1/RELEASE_X86_64 x86_64

      EXTRA RELEVANT SYSTEM CONFIGURATION :
      running squid proxy in local docker instance

      A DESCRIPTION OF THE PROBLEM :
      Tunneling a HTTPS request through a proxy that uses authorization fails unless a http request is performed first.

      using a HTTP backend works fine. It also seems to populate a cache that is used for HTTPS, so performing a HTTP request first seems to enable HTTPS.

      REGRESSION. Last worked in version 8u151

      ADDITIONAL REGRESSION INFORMATION:
      last working version:
      java version "1.8.0_102"
      Java(TM) SE Runtime Environment (build 1.8.0_102-b14)
      Java HotSpot(TM) 64-Bit Server VM (build 25.102-b14, mixed mode)

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1) start a docker container with a local squid proxy w/ auth
      docker run -e SQUID_USERNAME=foo -e SQUID_PASSWORD=bar -p 3128:3128 robhaswell/squid-authenticated

      2) execute the provided java program

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      the output should look like this (the first request should be successful and the password should be requested in the initial https call)

      === BEGIN https ===
      >>> requesting password
      {
        "headers": {
          "Accept": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2",
          "Connection": "close",
          "Host": "httpbin.org",
          "User-Agent": "Java/1.8.0_102"
        }
      }

      === END https ===
      === BEGIN http ===
      {
        "headers": {
          "Accept": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2",
          "Cache-Control": "max-age=259200",
          "Connection": "close",
          "Host": "httpbin.org",
          "User-Agent": "Java/1.8.0_102"
        }
      }

      === END http ===
      === BEGIN https ===
      {
        "headers": {
          "Accept": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2",
          "Connection": "close",
          "Host": "httpbin.org",
          "User-Agent": "Java/1.8.0_102"
        }
      }

      === END https ===
      ACTUAL -
      the initial HTTPS request fails and does call the Authenticator

      === BEGIN https ===
      ERROR: Unable to tunnel through proxy. Proxy returns "HTTP/1.1 407 Proxy Authentication Required"
      === END https ===
      === BEGIN http ===
      >>> requesting password
      {
        "headers": {
          "Accept": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2",
          "Cache-Control": "max-age=259200",
          "Connection": "close",
          "Host": "httpbin.org",
          "User-Agent": "Java/1.8.0_172-ea"
        }
      }

      === END http ===
      === BEGIN https ===
      {
        "headers": {
          "Accept": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2",
          "Connection": "close",
          "Host": "httpbin.org",
          "User-Agent": "Java/1.8.0_172-ea"
        }
      }

      === END https ===

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import static java.util.Arrays.asList;

      import java.io.InputStream;
      import java.net.Authenticator;
      import java.net.PasswordAuthentication;
      import java.net.URI;
      import java.util.Scanner;

      public class Sample
      {
          public static void main( String[] args )
          {
              for ( String protocol : asList( "http", "https" ) )
              {
                  System.setProperty( protocol + ".proxyHost", "localhost" );
                  System.setProperty( protocol + ".proxyPort", "3128" );
              }

              Authenticator.setDefault( new Authenticator()
              {
                  @Override
                  protected PasswordAuthentication getPasswordAuthentication()
                  {
                      System.out.println( ">>> requesting password" );
                      return new PasswordAuthentication( "foo", "bar".toCharArray() );
                  }
              } );

              for ( String protocol : asList( "https", "http", "https" ) )
              {
                  System.out.println( "=== BEGIN " + protocol + " ===" );

                  try ( InputStream in = new URI( protocol, "httpbin.org", "/headers", null ).toURL().openStream() )
                  {
                      System.out.println( asString( in ) );
                  }
                  catch ( Exception e )
                  {
                      System.out.println( "ERROR: " + e.getMessage() );
                  }

                  System.out.println( "=== END " + protocol + " ===" );
              }
          }

          private static String asString( InputStream in )
          {
              try ( Scanner scanner = new Scanner( in ) )
              {
                  scanner.useDelimiter( "\0" );
                  return scanner.hasNext() ? scanner.next() : "";
              }
          }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      performing a HTTP request beforehand seems to create a state where subsequent request succeed but this requires code changes in existing applications and has other negative implications as well.

            psonal Pallavi Sonal (Inactive)
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: