1 /* 2 * Copyright (c) 2001, 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 * 26 * This class is used by the regression test ClientHelloRead.java 27 * This class includes a proxy server that processes HTTP CONNECT requests, 28 * and tunnels the data between the client and server once the CONNECT 29 * request is accepted. It is provided to introduce some delay in the network 30 * traffic between the client and server. 31 * This test is to make sure that ClientHello is properly read by the server, 32 * while facing network delays. 33 */ 34 35 import java.io.*; 36 import java.net.*; 37 import javax.net.ssl.*; 38 import javax.net.ServerSocketFactory; 39 import sun.net.www.MessageHeader; 40 41 public class ProxyTunnelServer extends Thread { 42 43 private static ServerSocket ss = null; 44 45 // client requesting for a tunnel 46 private Socket clientSocket = null; 47 48 /* 49 * Origin server's address and port that the client 50 * wants to establish the tunnel for communication. 51 */ 52 private InetAddress serverInetAddr; 53 private int serverPort; 54 ProxyTunnelServer()55 public ProxyTunnelServer() throws IOException { 56 if (ss == null) { 57 ss = (ServerSocket) ServerSocketFactory.getDefault(). 58 createServerSocket(0); 59 } 60 } 61 run()62 public void run() { 63 try { 64 clientSocket = ss.accept(); 65 processRequests(); 66 } catch (Exception e) { 67 System.out.println("Proxy Failed: " + e); 68 e.printStackTrace(); 69 try { 70 ss.close(); 71 } 72 catch (IOException excep) { 73 System.out.println("ProxyServer close error: " + excep); 74 excep.printStackTrace(); 75 } 76 } 77 } 78 79 /* 80 * Processes the CONNECT requests 81 */ processRequests()82 private void processRequests() throws Exception { 83 84 InputStream in = clientSocket.getInputStream(); 85 MessageHeader response = new MessageHeader(in); 86 String statusLine = response.getValue(0); 87 88 if (statusLine.startsWith("CONNECT")) { 89 // retrieve the host and port info from the response line 90 retrieveConnectInfo(statusLine); 91 respondForConnect(); 92 doTunnel(); 93 ss.close(); 94 } else { 95 System.out.println("proxy server: processes only " 96 + "CONNECT method requests, recieved: " 97 + statusLine); 98 } 99 } 100 respondForConnect()101 private void respondForConnect() throws Exception { 102 OutputStream out = clientSocket.getOutputStream(); 103 PrintWriter pout = new PrintWriter(out); 104 pout.println("HTTP/1.1 200 OK"); 105 pout.println(); 106 pout.flush(); 107 } 108 109 /* 110 * note: Tunneling has to be provided in both directions, i.e 111 * from client->server and server->client, even if the application 112 * data may be unidirectional, SSL handshaking data flows in either 113 * direction. 114 */ doTunnel()115 private void doTunnel() throws Exception { 116 Socket serverSocket = new Socket(serverInetAddr, serverPort); 117 118 // delay the write from client -> server 119 ProxyTunnel clientToServer = new ProxyTunnel( 120 clientSocket, serverSocket, true); 121 ProxyTunnel serverToClient = new ProxyTunnel( 122 serverSocket, clientSocket, false); 123 clientToServer.start(); 124 serverToClient.start(); 125 126 clientToServer.join(); 127 serverToClient.join(); 128 129 clientToServer.close(); 130 serverToClient.close(); 131 } 132 133 /* 134 * This inner class provides unidirectional data flow through the sockets 135 * by continuously copying bytes from the input socket onto the output 136 * socket, until both sockets are open and EOF has not been received. 137 */ 138 class ProxyTunnel extends Thread { 139 Socket sockIn; 140 Socket sockOut; 141 InputStream input; 142 OutputStream output; 143 boolean delayedWrite; 144 ProxyTunnel(Socket sockIn, Socket sockOut, boolean delayedWrite)145 public ProxyTunnel(Socket sockIn, Socket sockOut, boolean delayedWrite) 146 throws Exception { 147 this.sockIn = sockIn; 148 this.sockOut = sockOut; 149 input = sockIn.getInputStream(); 150 output = sockOut.getOutputStream(); 151 this.delayedWrite = delayedWrite; 152 } 153 run()154 public void run() { 155 // the buffer size of < 47 introduces delays in availability 156 // of chunks of client handshake data 157 int BUFFER_SIZE = 40; 158 byte[] buf = new byte[BUFFER_SIZE]; 159 int bytesRead = 0; 160 int count = 0; // keep track of the amount of data transfer 161 162 try { 163 while ((bytesRead = input.read(buf)) >= 0) { 164 if (delayedWrite) { 165 try { 166 this.sleep(1); 167 } catch (InterruptedException excep) { 168 System.out.println(excep); 169 } 170 } 171 output.write(buf, 0, bytesRead); 172 output.flush(); 173 count += bytesRead; 174 } 175 } catch (IOException e) { 176 /* 177 * The peer end has closed the connection 178 * we will close the tunnel 179 */ 180 close(); 181 } 182 } 183 close()184 public void close() { 185 try { 186 if (!sockIn.isClosed()) 187 sockIn.close(); 188 if (!sockOut.isClosed()) 189 sockOut.close(); 190 } catch (IOException ignored) { } 191 } 192 } 193 194 /* 195 *************************************************************** 196 * helper methods follow 197 *************************************************************** 198 */ 199 200 /* 201 * This method retrieves the hostname and port of the destination 202 * that the connect request wants to establish a tunnel for 203 * communication. 204 * The input, connectStr is of the form: 205 * CONNECT server-name:server-port HTTP/1.x 206 */ retrieveConnectInfo(String connectStr)207 void retrieveConnectInfo(String connectStr) throws Exception { 208 int starti; 209 int endi; 210 String connectInfo; 211 String serverName = null; 212 try { 213 starti = connectStr.indexOf(' '); 214 endi = connectStr.lastIndexOf(' '); 215 connectInfo = connectStr.substring(starti+1, endi).trim(); 216 // retrieve server name and port 217 endi = connectInfo.indexOf(':'); 218 serverName = connectInfo.substring(0, endi); 219 serverPort = Integer.parseInt(connectInfo.substring(endi+1)); 220 } catch (Exception e) { 221 throw new IOException("Proxy recieved a request: " 222 + connectStr); 223 } 224 serverInetAddr = InetAddress.getByName(serverName); 225 } 226 getPort()227 public int getPort() { 228 return ss.getLocalPort(); 229 } 230 } 231