1 /*
   2  * Copyright (c) 2003, 2012, 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 4920526
  26  * @summary Needs per connection proxy support for URLs
  27  * @modules java.base/sun.net.www
  28  * @library ../../../sun/net/www/httptest/
  29  * @build ClosedChannelList TestHttpServer HttpTransaction HttpCallback
  30  * @compile PerConnectionProxy.java
  31  * @run main/othervm -Dhttp.proxyHost=inexistant -Dhttp.proxyPort=8080 PerConnectionProxy
  32  */
  33 
  34 import java.net.*;
  35 import java.io.*;
  36 import sun.net.www.*;
  37 
  38 public class PerConnectionProxy implements HttpCallback {
  39     static TestHttpServer server;
  40 
  41     public void request (HttpTransaction req) {
  42         req.setResponseEntityBody ("Hello .");
  43         try {
  44             req.sendResponse (200, "Ok");
  45             req.orderlyClose();
  46         } catch (IOException e) {
  47         }
  48     }
  49 
  50     public static void main(String[] args) {
  51         try {
  52             server = new TestHttpServer (new PerConnectionProxy(), 1, 10, 0);
  53             ProxyServer pserver = new ProxyServer(InetAddress.getByName("localhost"), server.getLocalPort());
  54             // start proxy server
  55             new Thread(pserver).start();
  56 
  57             URL url = new URL("http://localhost:"+server.getLocalPort());
  58 
  59             // for non existing proxy expect an IOException
  60             try {
  61                 InetSocketAddress isa = InetSocketAddress.createUnresolved("inexistent", 8080);
  62                 Proxy proxy = new Proxy(Proxy.Type.HTTP, isa);
  63                 HttpURLConnection urlc = (HttpURLConnection)url.openConnection (proxy);
  64                 InputStream is = urlc.getInputStream ();
  65                 is.close();
  66                 throw new RuntimeException("non existing per connection proxy should lead to IOException");
  67             } catch (IOException ioex) {
  68                 // expected
  69             }
  70 
  71             // for NO_PROXY, expect direct connection
  72             try {
  73                 HttpURLConnection urlc = (HttpURLConnection)url.openConnection (Proxy.NO_PROXY);
  74                 int respCode = urlc.getResponseCode();
  75                 urlc.disconnect();
  76             } catch (IOException ioex) {
  77                 throw new RuntimeException("direct connection should succeed :"+ioex.getMessage());
  78             }
  79 
  80             // for a normal proxy setting expect to see connection
  81             // goes through that proxy
  82             try {
  83                 InetSocketAddress isa = InetSocketAddress.createUnresolved("localhost", pserver.getPort());
  84                 Proxy p = new Proxy(Proxy.Type.HTTP, isa);
  85                 HttpURLConnection urlc = (HttpURLConnection)url.openConnection (p);
  86                 int respCode = urlc.getResponseCode();
  87                 urlc.disconnect();
  88             } catch (IOException ioex) {
  89                 throw new RuntimeException("connection through a local proxy should succeed :"+ioex.getMessage());
  90             }
  91 
  92         } catch (Exception e) {
  93             throw new RuntimeException(e);
  94         } finally {
  95             if (server != null) {
  96                 server.terminate();
  97             }
  98         }
  99 
 100     }
 101 
 102     static class ProxyServer extends Thread {
 103         private static ServerSocket ss = null;
 104 
 105         // client requesting for a tunnel
 106         private Socket clientSocket = null;
 107 
 108         /*
 109          * Origin server's address and port that the client
 110          * wants to establish the tunnel for communication.
 111          */
 112         private InetAddress serverInetAddr;
 113         private int     serverPort;
 114 
 115         public ProxyServer(InetAddress server, int port) throws IOException {
 116             serverInetAddr = server;
 117             serverPort = port;
 118             ss = new ServerSocket(0);
 119         }
 120 
 121         public void run() {
 122             try {
 123                 clientSocket = ss.accept();
 124                 processRequests();
 125             } catch (Exception e) {
 126                 System.out.println("Proxy Failed: " + e);
 127                 e.printStackTrace();
 128                 try {
 129                     ss.close();
 130                 }
 131                 catch (IOException excep) {
 132                     System.out.println("ProxyServer close error: " + excep);
 133                     excep.printStackTrace();
 134                 }
 135             }
 136         }
 137 
 138         private void processRequests() throws Exception {
 139             // connection set to the tunneling mode
 140 
 141             Socket serverSocket = new Socket(serverInetAddr, serverPort);
 142             ProxyTunnel clientToServer = new ProxyTunnel(
 143                                                          clientSocket, serverSocket);
 144             ProxyTunnel serverToClient = new ProxyTunnel(
 145                                                          serverSocket, clientSocket);
 146             clientToServer.start();
 147             serverToClient.start();
 148             System.out.println("Proxy: Started tunneling.......");
 149 
 150             clientToServer.join();
 151             serverToClient.join();
 152             System.out.println("Proxy: Finished tunneling........");
 153 
 154             clientToServer.close();
 155             serverToClient.close();
 156 
 157         }
 158 
 159         /**
 160 ***************************************************************
 161 *                       helper methods follow
 162 ***************************************************************
 163 */
 164         public int getPort() {
 165             return ss.getLocalPort();
 166         }
 167         /*
 168          * This inner class provides unidirectional data flow through the sockets
 169          * by continuously copying bytes from the input socket onto the output
 170          * socket, until both sockets are open and EOF has not been received.
 171          */
 172         static class ProxyTunnel extends Thread {
 173             Socket sockIn;
 174             Socket sockOut;
 175             InputStream input;
 176             OutputStream output;
 177 
 178             public ProxyTunnel(Socket sockIn, Socket sockOut)
 179                 throws Exception {
 180                 this.sockIn = sockIn;
 181                 this.sockOut = sockOut;
 182                 input = sockIn.getInputStream();
 183                 output = sockOut.getOutputStream();
 184             }
 185 
 186             public void run() {
 187                 int BUFFER_SIZE = 400;
 188                 byte[] buf = new byte[BUFFER_SIZE];
 189                 int bytesRead = 0;
 190                 int count = 0;  // keep track of the amount of data transfer
 191 
 192                 try {
 193                     while ((bytesRead = input.read(buf)) >= 0) {
 194                         output.write(buf, 0, bytesRead);
 195                         output.flush();
 196                         count += bytesRead;
 197                     }
 198                 } catch (IOException e) {
 199                     /*
 200                      * The peer end has closed the connection
 201                      * we will close the tunnel
 202                      */
 203                     close();
 204                 }
 205             }
 206 
 207             public void close() {
 208                 try {
 209                     if (!sockIn.isClosed())
 210                         sockIn.close();
 211                     if (!sockOut.isClosed())
 212                         sockOut.close();
 213                 } catch (IOException ignored) { }
 214             }
 215         }
 216 
 217     }
 218 }