blob: c04ad1d1560245edb572e5a88b11709334d22fd4 [file] [log] [blame]
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 6226610 6973030
* @summary HTTP tunnel connections send user headers to proxy
* @modules java.base/sun.net.www
* @run main/othervm B6226610
*/
/* This class includes a proxy server that processes the HTTP CONNECT request,
* and validates that the request does not have the user defined header in it.
* The proxy server always returns 400 Bad Request so that the Http client
* will not try to proceed with the connection as there is no back end http server.
*/
import java.io.*;
import java.net.*;
import sun.net.www.MessageHeader;
public class B6226610 {
static HeaderCheckerProxyTunnelServer proxy;
public static void main(String[] args) throws Exception
{
proxy = new HeaderCheckerProxyTunnelServer();
proxy.start();
String hostname = InetAddress.getLocalHost().getHostName();
try {
URL u = new URL("https://" + hostname + "/");
System.out.println("Connecting to " + u);
InetSocketAddress proxyAddr = new InetSocketAddress(hostname, proxy.getLocalPort());
java.net.URLConnection c = u.openConnection(new Proxy(Proxy.Type.HTTP, proxyAddr));
/* I want this header to go to the destination server only, protected
* by SSL
*/
c.setRequestProperty("X-TestHeader", "value");
c.connect();
} catch (IOException e) {
if ( e.getMessage().equals("Unable to tunnel through proxy. Proxy returns \"HTTP/1.1 400 Bad Request\"") )
{
// OK. Proxy will always return 400 so that the main thread can terminate correctly.
}
else
System.out.println(e);
} finally {
if (proxy != null) proxy.shutdown();
}
if (HeaderCheckerProxyTunnelServer.failed)
throw new RuntimeException("Test failed; see output");
}
}
class HeaderCheckerProxyTunnelServer extends Thread
{
public static boolean failed = false;
private static ServerSocket ss = null;
// client requesting for a tunnel
private Socket clientSocket = null;
/*
* Origin server's address and port that the client
* wants to establish the tunnel for communication.
*/
private InetAddress serverInetAddr;
private int serverPort;
public HeaderCheckerProxyTunnelServer() throws IOException
{
if (ss == null) {
ss = new ServerSocket(0);
}
}
void shutdown() {
try { ss.close(); } catch (IOException e) {}
}
public void run()
{
try {
clientSocket = ss.accept();
processRequests();
} catch (IOException e) {
System.out.println("Proxy Failed: " + e);
e.printStackTrace();
try {
ss.close();
}
catch (IOException excep) {
System.out.println("ProxyServer close error: " + excep);
excep.printStackTrace();
}
}
}
/**
* Returns the port on which the proxy is accepting connections.
*/
public int getLocalPort() {
return ss.getLocalPort();
}
/*
* Processes the CONNECT request
*/
private void processRequests() throws IOException
{
InputStream in = clientSocket.getInputStream();
MessageHeader mheader = new MessageHeader(in);
String statusLine = mheader.getValue(0);
if (statusLine.startsWith("CONNECT")) {
// retrieve the host and port info from the status-line
retrieveConnectInfo(statusLine);
if (mheader.findValue("X-TestHeader") != null) {
System.out.println("Proxy should not receive user defined headers for tunneled requests");
failed = true;
}
// 6973030
String value;
if ((value = mheader.findValue("Proxy-Connection")) == null ||
!value.equals("keep-alive")) {
System.out.println("Proxy-Connection:keep-alive not being sent");
failed = true;
}
//This will allow the main thread to terminate without trying to perform the SSL handshake.
send400();
in.close();
clientSocket.close();
ss.close();
}
else {
System.out.println("proxy server: processes only "
+ "CONNECT method requests, recieved: "
+ statusLine);
}
}
private void send400() throws IOException
{
OutputStream out = clientSocket.getOutputStream();
PrintWriter pout = new PrintWriter(out);
pout.println("HTTP/1.1 400 Bad Request");
pout.println();
pout.flush();
}
private void restart() throws IOException {
(new Thread(this)).start();
}
/*
* This method retrieves the hostname and port of the destination
* that the connect request wants to establish a tunnel for
* communication.
* The input, connectStr is of the form:
* CONNECT server-name:server-port HTTP/1.x
*/
private void retrieveConnectInfo(String connectStr) throws IOException {
int starti;
int endi;
String connectInfo;
String serverName = null;
try {
starti = connectStr.indexOf(' ');
endi = connectStr.lastIndexOf(' ');
connectInfo = connectStr.substring(starti+1, endi).trim();
// retrieve server name and port
endi = connectInfo.indexOf(':');
serverName = connectInfo.substring(0, endi);
serverPort = Integer.parseInt(connectInfo.substring(endi+1));
} catch (Exception e) {
throw new IOException("Proxy recieved a request: "
+ connectStr);
}
serverInetAddr = InetAddress.getByName(serverName);
}
}