JDK-4513568 : HttpURLConnection.getInputStream() throws IOException on http 500
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 1.3.1
  • Priority: P5
  • Status: Closed
  • Resolution: Not an Issue
  • OS: generic
  • CPU: generic
  • Submitted: 2001-10-11
  • Updated: 2001-10-12
  • Resolved: 2001-10-12
Description
Name: bsT130419			Date: 10/11/2001


java version "1.3.1_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1_01)
Java HotSpot(TM) Client VM (build 1.3.1_01, mixed mode)

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

1)
Both in JDK 1.3.1_01 and 1.4.0-beta2,
calling getInputStream on a URLConnection for which the server returns a staus
code of 500, result in a IOException like :

java.io.IOException: Server returned HTTP response code: 500 for URL:
http://easysoap.sourceforge.net/cgi-bin/interopserver

This makes impossible using HttpURLConnection for reading SOAP responses, like
example 9 of the SOAP 1.1. spec.


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;

/**
 * test case to show that on jdk 1.3.1_01 and 1.4.0-beta2
 * it is not possible to read the body of an HTTP response with status code 500,
 * as for example a SOAP response with a fault.
 */
public class TestCaseFaultHttp500
{
    private static final int SERVER_PORT = 7008;

    private TestFakeHttpServer fakeHttpServer;
    private String baseServerURL;

    private static String SOAP_REQUEST =
    "<SOAP-ENV:Envelope" + "\n" +
    "  xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'" + "\n" +
    "  SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>" + "\n" +
    "   <SOAP-ENV:Body>" + "\n" +
    "       <m:GetLastTradePrice xmlns:m='Some-URI'>" + "\n" +
    "           <symbol>DIS</symbol>" + "\n" +
    "       </m:GetLastTradePrice>" + "\n" +
    "   </SOAP-ENV:Body>"                                                   +
    "</SOAP-ENV:Envelope>";

    public static void main(String[] args)
    {
        TestCaseFaultHttp500 _tc = null;
        try {
            _tc = new TestCaseFaultHttp500();
            _tc.testFault200();
            _tc.testFault500();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally{
            if(_tc!=null)
                _tc.fakeHttpServer.stop();
        }
    }

    public TestCaseFaultHttp500() throws Exception {
        fakeHttpServer = new TestFakeHttpServer(SERVER_PORT);
        baseServerURL = "http://localhost:" + SERVER_PORT;
    }

    /**
     * this works with no problems
     * with jdk 1.3.1 and 1.3.1_01
     */
    public void testFault200()  throws Exception
    {
        doTest(TestFakeHttpServer.PATH_USE_200);
    }

    /**
     * with jdk1.3.1_01 fails with
     * java.io.IOException: Server returned HTTP response code: 500 for URL: http://localhost:8008/use500
     *
     * with 1.3.1 fails with
     * java.io.FileNotFoundException: http://localhost:8008/use500
     */
    public void testFault500() throws Exception
    {
        System.out.println("-------------------------------------");
        doTest(TestFakeHttpServer.PATH_USE_500);
        System.out.println("-------------------------------------");
    }

    private void doTest(String aPath) throws Exception {
        URL _url = new URL(baseServerURL + aPath);

        System.out.println("testing with " + _url.toExternalForm());

        URLConnection _urlConnection = _url.openConnection();
        _urlConnection.setDoInput(true);
        _urlConnection.setDoOutput(true);
        _urlConnection.setUseCaches(false);

        OutputStream _os = _urlConnection.getOutputStream();
        PrintWriter _pw = new PrintWriter(new OutputStreamWriter(_os));

         System.out.println("writing ...");
        _pw.write(SOAP_REQUEST);
        _pw.flush();

        InputStream _is = _urlConnection.getInputStream();
        BufferedReader _br = new BufferedReader(new InputStreamReader(_is));

        System.out.println("reading ...");
        String _line = null;
        while(((_line = _br.readLine()) !=null))  {
            System.out.print(_line);
            System.out.print("\n");
        }
    }


}


/**
 * sends back a hardcoded soap fault, using http response code that depends
 * on the path of the POST request (200 or 500)
 */
class TestFakeHttpServer {

    public static String PATH_USE_200 = "/use200";
    public static String PATH_USE_500 = "/use500";

    private static String SOAP_FAULT_RESPONSE =
    "<SOAP-ENV:Envelope "                                              + "\n" +
    "  xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>"    + "\n" +
    "   <SOAP-ENV:Body>"                                               + "\n" +
    "       <SOAP-ENV:Fault>"                                          + "\n" +
    "           <faultcode>SOAP-ENV:Server</faultcode>"                + "\n" +
    "           <faultstring>I am just a fake server!</faultstring>"   + "\n" +
    "       </SOAP-ENV:Fault>"                                         + "\n" +
    "   </SOAP-ENV:Body>"                                              + "\n" +
    "</SOAP-ENV:Envelope>";


    private ServerSocket serverSocket;
    private boolean keepRunning = true;

    public TestFakeHttpServer(int aPort) throws Exception {
        serverSocket = new ServerSocket(aPort);

        new Thread(new Runnable() {

            public void run() {
                while (keepRunning) {
                    Socket _clientSocket = null;
                    try {
                        _clientSocket = serverSocket.accept();
                        fakeHttp(_clientSocket);
                    }
                    catch (Exception anExc) {
                        if(keepRunning)
                          anExc.printStackTrace();
                        //else a socket close is expected and normal
                    }

                    finally {
                        if (_clientSocket != null)
                            try {
                                _clientSocket.close();
                            }
                            catch (IOException e) {
                                //ignore
                            }
                    }
                }
            }
        }).start();
     }


    private void fakeHttp(Socket aSocket) throws Exception {
        InputStream _inputStream = aSocket.getInputStream();
        Reader _br = new InputStreamReader(_inputStream);

        int _responseCodeToUse = 0;
        int _contentLength = 0;
        boolean _firstLine = true;

        String _line=null;
        //read headers
        while(((_line = readLine(_br))!=null)) {

            if(_firstLine) {
                //not interested in anything but the path to see
                // if we should respond with 200 or 500
                if(_line.startsWith("POST")) {
                    if(_line.indexOf(PATH_USE_200)!=-1)
                        _responseCodeToUse = 200;
                    else if(_line.indexOf(PATH_USE_500)!=-1)
                        _responseCodeToUse = 500;
                    else
                        throw new Exception("TestFakeHttpServer - " +
                                            PATH_USE_200 + " or " + PATH_USE_500 +
                                            " POST PATH expected");
                }
                else {
                    throw new Exception("TestFakeHttpServer - POST expected");
                }
                _firstLine = false;
            }


            if(_line.toUpperCase().indexOf("CONTENT-LENGTH:")!=-1) {
                _contentLength = Integer.parseInt(_line.substring("CONTENT-LENGTH: ".length()));
            }

            if(_line.equals(""))
                break;
        }

        //read body (and discard, because we're a fake server ;-)
        for(int i=0; i<_contentLength; i++)
           _br.read();

        OutputStream _outputStream = aSocket.getOutputStream();
        PrintWriter  _pw = new PrintWriter(new OutputStreamWriter(_outputStream));

        if(_responseCodeToUse==200)
            _pw.write("HTTP/1.0 200 OK\r\n");
        else if(_responseCodeToUse==500)
           _pw.write("HTTP/1.0 500 Internal Server Error\r\n");
        else
            throw new Exception("TestFakeHttpServer - _responseCode should be 200 or 500");

        _pw.write("Content-Type: text/xml\r\n");
        _pw.write("Content-Length: " + SOAP_FAULT_RESPONSE.length() +"\r\n");
        _pw.write("\r\n");
        _pw.write(SOAP_FAULT_RESPONSE);
        _pw.flush();
    }


    /**
     * @return line without ending \r\n
     */
    private String readLine(Reader aReader) throws Exception {
        StringBuffer _line = new StringBuffer();
        int _char = 0;

        while(((_char = aReader.read())!=-1)) {
            _line.append((char)_char);

            int _length = _line.length();
            if(_length >=2) {
                if(_line.charAt(_length-1) == '\n' &&
                   _line.charAt(_length-2) == '\r') {
                    break;
                }
            }
        }

        if(_line.length() == 0)
            return null;
        else {
            return _line.substring(0, _line.length()-2);
        }
    }


    public void stop() {
        if (serverSocket != null) {
            keepRunning = false;
            try {
                ServerSocket _tmp = serverSocket;
                serverSocket = null;
                _tmp.close();
            }
            catch (IOException anIOExc) {
                anIOExc.printStackTrace();
            }
        }
    }

}





3) the output of the above program on jdk131_01 is :
C:\Edo\java\jdk1.3.1_01\bin\javaw.exe -classpath C:\Edo\java\jdk1.3.1_01
\jre\lib\rt.jar;C:\Edo\myDev\http500\classes TestCaseFaultHttp500
testing with http://localhost:7008/use200
writing ...
reading ...
<SOAP-ENV:Envelope
  xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
   <SOAP-ENV:Body>
       <SOAP-ENV:Fault>
           <faultcode>SOAP-ENV:Server</faultcode>
           <faultstring>I am just a fake server!</faultstring>
       </SOAP-ENV:Fault>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
-------------------------------------
testing with http://localhost:7008/use500
writing ...
java.io.IOException: Server returned HTTP response code: 500 for URL:
http://localhost:7008/use500
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream
(HttpURLConnection.java:564)
	at TestCaseFaultHttp500.doTest(TestCaseFaultHttp500.java:93)
	at TestCaseFaultHttp500.testFault500(TestCaseFaultHttp500.java:72)
	at TestCaseFaultHttp500.main(TestCaseFaultHttp500.java:37)
Process terminated with exit code 0
(Review ID: 133429) 
======================================================================

Comments
WORK AROUND Name: bsT130419 Date: 10/11/2001 a workaround is pluggin in your own stream protocol handler for http ! ======================================================================
11-06-2004

EVALUATION The observed behaviour is as expected - if the http server returns an error then getInputStream will throw an IOException. Note that this doesn't preclude the application from reading the response. The SOAP example can be modified as follows :- HttpURLConnection httpConn = (HttpURLConnection)_urlConnection; InputStream _is; if (httpConn.getResponseCode() >= 400) { _is = httpConn.getInputStream(); } else { /* error from server */ _is = httpConn.getErrorStream(); } Given that this isn't a bug in the http client I am closing this bug. ###@###.### 2001-10-11
11-10-2001