diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderWriter.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderWriter.java index 362dd2f79d..64c58f3efd 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderWriter.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -19,13 +19,15 @@ import java.io.Closeable; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; import java.nio.charset.Charset; import java.security.AccessController; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -134,7 +136,7 @@ public static Charset getCharset(MediaType m) { * @throws IOException if there is an error reading from the input stream. */ public static String readFromAsString(InputStream in, MediaType type) throws IOException { - return readFromAsString(new InputStreamReader(in, getCharset(type))); + return new String(readAllBytes(in), getCharset(type)); } /** @@ -154,6 +156,73 @@ public static String readFromAsString(Reader reader) throws IOException { } return sb.toString(); } + /** + * The maximum size of array to allocate. + * Some VMs reserve some header words in an array. + * Attempts to allocate larger arrays may result in + * OutOfMemoryError: Requested array size exceeds VM limit + */ + private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; + + /** + * Java 9+ InputStream::readAllBytes + * TODO Replace when Java 8 not any longer supported (3.1+) + */ + private static byte[] readAllBytes(InputStream inputStream) throws IOException { + List bufs = null; + byte[] result = null; + int total = 0; + int remaining = Integer.MAX_VALUE; + int n; + do { + byte[] buf = new byte[Math.min(remaining, BUFFER_SIZE)]; + int nread = 0; + + // read to EOF which may read more or less than buffer size + while ((n = inputStream.read(buf, nread, + Math.min(buf.length - nread, remaining))) > 0) { + nread += n; + remaining -= n; + } + + if (nread > 0) { + if (MAX_BUFFER_SIZE - total < nread) { + throw new OutOfMemoryError("Required array size too large"); + } + total += nread; + if (result == null) { + result = buf; + } else { + if (bufs == null) { + bufs = new ArrayList<>(); + bufs.add(result); + } + bufs.add(buf); + } + } + // if the last call to read returned -1 or the number of bytes + // requested have been read then break + } while (n >= 0 && remaining > 0); + + if (bufs == null) { + if (result == null) { + return new byte[0]; + } + return result.length == total ? result : Arrays.copyOf(result, total); + } + + result = new byte[total]; + int offset = 0; + remaining = total; + for (byte[] b : bufs) { + int count = Math.min(b.length, remaining); + System.arraycopy(b, 0, result, offset, count); + offset += count; + remaining -= count; + } + + return result; + } /** * Convert a string to bytes and write those bytes to an output stream.