1 /* 2 * Copyright (c) 2005, 2010, 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 * @test 26 * @bug 6226610 6973030 27 * @run main/othervm B6226610 28 * @summary HTTP tunnel connections send user headers to proxy 29 */ 30 31 /* This class includes a proxy server that processes the HTTP CONNECT request, 32 * and validates that the request does not have the user defined header in it. 33 * The proxy server always returns 400 Bad Request so that the Http client 34 * will not try to proceed with the connection as there is no back end http server. 35 */ 36 37 import java.io.*; 38 import java.net.*; 39 import sun.net.www.MessageHeader; 40 41 public class B6226610 { 42 static HeaderCheckerProxyTunnelServer proxy; 43 44 public static void main(String[] args) throws Exception 45 { 46 proxy = new HeaderCheckerProxyTunnelServer(); 47 proxy.start(); 48 49 String hostname = InetAddress.getLocalHost().getHostName(); 50 51 try { 52 URL u = new URL("https://" + hostname + "/"); 53 System.out.println("Connecting to " + u); 54 InetSocketAddress proxyAddr = new InetSocketAddress(hostname, proxy.getLocalPort()); 55 java.net.URLConnection c = u.openConnection(new Proxy(Proxy.Type.HTTP, proxyAddr)); 56 57 /* I want this header to go to the destination server only, protected 58 * by SSL 59 */ 60 c.setRequestProperty("X-TestHeader", "value"); 61 c.connect(); 62 63 } catch (IOException e) { 64 if ( e.getMessage().equals("Unable to tunnel through proxy. Proxy returns \"HTTP/1.1 400 Bad Request\"") ) 65 { 66 // OK. Proxy will always return 400 so that the main thread can terminate correctly. 67 } 68 else 69 System.out.println(e); 70 } finally { 71 if (proxy != null) proxy.shutdown(); 72 } 73 74 if (HeaderCheckerProxyTunnelServer.failed) 75 throw new RuntimeException("Test failed; see output"); 76 } 77 } 78 79 class HeaderCheckerProxyTunnelServer extends Thread 80 { 81 public static boolean failed = false; 82 83 private static ServerSocket ss = null; 84 85 // client requesting for a tunnel 86 private Socket clientSocket = null; 87 88 /* 89 * Origin server's address and port that the client 90 * wants to establish the tunnel for communication. 91 */ 92 private InetAddress serverInetAddr; 93 private int serverPort; 94 95 public HeaderCheckerProxyTunnelServer() throws IOException 96 { 97 if (ss == null) { 98 ss = new ServerSocket(0); 99 } 100 } 101 102 void shutdown() { 103 try { ss.close(); } catch (IOException e) {} 104 } 105 106 public void run() 107 { 108 try { 109 clientSocket = ss.accept(); 110 processRequests(); 111 } catch (IOException e) { 112 System.out.println("Proxy Failed: " + e); 113 e.printStackTrace(); 114 try { 115 ss.close(); 116 } 117 catch (IOException excep) { 118 System.out.println("ProxyServer close error: " + excep); 119 excep.printStackTrace(); 120 } 121 } 122 } 123 124 /** 125 * Returns the port on which the proxy is accepting connections. 126 */ 127 public int getLocalPort() { 128 return ss.getLocalPort(); 129 } 130 131 /* 132 * Processes the CONNECT request 133 */ 134 private void processRequests() throws IOException 135 { 136 InputStream in = clientSocket.getInputStream(); 137 MessageHeader mheader = new MessageHeader(in); 138 String statusLine = mheader.getValue(0); 139 140 if (statusLine.startsWith("CONNECT")) { 141 // retrieve the host and port info from the status-line 142 retrieveConnectInfo(statusLine); 143 144 if (mheader.findValue("X-TestHeader") != null) { 145 System.out.println("Proxy should not receive user defined headers for tunneled requests"); 146 failed = true; 147 } 148 149 // 6973030 150 String value; 151 if ((value = mheader.findValue("Proxy-Connection")) == null || 152 !value.equals("keep-alive")) { 153 System.out.println("Proxy-Connection:keep-alive not being sent"); 154 failed = true; 155 } 156 157 //This will allow the main thread to terminate without trying to perform the SSL handshake. 158 send400(); 159 160 in.close(); 161 clientSocket.close(); 162 ss.close(); 163 } 164 else { 165 System.out.println("proxy server: processes only " 166 + "CONNECT method requests, recieved: " 167 + statusLine); 168 } 169 } 170 171 private void send400() throws IOException 172 { 173 OutputStream out = clientSocket.getOutputStream(); 174 PrintWriter pout = new PrintWriter(out); 175 176 pout.println("HTTP/1.1 400 Bad Request"); 177 pout.println(); 178 pout.flush(); 179 } 180 181 private void restart() throws IOException { 182 (new Thread(this)).start(); 183 } 184 185 /* 186 * This method retrieves the hostname and port of the destination 187 * that the connect request wants to establish a tunnel for 188 * communication. 189 * The input, connectStr is of the form: 190 * CONNECT server-name:server-port HTTP/1.x 191 */ 192 private void retrieveConnectInfo(String connectStr) throws IOException { 193 194 int starti; 195 int endi; 196 String connectInfo; 197 String serverName = null; 198 try { 199 starti = connectStr.indexOf(' '); 200 endi = connectStr.lastIndexOf(' '); 201 connectInfo = connectStr.substring(starti+1, endi).trim(); 202 // retrieve server name and port 203 endi = connectInfo.indexOf(':'); 204 serverName = connectInfo.substring(0, endi); 205 serverPort = Integer.parseInt(connectInfo.substring(endi+1)); 206 } catch (Exception e) { 207 throw new IOException("Proxy recieved a request: " 208 + connectStr); 209 } 210 serverInetAddr = InetAddress.getByName(serverName); 211 } 212 }