Uploaded image for project: 'Jira Data Center'
  1. Jira Data Center
  2. JRASERVER-11810

Random broken pages: Firefox/weblogic bug where 304 response followed by empty content packet is interpreted as 200 and then waits for content which never comes, until the socket is closed.

    XMLWordPrintable

Details

    • Suggestion
    • Resolution: Won't Fix
    • None
    • None
    • We collect Jira feedback from various sources, and we evaluate what we've collected when planning our product roadmap. To understand how this piece of feedback will be reviewed, see our Implementation of New Features Policy.

    Description

      Under certain circumstances on some application servers (this was observed on
      WebLogic 8.1), a 304 response can be closely followed by an empty content
      packet:
      "0000\r\n\r\n". For some reason, sometimes - not always, but reproducibly often
      enough - this gets interpretted as a 200 (as shown in LiveHeaders), and the
      browser then waits for the rest of the content. There is no further content
      coming and eventually the server closes the socket, leaving the browser
      displaying the following (the bit in between the lines):
      ----------------------------
      0000

      HTTP/1.1 304 Not Modified
      Date: Wed, 20 Dec 2006 06:24:02 GMT

      0000
      ----------------------------
      The server response is made up of two TCP packets, the first containing the 304
      and the second containing the empty content. If they are a single packet, the
      problem cannot be reproduced. Turning off keep-alive and switching to HTTP 1.0
      also appear to fix this (at least make it no longer visible!)

      BTW. I tried to file this on Mozilla core, but couldn't find the Bugzilla
      product. Please move over to there and to the Network:HTTP component if
      possible.

      I am going on holiday over christmas, any urgent follow up should be cc'd to
      dylan@atlassian.com

      Reproducible: Sometimes

      Steps to Reproduce:
      1. make request to dodgy web-server.
      2. web-server responds with 304 and then empty content packet.
      Actual Results:
      Client waits for server until socket is closed (20+ seconds), then shows the
      304 and content responses and as though they were the page content. If the
      request were for an image or javascript, the server tries to interpret them as
      such.

      Expected Results:
      Client properly uses cached version of resource.

      This was initially found in Firefox 2 on Ubuntu and Red Hat, confirmed on
      Windows in both version 2 and Minefield 3.0a1.

      The following Java class acts as a very simple web-server hanging on port 8070
      and reproduces the problem. To use, point the browser to this port and then
      refresh. For my Windows browser in VMWare, this could be reproduced after only
      a couple of refreshes, on the Ubuntu host, it took up to 20 refreshes before
      being seen, but was still always reproducible.
      ---------------

      
      /**
       * Simple server that returns an HTTP Response 200 on a socket and subsequently
      returns 304s with a follow-up empty
       * chunk. This reproduces a bug in Firefox that is seen with some content
      served from WebLogic.
       */
      public class WebServerTest
      {
          public static void main(String[] args) throws Exception
          {
              new WebServerTest().run();
          }
      
          static String getResponse()
          {
              StringBuilder builder = new StringBuilder("HTTP/1.1 200 OK\r\nDate:
      Wed, 20 Dec 2006 06:24:02 GMT\r\nContent-Length: ");
              String content = "<html><head><title>Test
      Page</title></head><body><h1>Some Content</h1>This is content</body></html>";
              builder.append(content.length());
              builder.append("\r\nLast-Modified: Tue, 19 Dec 2006 00:37:34
      GMT\r\n\r\n");
              builder.append(content);
              builder.append("\r\n\r\n");
              return builder.toString();
          }
      
          static void copy(String string, OutputStream stream) throws IOException
          {
              final int bufferSize = 8 * 1024;
              final Reader input = new StringReader(string);
              final Writer output = new OutputStreamWriter(stream);
              final char[] buffer = new char[bufferSize];
              int n = 0;
              while (-1 != (n = input.read(buffer)))
              {
                  output.write(buffer, 0, n);
              }
              output.flush();
          }
      
          private final Executor executor = Executors.newCachedThreadPool();
          private final ServerSocket serverSocket;
          private final String response = getResponse();
          private final String notModified = "HTTP/1.1 304 Not Modified\r\nDate: Wed,
      20 Dec 2006 06:24:02 GMT\r\n\r\n";
          private final String emptyContent = "0000\r\n\r\n";
      
          public WebServerTest()
          {
              try
              {
                  this.serverSocket = new ServerSocket(8070);
              }
              catch (UnknownHostException e)
              {
                  throw new RuntimeException(e);
              }
              catch (IOException e)
              {
                  throw new RuntimeException(e);
              }
          }
      
          public void run()
          {
              while (true)
              {
                  try
                  {
                      executor.execute(new RequestHandler(serverSocket.accept()));
                  }
                  catch (IOException e)
                  {
                      throw new RuntimeException(e);
                  }
              }
          }
      
          class RequestHandler implements Runnable
          {
              private final Socket socket;
              boolean newSocket = true;
      
              RequestHandler(Socket socket)
              {
                  this.socket = socket;
              }
      
              public void run()
              {
                  while (true)
                  {
                      try
                      {
                          getInput(socket);
                      }
                      catch (TimeoutException e)
                      {
                          try
                          {
                              socket.close();
                          }
                          catch (IOException ignore)
                          {}
                          return;
                      }
      
                      try
                      {
                          final OutputStream outputStream = socket.getOutputStream();
                          if (newSocket)
                          {
                              copy(response, outputStream);
                              newSocket = false;
                          }
                          else
                          {
                              copy(notModified, outputStream);
                              // send a second chunk, an empty content packet that
      fools firefox 
                              // into thinking more is on the way
                              copy(emptyContent, outputStream);
                          }
                          outputStream.flush();
                      }
                      catch (IOException e)
                      {
                          throw new RuntimeException(e);
                      }
                  }
              }
      
              String getInput(Socket socket) throws TimeoutException
              {
                  final int WAIT_TIME = 10;
                  final int TEN_SECONDS = 10000;
                  int waitedFor = 0;
                  StringBuilder buffer;
                  try
                  {
                      final InputStream inputStream = socket.getInputStream();
                      while (inputStream.available() == 0)
                      {
                          Thread.sleep(WAIT_TIME);
                          waitedFor = waitedFor + WAIT_TIME;
                          if (waitedFor > TEN_SECONDS)
                          {
                              socket.close();
                              throw new TimeoutException();
                          }
                      }
                      buffer = new StringBuilder(inputStream.available());
                      while (inputStream.available() > 0)
                      {
                          buffer.append((char) inputStream.read());
                      }
                  }
                  catch (IOException e)
                  {
                      throw new RuntimeException(e);
                  }
                  catch (InterruptedException e)
                  {
                      throw new RuntimeException(e);
                  }
                  return buffer.toString();
              }
          }
      }
      

      Attachments

        1. screenshot-1.jpg
          screenshot-1.jpg
          148 kB
        2. WebServerTest.java
          5 kB

        Issue Links

          Activity

            People

              Unassigned Unassigned
              dylan@atlassian.com Dylan Etkin [Atlassian]
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Time Tracking

                  Estimated:
                  Original Estimate - Not Specified
                  Not Specified
                  Remaining:
                  Remaining Estimate - 0h
                  0h
                  Logged:
                  Time Spent - 2h
                  2h