Skip to content
This repository was archived by the owner on May 28, 2018. It is now read-only.

Commit a7f358a

Browse files
Petr BoudaGerrit Code Review
authored andcommitted
Merge "JERSEY-2957: Unsatisfied delimiter buffer may contain a partial delimiter on the tail."
2 parents fff9b25 + eae9de4 commit a7f358a

File tree

2 files changed

+66
-16
lines changed

2 files changed

+66
-16
lines changed

core-client/src/main/java/org/glassfish/jersey/client/ChunkedInput.java

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public FixedBoundaryParser(final byte[] boundary) {
118118
@Override
119119
public byte[] readChunk(final InputStream in) throws IOException {
120120
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
121-
final byte[] delimiterBuffer = new byte[delimiter.length];
121+
byte[] delimiterBuffer = new byte[delimiter.length];
122122

123123
int data;
124124
int dPos;
@@ -135,24 +135,24 @@ public byte[] readChunk(final InputStream in) throws IOException {
135135
break;
136136
}
137137
} else if (dPos > 0) {
138-
// flush delimiter buffer
139-
buffer.write(delimiterBuffer, 0, dPos);
140-
dPos = 0;
141-
142-
if (b == delimiter[dPos]) {
143-
// last read byte is the first byte of of the chunk delimiter
144-
delimiterBuffer[dPos++] = b;
145-
if (dPos == delimiter.length) {
146-
// found chunk delimiter
147-
break;
148-
}
149-
} else {
150-
// last read byte is not the first byte of the chunk delimiter
138+
delimiterBuffer[dPos] = b;
139+
140+
int matched = matchTail(delimiterBuffer, 1, dPos, delimiter);
141+
if (matched == 0) {
142+
// flush delimiter buffer
143+
buffer.write(delimiterBuffer, 0, dPos);
151144
buffer.write(b);
145+
dPos = 0;
146+
} else if (matched == delimiter.length) {
147+
// found chunk delimiter
148+
break;
149+
} else {
150+
// one or more elements of a previous buffered delimiter
151+
// are parts of a current buffered delimiter
152+
buffer.write(delimiterBuffer, 0, dPos + 1 - matched);
153+
dPos = matched;
152154
}
153-
154155
} else {
155-
// last read byte is not part of the chunk delimiter
156156
buffer.write(b);
157157
}
158158
}
@@ -166,6 +166,47 @@ public byte[] readChunk(final InputStream in) throws IOException {
166166

167167
return (buffer.size() > 0) ? buffer.toByteArray() : null;
168168
}
169+
170+
/**
171+
* Tries to find an element intersection between two arrays in a way that intersecting elements must be
172+
* at the tail of the first array and at the beginning of the second array.
173+
* <p>
174+
* For example, consider the following two arrays:
175+
* <pre>
176+
* a1: {a, b, c, d, e}
177+
* a2: {d, e, f, g}
178+
* </pre>
179+
* In this example, the intersection of tail of {@code a1} with head of {@code a2} is <tt>{d, e}</tt>
180+
* and consists of 2 overlapping elements.
181+
* </p>
182+
* The method takes the first array represented as a sub-array in buffer demarcated by an offset and length.
183+
* The second array is a fixed pattern to be matched. The method then compares the tail of the
184+
* array in the buffer with the head of the pattern and returns the number of intersecting elements,
185+
* or zero in case the two arrays do not intersect tail to head.
186+
*
187+
* @param buffer byte buffer containing the array whose tail to intersect.
188+
* @param offset start of the array to be tail-matched in the {@code buffer}.
189+
* @param length length of the array to be tail-matched.
190+
* @param pattern pattern to be head-matched.
191+
* @return {@code 0} if any part of the tail of the array in the buffer does not match
192+
* any part of the head of the pattern, otherwise returns number of overlapping elements.
193+
*/
194+
private static int matchTail(byte[] buffer, int offset, int length, byte[] pattern) {
195+
outer:
196+
for (int i = 0; i < length; i++) {
197+
final int tailLength = length - i;
198+
for (int j = 0; j < tailLength; j++) {
199+
if (buffer[offset + i + j] != pattern[j]) {
200+
// mismatch - continue with shorter tail
201+
continue outer;
202+
}
203+
}
204+
205+
// found the longest matching tail
206+
return tailLength;
207+
}
208+
return 0;
209+
}
169210
}
170211

171212
/**

core-client/src/test/java/org/glassfish/jersey/client/ChunkedInputTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ public void testFixedBoundaryParserDelimiter4() throws IOException {
9393

9494
// input has the same beginning as the delimiter
9595
assertEquals("12", parse(parser, "121234"));
96+
97+
// input ends with first char of delimiter
98+
assertEquals("1231", parse(parser, "1231"));
9699
}
97100

98101
@Test
@@ -112,6 +115,12 @@ public void testFixedBoundaryParserDelimiter1() throws IOException {
112115
assertEquals("abc123", parse(parser, "abc123"));
113116
}
114117

118+
@Test
119+
public void delimiterWithRepeatedInitialCharacters() throws IOException {
120+
ChunkParser parser = ChunkedInput.createParser("**b**");
121+
assertEquals("1*", parse(parser, "1***b**"));
122+
}
123+
115124
private static String parse(ChunkParser parser, String str) throws IOException {
116125
InputStream input = new ByteArrayInputStream(str.getBytes());
117126
byte[] bytes = parser.readChunk(input);

0 commit comments

Comments
 (0)