1 /*
   2  * Copyright (c) 2005, 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 6299712
  27  * @library ../../httptest/
  28  * @build HttpCallback TestHttpServer ClosedChannelList HttpTransaction
  29  * @run main/othervm B6299712
  30  * @summary  NullPointerException in sun.net.www.protocol.http.HttpURLConnection.followRedirect
  31  */
  32 
  33 import java.net.*;
  34 import java.io.*;
  35 import java.util.*;
  36 
  37 /*
  38  * Test Description:
  39  *      - main thread run as a http client
  40  *      - another thread runs a http server, which redirect the first call to "/redirect"
  41  *        and return '200 OK' for the successive call
  42  *      - a global ResponseCache instance is installed, which return DeployCacheResponse
  43  *        for url ends with "/redirect", i.e. the url redirected to by our simple http server,
  44  *        and null for other url.
  45  *      - the whole result is that the first call will be served by our simple
  46  *        http server and is redirected to "/redirect". The successive call will be done
  47  *        automatically by HttpURLConnection, which will be served by DeployCacheResponse.
  48  *        The NPE will be thrown on the second round if the bug is there.
  49  */
  50 public class B6299712 {
  51     static SimpleHttpTransaction httpTrans;
  52     static TestHttpServer server;
  53 
  54     public static void main(String[] args) throws Exception {
  55         ResponseCache.setDefault(new DeployCacheHandler());
  56         startHttpServer();
  57 
  58         makeHttpCall();
  59     }
  60 
  61     public static void startHttpServer() {
  62         try {
  63             httpTrans = new SimpleHttpTransaction();
  64             server = new TestHttpServer(httpTrans, 1, 10, 0);
  65         } catch (IOException e) {
  66             e.printStackTrace();
  67         }
  68     }
  69 
  70     public static void makeHttpCall() {
  71         try {
  72             System.out.println("http server listen on: " + server.getLocalPort());
  73             URL url = new URL("http" , InetAddress.getLocalHost().getHostAddress(),
  74                                 server.getLocalPort(), "/");
  75             HttpURLConnection uc = (HttpURLConnection)url.openConnection();
  76             System.out.println(uc.getResponseCode());
  77         } catch (IOException e) {
  78             e.printStackTrace();
  79         } finally {
  80             server.terminate();
  81         }
  82     }
  83 }
  84 
  85 class SimpleHttpTransaction implements HttpCallback {
  86     /*
  87      * Our http server which simply redirect first call
  88      */
  89     public void request(HttpTransaction trans) {
  90         try {
  91             String path = trans.getRequestURI().getPath();
  92             if (path.equals("/")) {
  93                 // the first call, redirect it
  94                 String location = "/redirect";
  95                 trans.addResponseHeader("Location", location);
  96                 trans.sendResponse(302, "Moved Temporarily");
  97             } else {
  98                 // the second call
  99                 trans.sendResponse(200, "OK");
 100             }
 101         } catch (Exception e) {
 102             e.printStackTrace();
 103         }
 104     }
 105 }
 106 
 107 class DeployCacheHandler extends java.net.ResponseCache {
 108     private boolean inCacheHandler = false;
 109     private boolean _downloading = false;
 110 
 111     public synchronized CacheResponse get(final URI uri, String rqstMethod,
 112             Map requestHeaders) throws IOException {
 113         System.out.println("get!!!: " + uri);
 114         try {
 115             if (!uri.toString().endsWith("redirect")) {
 116                 return null;
 117             }
 118         } catch (Exception e) {
 119             e.printStackTrace();
 120         }
 121 
 122         return new DeployCacheResponse(new EmptyInputStream(), new HashMap());
 123     }
 124 
 125     public synchronized CacheRequest put(URI uri, URLConnection conn)
 126     throws IOException {
 127         URL url = uri.toURL();
 128         return new DeployCacheRequest(url, conn);
 129 
 130     }
 131 }
 132 
 133 class DeployCacheRequest extends java.net.CacheRequest {
 134 
 135     private URL _url;
 136     private URLConnection _conn;
 137     private boolean _downloading = false;
 138 
 139     DeployCacheRequest(URL url, URLConnection conn) {
 140         _url = url;
 141         _conn = conn;
 142     }
 143 
 144     public void abort() {
 145 
 146     }
 147 
 148     public OutputStream getBody() throws IOException {
 149 
 150         return null;
 151     }
 152 }
 153 
 154 class DeployCacheResponse extends java.net.CacheResponse {
 155     protected InputStream is;
 156     protected Map headers;
 157 
 158     DeployCacheResponse(InputStream is, Map headers) {
 159         this.is = is;
 160         this.headers = headers;
 161     }
 162 
 163     public InputStream getBody() throws IOException {
 164         return is;
 165     }
 166 
 167     public Map getHeaders() throws IOException {
 168         return headers;
 169     }
 170 }
 171 
 172 class EmptyInputStream extends InputStream {
 173     public EmptyInputStream() {
 174     }
 175 
 176     public int read()
 177     throws IOException {
 178         return -1;
 179     }
 180 }