1 /*
2  * Copyright (c) 2005, 2015, 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 // SunJSSE does not support dynamic system properties, no way to re-use
26 // system properties in samevm/agentvm mode.
27 //
28 
29 /*
30  * @test
31  * @bug 6216082
32  * @summary  Redirect problem with HttpsURLConnection using a proxy
33  * @library .. /lib
34  * @build jdk.test.lib.NetworkConfiguration
35  *        jdk.test.lib.Platform
36  *        HttpCallback TestHttpsServer ClosedChannelList
37  *        HttpTransaction TunnelProxy
38  * @key intermittent
39  * @run main/othervm B6216082
40  */
41 
42 import java.io.*;
43 import java.net.*;
44 import javax.net.ssl.*;
45 import java.util.*;
46 
47 import jdk.test.lib.NetworkConfiguration;
48 
49 public class B6216082 {
50     static SimpleHttpTransaction httpTrans;
51     static TestHttpsServer server;
52     static TunnelProxy proxy;
53 
54     // it seems there's no proxy ever if a url points to 'localhost',
55     // even if proxy related properties are set. so we need to bind
56     // our simple http proxy and http server to a non-loopback address
57     static InetAddress firstNonLoAddress = null;
58 
main(String[] args)59     public static void main(String[] args) throws Exception {
60         HostnameVerifier reservedHV =
61             HttpsURLConnection.getDefaultHostnameVerifier();
62         try {
63             // XXX workaround for CNFE
64             Class.forName("java.nio.channels.ClosedByInterruptException");
65             if (!setupEnv()) {
66                 return;
67             }
68 
69             startHttpServer();
70 
71             // https.proxyPort can only be set after the TunnelProxy has been
72             // created as it will use an ephemeral port.
73             System.setProperty("https.proxyPort",
74                         (new Integer(proxy.getLocalPort())).toString() );
75 
76             makeHttpCall();
77 
78             if (httpTrans.hasBadRequest) {
79                 throw new RuntimeException("Test failed : bad http request");
80             }
81         } finally {
82             if (proxy != null) {
83                 proxy.terminate();
84             }
85             if (server != null) {
86                server.terminate();
87             }
88             HttpsURLConnection.setDefaultHostnameVerifier(reservedHV);
89         }
90     }
91 
92     /*
93      * Where do we find the keystores for ssl?
94      */
95     static String pathToStores = "../../../../../../javax/net/ssl/etc";
96     static String keyStoreFile = "keystore";
97     static String trustStoreFile = "truststore";
98     static String passwd = "passphrase";
setupEnv()99     public static boolean setupEnv() throws Exception {
100         firstNonLoAddress = getNonLoAddress();
101         if (firstNonLoAddress == null) {
102             System.err.println("The test needs at least one non-loopback address to run. Quit now.");
103             return false;
104         }
105         System.out.println(firstNonLoAddress.getHostAddress());
106         // will use proxy
107         System.setProperty( "https.proxyHost", firstNonLoAddress.getHostAddress());
108 
109         // setup properties to do ssl
110         String keyFilename = System.getProperty("test.src", "./") + "/" +
111                              pathToStores + "/" + keyStoreFile;
112         String trustFilename = System.getProperty("test.src", "./") + "/" +
113                                pathToStores + "/" + trustStoreFile;
114 
115         System.setProperty("javax.net.ssl.keyStore", keyFilename);
116         System.setProperty("javax.net.ssl.keyStorePassword", passwd);
117         System.setProperty("javax.net.ssl.trustStore", trustFilename);
118         System.setProperty("javax.net.ssl.trustStorePassword", passwd);
119         HttpsURLConnection.setDefaultHostnameVerifier(new NameVerifier());
120         return true;
121     }
122 
getNonLoAddress()123     public static InetAddress getNonLoAddress() throws Exception {
124         InetAddress lh = InetAddress.getByName("localhost");
125         NetworkInterface loNIC = NetworkInterface.getByInetAddress(lh);
126 
127         NetworkConfiguration nc = NetworkConfiguration.probe();
128         Optional<InetAddress> oaddr = nc.interfaces()
129                 .filter(nif -> !nif.getName().equalsIgnoreCase(loNIC.getName()))
130                 .flatMap(nif -> nc.addresses(nif))
131                 .filter(a -> !a.isLoopbackAddress())
132                 .findFirst();
133 
134         return oaddr.orElseGet(() -> null);
135     }
136 
startHttpServer()137     public static void startHttpServer() throws IOException {
138         // Both the https server and the proxy let the
139         // system pick up an ephemeral port.
140         httpTrans = new SimpleHttpTransaction();
141         server = new TestHttpsServer(httpTrans, 1, 10, 0);
142         proxy = new TunnelProxy(1, 10, 0);
143     }
144 
makeHttpCall()145     public static void makeHttpCall() throws Exception {
146         System.out.println("https server listen on: " + server.getLocalPort());
147         System.out.println("https proxy listen on: " + proxy.getLocalPort());
148         URL url = new URL("https" , firstNonLoAddress.getHostAddress(),
149                             server.getLocalPort(), "/");
150         HttpURLConnection uc = (HttpURLConnection)url.openConnection();
151         System.out.println(uc.getResponseCode());
152         uc.disconnect();
153     }
154 
155     static class NameVerifier implements HostnameVerifier {
verify(String hostname, SSLSession session)156         public boolean verify(String hostname, SSLSession session) {
157             return true;
158         }
159     }
160 }
161 
162 class SimpleHttpTransaction implements HttpCallback {
163     public boolean hasBadRequest = false;
164 
165     /*
166      * Our http server which simply redirect first call
167      */
request(HttpTransaction trans)168     public void request(HttpTransaction trans) {
169         try {
170             String path = trans.getRequestURI().getPath();
171             if (path.equals("/")) {
172                 // the first call, redirect it
173                 String location = "/redirect";
174                 trans.addResponseHeader("Location", location);
175                 trans.sendResponse(302, "Moved Temporarily");
176             } else {
177                 // if the bug exsits, it'll send 2 GET commands
178                 // check 2nd GET here
179                 String duplicatedGet = trans.getRequestHeader(null);
180                 if (duplicatedGet != null &&
181                     duplicatedGet.toUpperCase().indexOf("GET") >= 0) {
182                     trans.sendResponse(400, "Bad Request");
183                     hasBadRequest = true;
184                 } else {
185                     trans.sendResponse(200, "OK");
186                 }
187             }
188         } catch (Exception e) {
189             throw new RuntimeException(e);
190         }
191     }
192 }
193