JDK-4620571 : urlconnection following redirect uses protocol of original request
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 1.3.0
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2002-01-08
  • Updated: 2017-10-31
  • Resolved: 2002-04-19
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
1.4.1 hopperFixed
Related Reports
Relates :  
Relates :  
Relates :  
Description

Name: nt126004			Date: 01/08/2002


java version "1.4.0-beta3"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-beta3-b84)
Java HotSpot(TM) Client VM (build 1.4.0-beta3-b84, mixed mode)

This problem looks like it might be related to bug 4447156.

When opening a connection to a url which has a server side redirect,
URLConnection follows the redirect and opens a connection to the target URL.
But it doesn't respect the protocol portion of the new URL (http, https,...)

If the protocol of the new URL is different from the protocol of the original
URL, I've found that URLConnection tries to connect to the new URL on the port
that is used by the original protocol.

For example, if the URL is http://host.com/something and the redirect is to
https://other.host.com/something then it attempts to connect to port 80 on
other.host.com when it should connect to port 443.

Following are 2 programs.

The first program just opens a connection to the HTTP port on the
local machine and tries to read the contents of the url "/".

The second program binds to port 80 of the local machine and tells any
client that connects to it to connect to the SSL port (which is 443)
of the local machine.

If you run the second program and then the first, you should see the
first connect to the HTTP port, get the redirect, and then fail to
connect to the HTTPS port (unless you have something running on the
HTTPS port on the machine you are running this on).

What actually happens is the first program connects to port 80, gets
the redirect but fails to notice that it is a redirect to another
protocol, so it connects to the HTTP port again and again until the
URLConnection class decides that there have been too many redirects
and throws an exception.  This is the exception:

Exception in thread "main" java.net.ProtocolException: Server redirected too many  times (20)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:683)
        at TestConnection.main(TestConnection.java:14)

--- TestConnection.java ---
import java.io.*;
import java.net.*;

// Copyright (c) 2001 Steven Procter
// All rights reserved.

// This program opens a connection to http://localhost/ and reads the
// contents and prints them to System.out.

public class TestConnection {
    public static void main(String [] args) throws Exception {
        String page = "http://localhost/";
        URL url = new URL(page);
        URLConnection conn = url.openConnection();
        conn.connect();

        InputStream input = conn.getInputStream();
        for (;;) {
            byte [] buf = new byte[1024];
            int len = input.read(buf, 0, buf.length);
            if (len <= 0)
                break;
            System.out.print(new String(buf, 0, len));
        }
    }
}
--- end TestConnection.java ---

--- Redirect.java ---
import java.net.*;
import java.io.*;

// Copyright (c) 2001 Steven Procter
// All rights reserved.
    
// This class implements a HTTP redirector, a single threaded HTTP
// server that does nothing but redirect http requests to the https
// server on the same machine.  Note that this server is for testing
// only so is single threaded, it will not accept a new connection
// while it is servicing a connection.
//
// The operation is quite simple.  The server gets a connection, reads
// requests off of the connection and redirects them all to the same
// location, the https server on this machine.  Only one connection is
// serviced at a time.
//
// An HTTP request is a series of lines followed by a blank line.
// Several requests may come over the same connection, so the server
// keeps reading from the connction until the peer shuts it down.
//
// The HTTP response contains 2 lines, a status line which contains
// the status 302, which means that the object has moved, and a line
// with the location of the new object.

public class Redirect {
    public static int listenPort = 80; // port to listen for connections on

    // Send a header redirect to the peer telling it to go to the
    // https server on the host it sent the connection request to.
    public static void sendReply(Socket sock) throws IOException {
        OutputStream out = sock.getOutputStream();
        StringBuffer reply = new StringBuffer();
        reply.append("HTTP/1.0 302 Found\r\n"
                     + "Location: https://" + sock.getLocalAddress().getHostAddress()
                     + "/\r\n\r\n");
        out.write(reply.toString().getBytes());
    }

    // Get HTTP requests from the peer on the connection.  Note that a
    // single connection may receive several requests, if the client
    // wants to get several objects from the same server.
    //
    // The protocol is read until there is a blank line, then write
    // the reply.  When the peer closes the connection, return.
    public static void serviceConnection(Socket sock) {
        try {
            // Create a buffered input stream reader to read lines from
            InputStreamReader isr = new InputStreamReader(sock.getInputStream());
            BufferedReader reader = new BufferedReader(isr);

            for (;;) {
                String line = reader.readLine();
                
                if (line == null) {
                    System.out.println("> connection closed");
                    return;
                }
                else if (line.length() == 0) {
                    System.out.println("> found HTTP request terminating blank line");
                    sendReply(sock);
                }
                else {
                    System.out.println("> got HTTP request header line");
                    // nothing, just another line of input
                }
            }
        }
        catch(Exception e) {
            System.err.println("Exception reading from peer");
            return;
        }
    }

    // Loop forever getting a connection and servicing the peer until
    // it shuts down the connection.
    public static void mainLoop(int port) {
        try {
            ServerSocket sock = new ServerSocket(port);
            for (;;) {
                try {
                    Socket conn = sock.accept();
                    System.out.println("> new connection accepted");
                    serviceConnection(conn);
                    conn.close();
                }
                catch(Exception e) {
                    System.err.println("mainLoop: exception " + e.getMessage());
                }
            }
        }
        catch(IOException io) {
            System.err.println("mainLoop: couldn't open socket on port " + port);
            System.err.println(io.getMessage());
        }
    }

    public static void main(String [] args) {
        mainLoop(listenPort);
    }
}
--- end Redirect.java ---
(Review ID: 137111) 
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: generic hopper FIXED IN: hopper INTEGRATED IN: hopper
14-06-2004

WORK AROUND Name: nt126004 Date: 01/08/2002 I'm going to write my own code to connect to URLs and download their contents. ======================================================================
11-06-2004

EVALUATION Yes. ###@###.### 2002-01-09 Needs more investigation to determine whether this should be done in Hopper. ###@###.### 2002-01-29 After discussion among Java Networking engineers, it is felt that we shouldn't automatically follow redirect from one protocol to another, for instance, from http to https and vise versa, doing so may have serious security consequences. Thus the fix is to return the server responses for redirect. Check response code and Location header field value for redirect information. It's the application's responsibility to follow the redirect. ###@###.### 2002-04-12
12-04-2002