1 /*
   2  * Copyright (c) 2019, 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 /* @test
  25  * @bug 8231632
  26  * @summary HttpURLConnection::usingProxy could specify that it lazily evaluates the fact
  27  * @modules java.base/sun.net.www
  28  * @library /test/lib
  29  * @run main/othervm HttpURLConnUsingProxy
  30  */
  31 
  32 import java.io.*;
  33 import java.net.*;
  34 import java.nio.charset.StandardCharsets;
  35 
  36 import com.sun.net.httpserver.HttpExchange;
  37 import com.sun.net.httpserver.HttpHandler;
  38 import com.sun.net.httpserver.HttpServer;
  39 import jdk.test.lib.net.URIBuilder;
  40 
  41 public class HttpURLConnUsingProxy {
  42     static HttpServer server;
  43     static Proxy proxy;
  44     static InetSocketAddress isa;
  45 
  46     static class Handler implements HttpHandler {
  47 
  48         @Override
  49         public void handle(HttpExchange exchange) throws IOException {
  50             byte[] response = "Hello World!".getBytes(StandardCharsets.UTF_8);
  51             try (InputStream req = exchange.getRequestBody()) {
  52                 req.readAllBytes();
  53             }
  54             exchange.sendResponseHeaders(200, response.length);
  55             try (OutputStream resp = exchange.getResponseBody()) {
  56                 resp.write(response);
  57             }
  58         }
  59     }
  60 
  61     public static void main(String[] args) {
  62         try {
  63             InetAddress loopbackAddress = InetAddress.getLoopbackAddress();
  64             InetSocketAddress addr = new InetSocketAddress(loopbackAddress, 0);
  65             server = HttpServer.create(addr, 0);
  66             server.createContext("/HttpURLConnUsingProxy/http1/", new Handler());
  67             server.start();
  68 
  69             ProxyServer pserver = new ProxyServer(loopbackAddress,
  70                     server.getAddress().getPort());
  71             // Start proxy server
  72             new Thread(pserver).start();
  73 
  74             URL url = URIBuilder.newBuilder()
  75                     .scheme("http")
  76                     .loopback()
  77                     .port(server.getAddress().getPort())
  78                     .path("/HttpURLConnUsingProxy/http1/x.html")
  79                     .toURLUnchecked();
  80 
  81             // NO_PROXY
  82             try {
  83                 HttpURLConnection urlc =
  84                         (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
  85                 assertEqual(urlc.usingProxy(), false);
  86                 urlc.getResponseCode();
  87                 assertEqual(urlc.usingProxy(), false);
  88                 urlc.disconnect();
  89             } catch (IOException ioe) {
  90                 throw new RuntimeException("Direct connection should succeed: "
  91                         + ioe.getMessage());
  92             }
  93 
  94             // Non-existing proxy
  95             try {
  96                 isa = InetSocketAddress.createUnresolved("inexistent", 8080);
  97                 proxy = new Proxy(Proxy.Type.HTTP, isa);
  98                 HttpURLConnection urlc = (HttpURLConnection) url.openConnection(proxy);
  99                 assertEqual(urlc.usingProxy(), true);
 100                 InputStream is = urlc.getInputStream();
 101                 is.close();
 102                 throw new RuntimeException("Non-existing proxy should cause IOException");
 103             } catch (IOException ioe) {
 104                 // expected
 105             }
 106 
 107             // Normal proxy settings
 108             try {
 109                 isa = InetSocketAddress.createUnresolved(loopbackAddress.getHostAddress(),
 110                         pserver.getPort());
 111                 proxy = new Proxy(Proxy.Type.HTTP, isa);
 112                 HttpURLConnection urlc = (HttpURLConnection) url.openConnection(proxy);
 113                 assertEqual(urlc.usingProxy(), true);
 114                 urlc.getResponseCode();
 115                 assertEqual(urlc.usingProxy(), true);
 116                 urlc.disconnect();
 117             } catch (IOException ioe) {
 118                 throw new RuntimeException("Connection through local proxy should succeed: "
 119                         + ioe.getMessage());
 120             }
 121 
 122             // Reuse proxy with new HttpURLConnection
 123             try {
 124                 HttpURLConnection urlc = (HttpURLConnection) url.openConnection(proxy);
 125                 assertEqual(urlc.usingProxy(), true);
 126                 urlc.getResponseCode();
 127                 assertEqual(urlc.usingProxy(), true);
 128                 read(urlc.getInputStream());
 129                 assertEqual(urlc.usingProxy(), true);
 130             } catch (IOException ioe) {
 131                 throw new RuntimeException("Connection through local proxy should succeed: "
 132                         + ioe.getMessage());
 133             }
 134 
 135             // Reuse proxy with existing HttpURLConnection
 136             try {
 137                 HttpURLConnection urlc = (HttpURLConnection) url.openConnection(proxy);
 138                 assertEqual(urlc.usingProxy(), true);
 139                 urlc.getResponseCode();
 140                 assertEqual(urlc.usingProxy(), true);
 141                 read(urlc.getInputStream());
 142                 assertEqual(urlc.usingProxy(), true);
 143                 urlc.disconnect();
 144             } catch (IOException ioe) {
 145                 throw new RuntimeException("Connection through local proxy should succeed: "
 146                         + ioe.getMessage());
 147             }
 148 
 149             // ProxySelector with normal proxy settings
 150             try {
 151                 ProxySelector.setDefault(ProxySelector.of(isa));
 152                 HttpURLConnection urlc = (HttpURLConnection) url.openConnection();
 153                 assertEqual(urlc.usingProxy(), false);
 154                 urlc.getResponseCode();
 155                 assertEqual(urlc.usingProxy(), true);
 156                 read(urlc.getInputStream());
 157                 assertEqual(urlc.usingProxy(), true);
 158                 urlc.disconnect();
 159                 assertEqual(urlc.usingProxy(), true);
 160             } catch (IOException ioe) {
 161                 throw new RuntimeException("Connection through local proxy should succeed: "
 162                         + ioe.getMessage());
 163             }
 164 
 165             // ProxySelector with proxying disabled
 166             try {
 167                 ProxySelector.setDefault(ProxySelector.of(null));
 168                 HttpURLConnection urlc = (HttpURLConnection) url.openConnection();
 169                 assertEqual(urlc.usingProxy(), false);
 170                 urlc.getResponseCode();
 171                 assertEqual(urlc.usingProxy(), false);
 172                 read(urlc.getInputStream());
 173                 assertEqual(urlc.usingProxy(), false);
 174             } catch (IOException ioe) {
 175                 throw new RuntimeException("Direct connection should succeed: "
 176                         + ioe.getMessage());
 177             }
 178 
 179             // ProxySelector overwritten
 180             try {
 181                 ProxySelector.setDefault(ProxySelector.of(isa));
 182                 HttpURLConnection urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
 183                 assertEqual(urlc.usingProxy(), false);
 184                 urlc.disconnect();
 185             } catch (IOException ioe) {
 186                 throw new RuntimeException("Direct connection should succeed: "
 187                         + ioe.getMessage());
 188             }
 189 
 190         } catch (Exception e) {
 191             throw new RuntimeException(e);
 192         } finally {
 193             if (server != null) {
 194                 server.stop(0);
 195             }
 196         }
 197     }
 198 
 199     static class ProxyServer extends Thread {
 200         private static ServerSocket ss = null;
 201 
 202         // Client requesting a tunnel
 203         private Socket clientSocket = null;
 204 
 205         /*
 206          * Origin server's address and port that the client
 207          * wants to establish the tunnel for communication.
 208          */
 209         private InetAddress serverInetAddr;
 210         private int serverPort;
 211 
 212         public ProxyServer(InetAddress server, int port) throws IOException {
 213             serverInetAddr = server;
 214             serverPort = port;
 215             ss = new ServerSocket(0, 0, InetAddress.getLoopbackAddress());
 216         }
 217 
 218         public void run() {
 219             while (true) {
 220                 try {
 221                     clientSocket = ss.accept();
 222                     processRequests();
 223                 } catch (Exception e) {
 224                     System.out.println("Proxy failed: " + e);
 225                     e.printStackTrace();
 226                     try {
 227                         ss.close();
 228                     } catch (IOException ioe) {
 229                         System.out.println("ProxyServer close error: " + ioe);
 230                         ioe.printStackTrace();
 231                     }
 232                 }
 233             }
 234         }
 235 
 236         private void processRequests() throws Exception {
 237             // Connection set to tunneling mode
 238 
 239             Socket serverSocket = new Socket(serverInetAddr, serverPort);
 240             ProxyTunnel clientToServer = new ProxyTunnel(
 241                     clientSocket, serverSocket);
 242             ProxyTunnel serverToClient = new ProxyTunnel(
 243                     serverSocket, clientSocket);
 244             clientToServer.start();
 245             serverToClient.start();
 246             System.out.println("Proxy: Started tunneling...");
 247 
 248             clientToServer.join();
 249             serverToClient.join();
 250             System.out.println("Proxy: Finished tunneling...");
 251 
 252             clientToServer.close();
 253             serverToClient.close();
 254         }
 255 
 256         /**
 257          * **************************************************************
 258          * Helper methods follow
 259          * **************************************************************
 260          */
 261         public int getPort() {
 262             return ss.getLocalPort();
 263         }
 264 
 265         /*
 266          * This inner class provides unidirectional data flow through the sockets
 267          * by continuously copying bytes from input socket to output socket
 268          * while both sockets are open and EOF has not been received.
 269          */
 270         static class ProxyTunnel extends Thread {
 271             Socket sockIn;
 272             Socket sockOut;
 273             InputStream input;
 274             OutputStream output;
 275 
 276             public ProxyTunnel(Socket sockIn, Socket sockOut) throws Exception {
 277                 this.sockIn = sockIn;
 278                 this.sockOut = sockOut;
 279                 input = sockIn.getInputStream();
 280                 output = sockOut.getOutputStream();
 281             }
 282 
 283             public void run() {
 284                 int BUFFER_SIZE = 400;
 285                 byte[] buf = new byte[BUFFER_SIZE];
 286                 int bytesRead = 0;
 287                 int count = 0;    // Keep track of amount of data transferred
 288 
 289                 try {
 290                     while ((bytesRead = input.read(buf)) >= 0) {
 291                         output.write(buf, 0, bytesRead);
 292                         output.flush();
 293                         count += bytesRead;
 294                     }
 295                 } catch (IOException e) {
 296                     /*
 297                      * Peer end has closed connection
 298                      * so we close tunnel
 299                      */
 300                     close();
 301                 }
 302             }
 303 
 304             public void close() {
 305                 try {
 306                     if (!sockIn.isClosed())
 307                         sockIn.close();
 308                     if (!sockOut.isClosed())
 309                         sockOut.close();
 310                 } catch (IOException ignored) {
 311                 }
 312             }
 313         }
 314     }
 315 
 316     private static void assertEqual(boolean usingProxy, boolean expected) {
 317         if (usingProxy != expected) {
 318             throw new RuntimeException("Expected: " + expected
 319                     + " but usingProxy returned: " + usingProxy);
 320         }
 321     }
 322 
 323     private static String read(InputStream inputStream) throws IOException {
 324         StringBuilder sb = new StringBuilder();
 325         BufferedReader bufferedReader = new BufferedReader(
 326                 new InputStreamReader(inputStream, StandardCharsets.UTF_8));
 327         int i = bufferedReader.read();
 328         while (i != -1) {
 329             sb.append((char) i);
 330             i = bufferedReader.read();
 331         }
 332         bufferedReader.close();
 333         return sb.toString();
 334     }
 335 }