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