|
23 | 23 | import java.io.IOException; |
24 | 24 | import java.nio.charset.StandardCharsets; |
25 | 25 | import java.text.MessageFormat; |
26 | | -import java.util.Arrays; |
27 | 26 | import java.util.logging.Level; |
28 | 27 | import java.util.logging.Logger; |
29 | 28 |
|
@@ -149,30 +148,47 @@ public String read(int length) throws IOException { |
149 | 148 | } |
150 | 149 |
|
151 | 150 | /** |
152 | | - * Reads a null-terminated string from a {@link DataInputStream}. Note that though existing |
153 | | - * solutions for this exist, they are either not keyed exactly for our use case, or would lead to |
154 | | - * a more combersome addition to this codebase. Also note the 128 byte length is chosen from |
155 | | - * profiling and determining that it exceeds the 90th percentile size for inbound messages. |
| 151 | + * The max number of characters to read when scanning for a null-terminator. Null-terminated |
| 152 | + * strings are used in the PG wire-protocol for names of prepared statements and portals, but also |
| 153 | + * for the SQL string of a statement. We therefore need to read potentially very long strings. |
| 154 | + */ |
| 155 | + private static final int MARK_READ_LIMIT = 100_000_000; |
| 156 | + |
| 157 | + /** |
| 158 | + * Reads a null-terminated string from a {@link DataInputStream}. |
156 | 159 | * |
157 | 160 | * @return the string. |
158 | 161 | * @throws IOException if an error occurs while reading from the stream, or if no null-terminator |
159 | 162 | * is found before the end of the stream. |
160 | 163 | */ |
161 | 164 | public String readString() throws IOException { |
162 | | - byte[] buffer = new byte[128]; |
| 165 | + this.inputStream.mark(MARK_READ_LIMIT); |
163 | 166 | int index = 0; |
164 | | - while (true) { |
| 167 | + while (index < MARK_READ_LIMIT) { |
165 | 168 | byte b = this.inputStream.readByte(); |
166 | 169 | if (b == 0) { |
167 | 170 | break; |
168 | 171 | } |
169 | | - buffer[index] = b; |
170 | 172 | index++; |
171 | | - if (index == buffer.length) { |
172 | | - buffer = Arrays.copyOf(buffer, buffer.length * 2); |
| 173 | + if (index == MARK_READ_LIMIT) { |
| 174 | + throw new IOException("No null terminator found"); |
173 | 175 | } |
174 | 176 | } |
175 | | - return new String(buffer, 0, index, StandardCharsets.UTF_8); |
| 177 | + // Reset the stream to the mark and read the name (if any). |
| 178 | + this.inputStream.reset(); |
| 179 | + if (index == 0) { |
| 180 | + // No name, but we still need to skip the null-terminator. |
| 181 | + //noinspection StatementWithEmptyBody |
| 182 | + while (this.inputStream.skip(1) < 1) {} |
| 183 | + return ""; |
| 184 | + } |
| 185 | + |
| 186 | + byte[] result = new byte[index]; |
| 187 | + this.inputStream.readFully(result); |
| 188 | + // Skip the null-terminator. |
| 189 | + //noinspection StatementWithEmptyBody |
| 190 | + while (this.inputStream.skip(1) < 1) {} |
| 191 | + return new String(result, StandardCharsets.UTF_8); |
176 | 192 | } |
177 | 193 |
|
178 | 194 | /** |
|
0 commit comments