Uploaded image for project: 'Undertow'
  1. Undertow
  2. UNDERTOW-197

ServletInputStreamImpl.read() return 0x00 when there's no data available

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Done
    • Icon: Major Major
    • 1.0.2.Final
    • 1.0.0.Final
    • Servlet
    • None
    • Hide

      Do unit test in undertow-servlet project, add following code snippets first.

      1) In io.undertow.servlet.test.streams.AsyncInputStreamServlet.MyListener.java, replace original onDataAvailable() method to following one
      @Override
      public void onDataAvailable() throws IOException {
      int read;
      while (inputStream.isReady()) {
      read = inputStream.read();
      if (read == 0)

      { System.out.println("onDataAvailable> read 0x00"); }

      if (read != -1)

      { dataToWrite.write(read); }

      else

      { onWritePossible(); }

      }
      }

      2) In io.undertow.servlet.test.streams.ServletInputStreamTestCase.java, add

      private void runTestViaJavaImpl(final String message, String url)
      throws IOException {
      HttpURLConnection urlcon = null;
      try {
      String uri = DefaultServer.getDefaultServerURL() + "/servletContext/" + url;
      urlcon = (HttpURLConnection) new URL(uri).openConnection();
      urlcon.setInstanceFollowRedirects(true);
      urlcon.setRequestProperty("Connection", "close");
      urlcon.setRequestMethod("POST");
      urlcon.setDoInput(true);
      urlcon.setDoOutput(true);
      OutputStream os = urlcon.getOutputStream();
      os.write(message.getBytes());
      os.close();
      Assert.assertEquals(200, urlcon.getResponseCode());
      InputStream is = urlcon.getInputStream();
      byte[] buf = new byte[256];
      int len = is.read(buf);
      is.close();
      final String response = new String(buf, 0, len);
      if (!message.equals(response))

      { System.out.println(String.format("response=%s", Hex.encodeHexString(response.getBytes()))); }

      Assert.assertEquals(message, response);
      } finally {
      if (urlcon != null)

      { urlcon.disconnect(); }

      }
      }

      @Test
      public void testAsyncServletInputStream3() {
      String message = "to_user_id=7999&msg_body=msg3";
      for (int i = 0; i < 200; ++i) {
      try

      { runTestViaJavaImpl(message, ASYNC_SERVLET); }

      catch (Throwable e)

      { System.out.println("test failed with i equal to " + i); e.printStackTrace(); throw new RuntimeException("test failed with i equal to " + i, e); }

      }
      }

      3) in io.undertow.servlet.spec.ServletInputStreamImpl.read(byte[] b, int off, int len), add
      if (copied == 0)

      { System.out.println("ServletInputStreamImpl.read> copied is 0"); }

      4) unit test testAsyncServletInputStream3

      Show
      Do unit test in undertow-servlet project, add following code snippets first. 1) In io.undertow.servlet.test.streams.AsyncInputStreamServlet.MyListener.java, replace original onDataAvailable() method to following one @Override public void onDataAvailable() throws IOException { int read; while (inputStream.isReady()) { read = inputStream.read(); if (read == 0) { System.out.println("onDataAvailable> read 0x00"); } if (read != -1) { dataToWrite.write(read); } else { onWritePossible(); } } } 2) In io.undertow.servlet.test.streams.ServletInputStreamTestCase.java, add private void runTestViaJavaImpl(final String message, String url) throws IOException { HttpURLConnection urlcon = null; try { String uri = DefaultServer.getDefaultServerURL() + "/servletContext/" + url; urlcon = (HttpURLConnection) new URL(uri).openConnection(); urlcon.setInstanceFollowRedirects(true); urlcon.setRequestProperty("Connection", "close"); urlcon.setRequestMethod("POST"); urlcon.setDoInput(true); urlcon.setDoOutput(true); OutputStream os = urlcon.getOutputStream(); os.write(message.getBytes()); os.close(); Assert.assertEquals(200, urlcon.getResponseCode()); InputStream is = urlcon.getInputStream(); byte[] buf = new byte [256] ; int len = is.read(buf); is.close(); final String response = new String(buf, 0, len); if (!message.equals(response)) { System.out.println(String.format("response=%s", Hex.encodeHexString(response.getBytes()))); } Assert.assertEquals(message, response); } finally { if (urlcon != null) { urlcon.disconnect(); } } } @Test public void testAsyncServletInputStream3() { String message = "to_user_id=7999&msg_body=msg3"; for (int i = 0; i < 200; ++i) { try { runTestViaJavaImpl(message, ASYNC_SERVLET); } catch (Throwable e) { System.out.println("test failed with i equal to " + i); e.printStackTrace(); throw new RuntimeException("test failed with i equal to " + i, e); } } } 3) in io.undertow.servlet.spec.ServletInputStreamImpl.read(byte[] b, int off, int len), add if (copied == 0) { System.out.println("ServletInputStreamImpl.read> copied is 0"); } 4) unit test testAsyncServletInputStream3
    • Workaround Exists
    • Hide

      Use ServletInputStream.read(byte[] buf) in onDataAvailable() method, even ServletInputStream.read(byte[] buf) return 0, output.write(buf, 0, 0) won't write any byte.

      Show
      Use ServletInputStream.read(byte[] buf) in onDataAvailable() method, even ServletInputStream.read(byte[] buf) return 0, output.write(buf, 0, 0) won't write any byte.

      I'm running WildFly 8 on staging to supply HTTP based API, I implemented javax.servlet.ReadListener, ServletInputStream.read() was used to read data in onDataAvailable() method, our Android client use HttpURLConnection to access API, I found servlet got wrong request parameter name sometimes, in fact, 0x00 was added just at the beginning of HTTP posted data.

      I did several testing, I located the issue root to ServletInputStreamImpl.read(byte[] b, int off, int len), I haven't digged much more, so I'm not sure it's xnio issue or not. copied byte may be 0 in this method, since it's not -1, it will be returned by ServletInputStreamImpl.read(), and I accepted it, then issue occur; but it will be ok if ServletInputStream.read(byte[]) is used, since output.write(buf, 0, 0) won't write any byte.

      Unit test of io.undertow.servlet.test.streams.ServletInputStreamTestCase.testAsyncServletInputStream() will pass through every time, since HTTPClient is used, but not HttpURLConnection, I added new unit test based on HttpURLConnection, they make difference, issue just occur when HttpURLConnection is used. I captured packets via Wireshark and found one difference: HTTPClient commit HTTP header and post body in one tcp packet, HttpURLConnection commit HTTP header in one tcp packet and post body in another tcp packet.

            sdouglas1@redhat.com Stuart Douglas
            zhs_jira Zen Zhong (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: