JDK-4361492 : HTTPUrlConnection does not receive binary data correctly
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 1.3.0
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_nt
  • CPU: x86
  • Submitted: 2000-08-10
  • Updated: 2002-01-22
  • Resolved: 2001-03-10
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 Other
1.3.1_01 01Fixed 1.4.0Fixed
Related Reports
Relates :  
Description

Name: stC104175			Date: 08/10/2000


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

We use the java.net.URL class to manage a server request to a servlet.
There is a transfer of binary data between the client and the servlet,
where Objects are serialized via ByteArrayStreams and transfered as
a byte[] like this:

public byte[] requestBytes(byte[] rData)
{
  byte[] lResult = null;
  URL lURL = new URL("http://servername/servlets/TestServlet");
  HttpURLConnection lCon = (HttpURLConnection) lURL.openConnection();
  lCon.setDoOutput(true);
  lCon.setDoInput(true);
  lCon.setUseCaches(false);
  lCon.setRequestMethod("POST");
  lCon.setRequestProperty("Content-Type", "application/octet-stream");
  OutputStream lOut = lCon.getOutputStream();
  lOut.write(rData);
  lOut.close(); // Does not change behaviour, if not present
  // until here anything works fine, servlet gets all data and responds
  // correct (could be checked with Internet Explorer download)
  System.out.println(lCon.getContentEncoding()); // is null whatever the
                                                 // servlet responds
  InputStream lIn = lCon.getInputStream(); // with -Dhttp.keepAlive=false
                                           // this could takes about 5 Minutes
  int liSize = lCon.getContentLength(); // is -1, if data exceeds a certain
                                        // amount or data is transfered via
                                        // a proxy server
  System.out.println("RespCode: "+lCon.getResponseCode()); // is 200
  System.out.println("RespMsg: "+lCon.getResponseMessage()); // is 'OK'
  if (liSize > 0)
    lResult = StreamScan.blockread(lIn,liSize);
  else
    lResult = StreamScan.blockreadFully(lIn);
//  lIn.close(); // does not change behavior, if present
  return lResult;
}


StreamScan.blockread reads streamdata with respect to timeout values like this:


public class StreamScan
{
  private static final int giK_BlockSize           = 8192;
  private static final long glK_TimeoutMillis      = 800;
  private static final long glK_BlockTimeoutMillis = 5000;
  private static final long glK_SleepMillis        = 200;
  private static final long glK_BlockSleepMillis   = 500;

  private static int blockread(InputStream rInput,
    byte[] rbyBuffer, int viBufPos, int viLen)
  {
    int liBytesAvailable;
    int liByte;
    int liTotalBytesRead = 0;
    int liBufPos = viBufPos;
    if (rbyBuffer == null)
      return -1;
    int liLen = Math.min(viLen,rbyBuffer.length-viBufPos);
    try {
      do
      { // wait for data to become available or timeout is reached
        if ((liBytesAvailable = rInput.available()) == 0)
        {
          long llTimerStart = System.currentTimeMillis();
          do
          {
            try {Thread.currentThread().sleep(glK_BlockSleepMillis);}
            catch (InterruptedException ex) {}
          } while (((liBytesAvailable = rInput.available()) == 0) &&
       ((System.currentTimeMillis() - llTimerStart) < glK_BlockTimeoutMillis));
        }
        
        // cancel, if timed out
        if (liBytesAvailable == 0)
          return liTotalBytesRead;
        
        // read available bytes
        while ((liTotalBytesRead < liLen) && (liBytesAvailable > 0))
        {
          liByte = rInput.read();
          if (liByte == -1) return -1; // should not really happen
          rbyBuffer[liBufPos++] = (byte) liByte;
          liTotalBytesRead++;
          liBytesAvailable--;
        }
      } while (liTotalBytesRead < liLen);
      return liTotalBytesRead;
    }
    catch (Exception e) {return -1;}
  }

  public static byte[] blockread(InputStream rInput, int viLen)
  {
    byte[] lbyBuffer = new byte[viLen];
    if ((StreamScan.blockread(rInput,lbyBuffer,0,viLen)) < viLen)
      return null;
    else
      return lbyBuffer;
  }
}


The problem is, that this doesn't work stable. Often there is a content length
of -1 reported from the HttpURLConnection and indeed there could be read no data
from the stream. But the data IS sent from the servlet as we could prove with
a download from a browser. Anythings well until we try to get the InputStream
and read from it.

We thought it to be related to the keepAlive problem and tried the
-Dhttp.keepAlive=false parameter to java.exe, but things get even more curious
with that. Now sometimes we didnt even get the InputStream from the
HttpURLConnection (or it took until 10 Minutes!!! to get the stream and the
content was cut after a few bytes). Switching off the keepAlive in the Internet
Information Server didnt change anything by the way.

Our environtment is an NT 4 Server (SR6) with IIS4 and the JRun Servlet Engine.
Client and server are working with the JRE 1.3

After weeks of testing we believe this to be a bug of the HttpURLConnection.
It seems, as if the connection interprets a keepAlive message as the data packet
with empty content or something alike. Very strange.

Bye the way, here's the servlet code:

  ...
  byte[] lBuf = Meta.serialize(lTrans); // binary representation of an Object
  System.out.println("lBufSize: "+lBuf.length);
  response.setContentType("application/octet-stream");
  response.setContentLength(lBuf.length); // doesnt change anything, if missing
//  response.setHeader("Transfer-Encoding","chunked-data"); // doesnt any matter
//  response.setHeader("Content-Encoding","binary"); // doesnt matter either
  OutputStream lOut = response.getOutputStream();
  lOut.write(lBuf);
  ...

very simple, isnt it?
(Review ID: 108179) 
======================================================================

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

WORK AROUND Name: stC104175 Date: 08/10/2000 None ======================================================================
11-06-2004

EVALUATION This problem only happens when communicating through a proxy (in particular Netscape proxy). What's happening is that the client requests keep-alive to the proxy and the proxy does not want to keep the connection alive. The proxy is operating accroding to HTTP 1.0 even though it forwards a 1.1 response from the IIS origin server. The proxy wants to close the connection to the client but does not signal its intention to do so beforehand. Therefore, the first POST succeeds and the client sends the second POST on the first connection. The proxy then closes the connection. We should recover from this by re-opening the connection and retrying the POST. However, (due to a bug in HttpClient) we open the connection, resend the POST header but forget to send the body so everything hangs. This bug was detected already and partially fixed in 4242616. One code path (exposed here) was overlooked. michael.mcmahon@ireland 2001-02-26 -----------------------------------------------------------------------
26-02-2001