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

BufferedReader doesn't read full \r\n line ending when it doesn't fit in buffer

XMLWordPrintable

    • b18
    • generic
    • generic

      ADDITIONAL SYSTEM INFORMATION :
      Windows 10, OpenJDK 17.0.1 and 24-ea+6

      A DESCRIPTION OF THE PROBLEM :
      BufferedReader.readLine() doesn't read the full \r\n line ending when the buffer only has room for the \r. This means if another BufferedReader is created to read the next line, readLine() sees the \n from the line ending that was already partially read and returns a blank line.

      I recognise this issue doesn't occur if a single BufferedReader is used, but I couldn't see anything in the javadoc saying a second one cannot be used after the first, and in any case the behaviour shouldn't be changing with the buffer size or size of the line being read. I think it would be better if instead of setting skipLF to true without reading the line feed, it would be better to actually read it. A new BufferedReader should be in the same state as the first (apart from the contents of the buffer) so it is able to continue where the first one ended.

      In my code reproducing the issue, it should be acceptable to use a second BufferedReader after the first has read all the data so far, or else there should be an exception thrown or javadoc warning against it. The code is based on a real Java application which passes texts one by one to an external Process. The output of the external Process must be received via its InputStream before the next text can be sent to the Process. A blank line is used to indicate the end of the output for a text. A BufferedReader is used to read the InputStream until there is a blank line, indicating the end of the data for the time being. A new BufferedReader is created around the InputStream each time the output for a text needs to be read. Because all the data in the InputStream is being read each time, this works perfectly fine and no data is missed. Only in this case where the \r\n overflows the buffer is there any issue, causing the output for the second text to be read as blank, and hereafter the output is out of sync with the input, as the real output of the second text is read when the output from the third text ought to be read, and so on.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run the source code and observe the second line of the output is incorrect. Change the number of 'a's in the first line and re-run; observe the second line of output is now correct.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The first line printed should be lots of 'a's, and the second line printed should be 'b'.
      ACTUAL -
      The first line printed is correct, but the second line printed is blank.

      ---------- BEGIN SOURCE ----------
      import java.io.BufferedReader;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.InputStreamReader;
      import java.util.ArrayList;
      import java.util.List;

      import static java.nio.charset.StandardCharsets.UTF_8;

      public class Main {
      private static class AppendableInputStream extends InputStream {
      private final List<Byte> bytes = new ArrayList<>();

      public synchronized void append(byte[] bytes) {
      for (byte b : bytes) {
      this.bytes.add(b);
      }
      notifyAll();
      }

      @Override
      public synchronized int read() {
      while (available() == 0) {
      try {
      wait();
      } catch (InterruptedException e) {
      throw new RuntimeException(e);
      }
      }
      return bytes.remove(0);
      }

      @Override
      public synchronized int read(byte[] b, int off, int len) {
      len = Math.max(1, Math.min(len, available()));
      for (int i = off; i < off + len; i++) {
      b[i] = (byte) read();
      }
      return len;
      }

      @Override
      public synchronized int available() {
      return bytes.size();
      }
      }

      public static void main(String[] args) throws IOException {
      try (AppendableInputStream appendableInputStream = new AppendableInputStream()) {
      appendableInputStream.append(("a".repeat(8191) + "\r\n").getBytes(UTF_8));
      printNextLine(appendableInputStream);
      appendableInputStream.append(("b\r\n").getBytes(UTF_8));
      printNextLine(appendableInputStream);
      }
      }

      private static void printNextLine(InputStream inputStream) throws IOException {
      BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
      String line = bufferedReader.readLine();
      System.out.println(line);
      }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Use a single shared BufferedReader instead of creating several, or else be aware that there may be a false blank line at the start of the data when a new BufferedReader is used.

      FREQUENCY : always


            bpb Brian Burkhalter
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            6 Start watching this issue

              Created:
              Updated:
              Resolved: