Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4764567

applet hangs on OutputStream.close() method

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Resolved
    • Priority: P2
    • Resolution: Fixed
    • Affects Version/s: 1.3.1_03
    • Fix Version/s: 1.3.1_07
    • Component/s: deploy
    • Labels:
      None
    • Subcomponent:
    • Resolved In Build:
      07
    • CPU:
      generic
    • OS:
      windows_nt

      Description

      We have an Applet running in IE 5.0/5.5/6.0 using the Java Plugin
      version 1.3.1_03.
      OS is 98, win NT, and win2000

      The applet uses a URLConnection to post serialized
      objects to a servlet and receive a serialized object back in the
      response. Everything works fine, but every now and again, when trying
      to communicate with the servlet, the applet hangs and the CPU goes
      to 100%.

      Further investigation shows the applet is hanging on the
      OutputStream.close() method. This is called after the serialized
      object has been written to the stream and we are ready to close the
      stream and receive the reply. The OutputStream is given to us be the
      URLConnection.

      Further investigation shows that the OutputStream is actually a
      BrowserHttpsOutputStream provided by the SSL protocol handler
      BrowerHttpsURLConnection. That close method does quite a bit of
      work:

      public synchronized void close() throws IOException
          {
      // Make sure the stream has not been closed multiple times
      if (bConnected)
      {
      bConnected = false;
      super.close();

      byte[] postResponse;
      byte[] buf = toByteArray();

      if (buf == null)
      postResponse = postHttpsURL(urlConnection, urlConnection.getURL().toString(), null, 0);
      else
      postResponse = postHttpsURL(urlConnection, urlConnection.getURL().toString(), buf, buf.length);

      if (urlConnection.getDoInput()) {
      urlConnection.postResponse = postResponse;
      urlConnection.postResponseReady = true;
      }
      }
          }

      Here we clearly see that the close() method not only begins the process
      of sending information to the server, but also waits for the reply which
      is stored in internal buffers. So, the fact that we never return from this
      method could mean many things.

      This method makes native calls to some C++ glue code, CWininet.cpp. This code
      pretty much does the same thing at a lower level:

      jbyteArray CWininet::postHttpsURL
      (
      JNIEnv * env,
      jobject urlConnection,
      jstring url,
      jbyteArray byteArray,
      jsize byteArrayLength
      )
      {
          LPVOID lpVoid = env->GetByteArrayElements(byteArray, NULL);
          bool bRes = sendRequest(env, urlConnection, lpVoid, byteArrayLength);
          env->ReleaseByteArrayElements(byteArray, (jbyte *) lpVoid, JNI_ABORT);
          
          if (!bRes)
      return NULL;

          return this->readFromConnection(env);
      }

      The sendRequest and readFromConnection procedures use the MFC WinInet
      libraries to do the actual work of establishing the SSL connections and
      tranferring the data. Here is the code for readFromConnection:

      //=--------------------------------------------------------------------------=
      // CWininet::readFromConnection
      //=--------------------------------------------------------------------------=
      // Read from an open connection
      //
      // Parameters :
      //
      // Return
      // byte[] the input stream
      //
      jbyteArray CWininet::readFromConnection(JNIEnv *env)
      {
          DWORD nRead = 0, nTotalRead = 0;
          DWORD dwStatus=0;
          DWORD dwStatusSize = sizeof(DWORD);

          ::HttpQueryInfo(m_hRequest, HTTP_QUERY_FLAG_NUMBER |
      HTTP_QUERY_STATUS_CODE, &dwStatus, &dwStatusSize, NULL);

          if (dwStatus == HTTP_STATUS_OK) {
      DWORD nContentLength = 0;
      DWORD nLengthSize = sizeof(DWORD);
      ::HttpQueryInfo(m_hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
      &nContentLength, &nLengthSize, NULL);

      if (nContentLength!=0) {
      jbyteArray byteArray = env->NewByteArray(nContentLength);
      char szBuffer[HTTP_BUFFERLEN];
      nTotalRead = 0;
      do {
      DWORD nMaxRead = HTTP_BUFFERLEN;
      if (nContentLength < nTotalRead + nMaxRead)
      nMaxRead = nContentLength - nTotalRead;
      if (!::InternetReadFile(m_hRequest, szBuffer, nMaxRead, &nRead)) {
      nRead=0;
      } else {
      if (byteArray)
      env->SetByteArrayRegion(byteArray, nTotalRead,
      nRead,
      reinterpret_cast<jbyte*> (szBuffer));

      nTotalRead+=nRead;
      }
      }
      while (nTotalRead < nContentLength);

      return byteArray;
      } else {
      jbyteArray byteArray=NULL;
      DWORD dwAllocSize = HTTP_BUFFERLEN;
      DWORD dwTotalRead=0;
      LPVOID lpVoid = HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, dwAllocSize);

      do {
      nRead=0;
      if (::InternetReadFile(m_hRequest, ((char *) lpVoid)+dwTotalRead, dwAllocSize, &nRead) && nRead)
      {
      lpVoid=HeapReAlloc(g_hHeap, HEAP_ZERO_MEMORY, lpVoid, dwTotalRead+nRead+dwAllocSize);
      }
      dwTotalRead=dwTotalRead+nRead;
      }
      while (nRead);

      if (dwTotalRead>0) {
      byteArray = env->NewByteArray(dwTotalRead);
      env->SetByteArrayRegion(byteArray, 0, dwTotalRead,
      reinterpret_cast<jbyte*> (lpVoid));
      }
      HeapFree(g_hHeap, 0, lpVoid);
      return byteArray;
      }
          }

          return NULL;
      }

      I believe that this code is fundimentally weak, although I can't be
      sure. The MSDN API shows that the InternetReadFile command returns true
      or false depending on the success of the call, but the above code does
      not do anything useful with that information. For example, if we have
      contentLength > 0, but the connection drops and we never get all the data,
      this code will loop infinitely. There is no sanity checking to break us
      out of these conditions. The same appears to be true for sending data
      as well as receiving it.

      Unfortunately, this problem could be even further down the stack in the
      bowels of IE or Windows libraries. People have reported 100% utilization
      from the browser even without the plugin. However, our Applet code should
      be able to break out. --BUT-- don't worry about that possibility right now.

      The question now is HOW do we determine where in this code we are failing?


        Attachments

          Activity

            People

            Assignee:
            abhalesunw Ajit Bhale (Inactive)
            Reporter:
            msusko Mark Susko (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: