1 /*
   2  * Copyright (c) 2002, 2010, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * @test
  26  * @bug 4769350
  27  * @library ../../../sun/net/www/httptest/
  28  * @build HttpCallback TestHttpServer ClosedChannelList HttpTransaction AbstractCallback
  29  * @run main/othervm B4769350 server
  30  * @run main/othervm B4769350 proxy
  31  * @summary proxy authentication username and password caching only works in serial case
  32  * Run in othervm since the test sets system properties that are read by the
  33  * networking stack and cached when the HTTP handler is invoked, and previous
  34  * tests may already have invoked the HTTP handler.
  35  */
  36 
  37 import java.io.*;
  38 import java.net.*;
  39 
  40 public class B4769350 {
  41 
  42     static int count = 0;
  43     static boolean error = false;
  44 
  45     static void read (InputStream is) throws IOException {
  46         int c;
  47         while ((c=is.read()) != -1) {
  48             //System.out.write (c);
  49         }
  50     }
  51 
  52     static class Client extends Thread {
  53         String authority, path;
  54         boolean allowerror;
  55 
  56         Client (String authority, String path, boolean allowerror) {
  57             super("Thread-" + path);
  58             this.authority = authority;
  59             this.path = path;
  60             this.allowerror = allowerror;
  61         }
  62 
  63         public void run () {
  64             try {
  65                 URI u = new URI ("http", authority, path, null, null);
  66                 URL url = u.toURL();
  67                 URLConnection urlc = url.openConnection ();
  68                 InputStream is = urlc.getInputStream ();
  69                 read (is);
  70                 is.close();
  71             } catch (URISyntaxException  e) {
  72                 System.out.println (e);
  73                 error = true;
  74             } catch (IOException e) {
  75                 if (!allowerror) {
  76                     System.out.println (Thread.currentThread().getName() + " " + e);
  77                     e.printStackTrace();
  78                     error = true;
  79                 }
  80             }
  81         }
  82     }
  83 
  84     static class CallBack extends AbstractCallback {
  85 
  86         void errorReply (HttpTransaction req, String reply) throws IOException {
  87             req.addResponseHeader ("Connection", "close");
  88             req.addResponseHeader ("WWW-Authenticate", reply);
  89             req.sendResponse (401, "Unauthorized");
  90             req.orderlyClose();
  91         }
  92 
  93         void proxyReply (HttpTransaction req, String reply) throws IOException {
  94             req.addResponseHeader ("Proxy-Authenticate", reply);
  95             req.sendResponse (407, "Proxy Authentication Required");
  96         }
  97 
  98         void okReply (HttpTransaction req) throws IOException {
  99             req.addResponseHeader ("Connection", "close");
 100             req.setResponseEntityBody ("Hello .");
 101             req.sendResponse (200, "Ok");
 102             req.orderlyClose();
 103         }
 104 
 105         public void request (HttpTransaction req, int count) {
 106             try {
 107                 URI uri = req.getRequestURI();
 108                 String path = uri.getPath();
 109                 if (path.endsWith ("/t1a")) {
 110                     doT1a (req, count);
 111                 } else if (path.endsWith ("/t1b")) {
 112                     doT1b (req, count);
 113                 } else if (path.endsWith ("/t1c")) {
 114                     doT1c (req, count);
 115                 } else if (path.endsWith ("/t1d")) {
 116                     doT1d (req, count);
 117                 } else if (path.endsWith ("/t2a")) {
 118                     doT2a (req, count);
 119                 } else if (path.endsWith ("/t2b")) {
 120                     doT2b (req, count);
 121                 } else if (path.endsWith ("/t3a")) {
 122                     doT3a (req, count);
 123                 } else if (path.endsWith ("/t3b")) {
 124                     doT3bc (req, count);
 125                 } else if (path.endsWith ("/t3c")) {
 126                     doT3bc (req, count);
 127                 } else {
 128                    System.out.println ("unexpected request URI");
 129                 }
 130             } catch (IOException e) {
 131                 e.printStackTrace();
 132             }
 133         }
 134 
 135         /* T1 tests the client by sending 4 requests to 2 different realms
 136          * in parallel. The client should recognise two pairs of dependent requests
 137          * and execute the first of each pair in parallel. When they both succeed
 138          * the second requests should be executed without calling the authenticator.
 139          * The test succeeds if the authenticator was only called twice.
 140          */
 141         void doT1a (HttpTransaction req, int count) throws IOException {
 142             switch (count) {
 143             case 0:
 144                 errorReply (req, "Basic realm=\"realm1\"");
 145                 TestHttpServer.rendezvous ("one", 2);
 146                 break;
 147             case 1:
 148                 TestHttpServer.waitForCondition ("cond2");
 149                 okReply (req);
 150                 break;
 151             default:
 152                 System.out.println ("Unexpected request");
 153             }
 154         }
 155 
 156 
 157         void doT1b (HttpTransaction req, int count) throws IOException {
 158             switch (count) {
 159             case 0:
 160                 errorReply (req, "Basic realm=\"realm2\"");
 161                 TestHttpServer.rendezvous ("one", 2);
 162                 TestHttpServer.setCondition ("cond1");
 163                 break;
 164             case 1:
 165                 TestHttpServer.waitForCondition ("cond2");
 166                 okReply (req);
 167                 break;
 168             default:
 169                 System.out.println ("Unexpected request");
 170             }
 171         }
 172 
 173         void doT1c (HttpTransaction req, int count) throws IOException {
 174             switch (count) {
 175             case 0:
 176                 errorReply (req, "Basic realm=\"realm1\"");
 177                 TestHttpServer.rendezvous ("two", 2);
 178                 break;
 179             case 1:
 180                 okReply (req);
 181                 break;
 182             default:
 183                 System.out.println ("Unexpected request");
 184             }
 185         }
 186 
 187         void doT1d (HttpTransaction req, int count) throws IOException {
 188             switch (count) {
 189             case 0:
 190                 errorReply (req, "Basic realm=\"realm2\"");
 191                 TestHttpServer.rendezvous ("two", 2);
 192                 TestHttpServer.setCondition ("cond2");
 193                 break;
 194             case 1:
 195                 okReply (req);
 196                 break;
 197             default:
 198                 System.out.println ("Unexpected request");
 199             }
 200         }
 201 
 202 
 203         /* T2 tests to check that if initial authentication fails, the second will
 204          * succeed, and the authenticator is called twice
 205          */
 206 
 207         void doT2a (HttpTransaction req, int count) throws IOException {
 208             /* This will be called several times */
 209             if (count == 1) {
 210                 TestHttpServer.setCondition ("T2cond1");
 211             }
 212             errorReply (req, "Basic realm=\"realm3\"");
 213         }
 214 
 215         void doT2b (HttpTransaction req, int count) throws IOException {
 216             switch (count) {
 217             case 0:
 218                 errorReply (req, "Basic realm=\"realm3\"");
 219                 break;
 220             case 1:
 221                 okReply (req);
 222                 break;
 223             default:
 224                 System.out.println ("Unexpected request");
 225             }
 226         }
 227 
 228         /* T3 tests proxy and server authentication. three threads request same
 229          * resource at same time. Authenticator should be called once for server
 230          * and once for proxy
 231          */
 232         void doT3a (HttpTransaction req, int count) throws IOException {
 233             switch (count) {
 234             case 0:
 235                 proxyReply (req, "Basic realm=\"proxy\"");
 236                 TestHttpServer.setCondition ("T3cond1");
 237                 break;
 238             case 1:
 239                 errorReply (req, "Basic realm=\"realm4\"");
 240                 break;
 241             case 2:
 242                 okReply (req);
 243                 break;
 244             default:
 245                 System.out.println ("Unexpected request");
 246             }
 247         }
 248 
 249         void doT3bc (HttpTransaction req, int count) throws IOException {
 250             switch (count) {
 251             case 0:
 252                 proxyReply (req, "Basic realm=\"proxy\"");
 253                 break;
 254             case 1:
 255                 okReply (req);
 256                 break;
 257             default:
 258                 System.out.println ("Unexpected request");
 259             }
 260         }
 261     };
 262 
 263     static TestHttpServer server;
 264     static MyAuthenticator auth = new MyAuthenticator ();
 265 
 266     static int redirects = 4;
 267 
 268     static Client c1,c2,c3,c4,c5,c6,c7,c8,c9;
 269 
 270     static void doServerTests (String authority) throws Exception {
 271         System.out.println ("Doing Server tests");
 272         System.out.println ("T1");
 273         c1 = new Client (authority, "/test/realm1/t1a", false);
 274         c2 = new Client (authority, "/test/realm2/t1b", false);
 275         c3 = new Client (authority, "/test/realm1/t1c", false);
 276         c4 = new Client (authority, "/test/realm2/t1d", false);
 277 
 278         c1.start(); c2.start();
 279         TestHttpServer.waitForCondition ("cond1");
 280         c3.start(); c4.start();
 281         c1.join(); c2.join(); c3.join(); c4.join();
 282 
 283         int f = auth.getCount();
 284         if (f != 2) {
 285             except ("Authenticator was called "+f+" times. Should be 2");
 286         }
 287         if (error) {
 288             except ("error occurred");
 289         }
 290 
 291         auth.resetCount();
 292         System.out.println ("T2");
 293 
 294         c5 = new Client (authority, "/test/realm3/t2a", true);
 295         c6 = new Client (authority, "/test/realm3/t2b", false);
 296         c5.start ();
 297         TestHttpServer.waitForCondition ("T2cond1");
 298         c6.start ();
 299         c5.join(); c6.join();
 300 
 301         f = auth.getCount();
 302         if (f != redirects+1) {
 303             except ("Authenticator was called "+f+" times. Should be: " + redirects+1);
 304         }
 305         if (error) {
 306             except ("error occurred");
 307         }
 308     }
 309 
 310     static void doProxyTests (String authority) throws Exception {
 311         System.out.println ("Doing Proxy tests");
 312         c7 = new Client (authority, "/test/realm4/t3a", false);
 313         c8 = new Client (authority, "/test/realm4/t3b", false);
 314         c9 = new Client (authority, "/test/realm4/t3c", false);
 315         c7.start ();
 316         TestHttpServer.waitForCondition ("T3cond1");
 317         c8.start ();
 318         c9.start ();
 319         c7.join(); c8.join(); c9.join();
 320 
 321         int f = auth.getCount();
 322         if (f != 2) {
 323             except ("Authenticator was called "+f+" times. Should be: " + 2);
 324         }
 325         if (error) {
 326             except ("error occurred");
 327         }
 328     }
 329 
 330     public static void main (String[] args) throws Exception {
 331         System.setProperty ("http.maxRedirects", Integer.toString (redirects));
 332         System.setProperty ("http.auth.serializeRequests", "true");
 333         Authenticator.setDefault (auth);
 334         boolean proxy = args[0].equals ("proxy");
 335         try {
 336             server = new TestHttpServer (new CallBack(), 10, 1, 0);
 337             System.out.println ("Server: listening on port: " + server.getLocalPort());
 338             if (proxy) {
 339                 System.setProperty ("http.proxyHost", "localhost");
 340                 System.setProperty ("http.proxyPort",Integer.toString(server.getLocalPort()));
 341                 doProxyTests ("www.foo.com");
 342             } else {
 343                 doServerTests ("localhost:"+server.getLocalPort());
 344             }
 345             server.terminate();
 346 
 347         } catch (Exception e) {
 348             if (server != null) {
 349                 server.terminate();
 350             }
 351             throw e;
 352         }
 353     }
 354 
 355     static void pause (int millis) {
 356         try {
 357             Thread.sleep (millis);
 358         } catch (InterruptedException e) {}
 359     }
 360 
 361     public static void except (String s) {
 362         server.terminate();
 363         throw new RuntimeException (s);
 364     }
 365 
 366     static class MyAuthenticator extends Authenticator {
 367         MyAuthenticator () {
 368             super ();
 369         }
 370 
 371         int count = 0;
 372 
 373         public PasswordAuthentication getPasswordAuthentication () {
 374             //System.out.println ("Authenticator called: " + getRequestingPrompt());
 375             //try {
 376                 //Thread.sleep (1000);
 377             //} catch (InterruptedException e) {}
 378             PasswordAuthentication pw;
 379             pw = new PasswordAuthentication ("user", "pass1".toCharArray());
 380             count ++;
 381             return pw;
 382         }
 383 
 384         public void resetCount () {
 385             count = 0;
 386         }
 387 
 388         public int getCount () {
 389             return (count);
 390         }
 391     }
 392 }