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