1 /*
   2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  */
  24 
  25 /*
  26  * @test
  27  * @bug 8163561
  28  * @modules java.base/sun.net.www
  29  *          java.httpclient
  30  * @summary Verify that Proxy-Authenticate header is correctly handled
  31  *
  32  * @run main/othervm ProxyAuthTest
  33  */
  34 
  35 import java.io.BufferedWriter;
  36 import java.io.IOException;
  37 import java.io.InputStream;
  38 import java.io.OutputStream;
  39 import java.io.OutputStreamWriter;
  40 import java.io.PrintWriter;
  41 import java.net.Authenticator;
  42 import java.net.InetSocketAddress;
  43 import java.net.PasswordAuthentication;
  44 import java.net.ProxySelector;
  45 import java.net.ServerSocket;
  46 import java.net.Socket;
  47 import java.net.URI;
  48 import java.net.http.HttpClient;
  49 import java.net.http.HttpResponse;
  50 import java.util.Base64;
  51 
  52 import sun.net.www.MessageHeader;
  53 
  54 public class ProxyAuthTest {
  55     private static final String AUTH_USER = "user";
  56     private static final String AUTH_PASSWORD = "password";
  57 
  58     public static void main(String[] args) throws Exception {
  59         try (ServerSocket ss = new ServerSocket(0)) {
  60             int port = ss.getLocalPort();
  61             MyProxy proxy = new MyProxy(ss);
  62             (new Thread(proxy)).start();
  63             System.out.println("Proxy listening port " + port);
  64 
  65             Auth auth = new Auth();
  66             InetSocketAddress paddr = new InetSocketAddress("localhost", port);
  67 
  68             URI uri = new URI("http://www.google.ie/");
  69             HttpClient client = HttpClient.create()
  70                     .proxy(ProxySelector.of(paddr))
  71                     .authenticator(auth)
  72                     .build();
  73             HttpResponse resp = client.request(uri)
  74                     .GET()
  75                     .responseAsync()
  76                     .get();
  77             if (resp.statusCode() != 404) {
  78                 throw new RuntimeException("Unexpected status code: " + resp.statusCode());
  79             }
  80 
  81             if (auth.isCalled) {
  82                 System.out.println("Authenticator is called");
  83             } else {
  84                 throw new RuntimeException("Authenticator is not called");
  85             }
  86 
  87             if (!proxy.matched) {
  88                 throw new RuntimeException("Proxy authentication failed");
  89             }
  90         }
  91     }
  92 
  93     static class Auth extends Authenticator {
  94         private volatile boolean isCalled;
  95 
  96         @Override
  97         protected PasswordAuthentication getPasswordAuthentication() {
  98             System.out.println("scheme: " + this.getRequestingScheme());
  99             isCalled = true;
 100             return new PasswordAuthentication(AUTH_USER,
 101                     AUTH_PASSWORD.toCharArray());
 102         }
 103     }
 104 
 105     static class MyProxy implements Runnable {
 106         final ServerSocket ss;
 107         private volatile boolean matched;
 108 
 109         MyProxy(ServerSocket ss) {
 110             this.ss = ss;
 111         }
 112 
 113         public void run() {
 114             for (int i = 0; i < 2; i++) {
 115                 try (Socket s = ss.accept();
 116                      InputStream in = s.getInputStream();
 117                      OutputStream os = s.getOutputStream();
 118                      BufferedWriter writer = new BufferedWriter(
 119                              new OutputStreamWriter(os));
 120                      PrintWriter out = new PrintWriter(writer);) {
 121                     MessageHeader headers = new MessageHeader(in);
 122                     System.out.println("Proxy: received " + headers);
 123 
 124                     String authInfo = headers
 125                             .findValue("Proxy-Authorization");
 126                     if (authInfo != null) {
 127                         authenticate(authInfo);
 128                         out.print("HTTP/1.1 404 Not found\r\n");
 129                         out.print("\r\n");
 130                         System.out.println("Proxy: 404");
 131                         out.flush();
 132                     } else {
 133                         out.print("HTTP/1.1 407 Proxy Authorization Required\r\n");
 134                         out.print(
 135                                 "Proxy-Authenticate: Basic realm=\"a fake realm\"\r\n");
 136                         out.print("\r\n");
 137                         System.out.println("Proxy: Authorization required");
 138                         out.flush();
 139                     }
 140                 } catch (IOException x) {
 141                     System.err.println("Unexpected IOException from proxy.");
 142                     x.printStackTrace();
 143                     break;
 144                 }
 145             }
 146         }
 147 
 148         private void authenticate(String authInfo) throws IOException {
 149             try {
 150                 authInfo.trim();
 151                 int ind = authInfo.indexOf(' ');
 152                 String recvdUserPlusPass = authInfo.substring(ind + 1).trim();
 153                 // extract encoded username:passwd
 154                 String value = new String(
 155                         Base64.getMimeDecoder().decode(recvdUserPlusPass));
 156                 String userPlusPassword = AUTH_USER + ":" + AUTH_PASSWORD;
 157                 if (userPlusPassword.equals(value)) {
 158                     matched = true;
 159                     System.out.println("Proxy: client authentication successful");
 160                 } else {
 161                     System.err.println(
 162                             "Proxy: client authentication failed, expected ["
 163                                     + userPlusPassword + "], actual [" + value
 164                                     + "]");
 165                 }
 166             } catch (Exception e) {
 167                 throw new IOException(
 168                         "Proxy received invalid Proxy-Authorization value: "
 169                                 + authInfo);
 170             }
 171         }
 172     }
 173 
 174 }
 175