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

Commit 326b74b

Browse files
pavelbucekGerrit Code Review
authored andcommitted
Merge "JERSEY-3015: SSE is able to parse CRLFCRLF chunks."
2 parents e307513 + 120f4ca commit 326b74b

File tree

4 files changed

+366
-13
lines changed

4 files changed

+366
-13
lines changed

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

Lines changed: 134 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,11 @@
4646
import java.io.InputStream;
4747
import java.lang.annotation.Annotation;
4848
import java.lang.reflect.Type;
49+
import java.util.ArrayList;
4950
import java.util.Arrays;
5051
import java.util.Collections;
52+
import java.util.Comparator;
53+
import java.util.List;
5154
import java.util.concurrent.atomic.AtomicBoolean;
5255
import java.util.logging.Level;
5356
import java.util.logging.Logger;
@@ -107,34 +110,41 @@ public static ChunkParser createParser(final byte[] boundary) {
107110
return new FixedBoundaryParser(boundary);
108111
}
109112

110-
private static class FixedBoundaryParser implements ChunkParser {
111-
112-
private final byte[] delimiter;
113+
/**
114+
* Create a new chunk multi-parser that will split the response entity input stream
115+
* based on multiple fixed boundary strings.
116+
*
117+
* @param boundaries chunk boundaries.
118+
* @return new fixed boundary string-based chunk parser.
119+
*/
120+
public static ChunkParser createMultiParser(final String... boundaries) {
121+
return new FixedMultiBoundaryParser(boundaries);
122+
}
113123

114-
public FixedBoundaryParser(final byte[] boundary) {
115-
delimiter = Arrays.copyOf(boundary, boundary.length);
116-
}
124+
private abstract static class AbstractBoundaryParser implements ChunkParser {
117125

118126
@Override
119127
public byte[] readChunk(final InputStream in) throws IOException {
120128
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
121-
byte[] delimiterBuffer = new byte[delimiter.length];
129+
byte[] delimiterBuffer = new byte[getDelimiterBufferSize()];
122130

123131
int data;
124132
int dPos;
125133
do {
126134
dPos = 0;
127135
while ((data = in.read()) != -1) {
128136
final byte b = (byte) data;
137+
byte[] delimiter = getDelimiter(b, dPos, delimiterBuffer);
129138

130139
// last read byte is part of the chunk delimiter
131-
if (b == delimiter[dPos]) {
140+
if (delimiter != null && b == delimiter[dPos]) {
132141
delimiterBuffer[dPos++] = b;
133142
if (dPos == delimiter.length) {
134143
// found chunk delimiter
135144
break;
136145
}
137146
} else if (dPos > 0) {
147+
delimiter = getDelimiter(dPos - 1, delimiterBuffer);
138148
delimiterBuffer[dPos] = b;
139149

140150
int matched = matchTail(delimiterBuffer, 1, dPos, delimiter);
@@ -159,14 +169,44 @@ public byte[] readChunk(final InputStream in) throws IOException {
159169

160170
} while (data != -1 && buffer.size() == 0); // skip an empty chunk
161171

162-
if (dPos > 0 && dPos != delimiter.length) {
172+
if (dPos > 0 && dPos != getDelimiter(dPos - 1, delimiterBuffer).length) {
163173
// flush the delimiter buffer, if not empty - parsing finished in the middle of a potential delimiter sequence
164174
buffer.write(delimiterBuffer, 0, dPos);
165175
}
166176

167177
return (buffer.size() > 0) ? buffer.toByteArray() : null;
168178
}
169179

180+
/**
181+
* Selects a delimiter which corresponds to delimiter buffer. Method automatically appends {@code b} param on the
182+
* {@code pos} position of {@code delimiterBuffer} array and then starts the selection process with a newly created array.
183+
*
184+
* @param b byte which will be added on the {@code pos} position of {@code delimiterBuffer} array
185+
* @param pos number of bytes from the delimiter buffer which will be used in processing
186+
* @param delimiterBuffer current content of the delimiter buffer
187+
* @return delimiter which corresponds to delimiterBuffer
188+
*/
189+
abstract byte[] getDelimiter(byte b, int pos, byte[] delimiterBuffer);
190+
191+
/**
192+
* Selects a delimiter which corresponds to delimiter buffer.
193+
*
194+
* @param pos position of the last read byte
195+
* @param delimiterBuffer number of bytes from the delimiter buffer which will be used in processing
196+
* @return delimiter which corresponds to delimiterBuffer
197+
*/
198+
abstract byte[] getDelimiter(int pos, byte[] delimiterBuffer);
199+
200+
/**
201+
* Returns a delimiter buffer size depending on the selected strategy.
202+
* <p>
203+
* If a strategy has multiple registered delimiters, then the delimiter buffer should be a length of the longest
204+
* delimiter.
205+
*
206+
* @return length of the delimiter buffer
207+
*/
208+
abstract int getDelimiterBufferSize();
209+
170210
/**
171211
* Tries to find an element intersection between two arrays in a way that intersecting elements must be
172212
* at the tail of the first array and at the beginning of the second array.
@@ -192,6 +232,10 @@ public byte[] readChunk(final InputStream in) throws IOException {
192232
* any part of the head of the pattern, otherwise returns number of overlapping elements.
193233
*/
194234
private static int matchTail(byte[] buffer, int offset, int length, byte[] pattern) {
235+
if (pattern == null) {
236+
return 0;
237+
}
238+
195239
outer:
196240
for (int i = 0; i < length; i++) {
197241
final int tailLength = length - i;
@@ -209,6 +253,87 @@ private static int matchTail(byte[] buffer, int offset, int length, byte[] patte
209253
}
210254
}
211255

256+
private static class FixedBoundaryParser extends AbstractBoundaryParser {
257+
258+
private final byte[] delimiter;
259+
260+
public FixedBoundaryParser(final byte[] boundary) {
261+
delimiter = Arrays.copyOf(boundary, boundary.length);
262+
}
263+
264+
@Override
265+
byte[] getDelimiter(byte b, int pos, byte[] delimiterBuffer) {
266+
return delimiter;
267+
}
268+
269+
@Override
270+
byte[] getDelimiter(int pos, byte[] delimiterBuffer) {
271+
return delimiter;
272+
}
273+
274+
@Override
275+
int getDelimiterBufferSize() {
276+
return delimiter.length;
277+
}
278+
}
279+
280+
private static class FixedMultiBoundaryParser extends AbstractBoundaryParser {
281+
282+
private final List<byte[]> delimiters = new ArrayList<byte[]>();
283+
284+
private final int longestDelimiterLength;
285+
286+
public FixedMultiBoundaryParser(String... boundaries) {
287+
for (String boundary: boundaries) {
288+
byte[] boundaryBytes = boundary.getBytes();
289+
delimiters.add(Arrays.copyOf(boundaryBytes, boundaryBytes.length));
290+
}
291+
292+
Collections.sort(delimiters, new Comparator<byte[]>() {
293+
@Override
294+
public int compare(byte[] o1, byte[] o2) {
295+
return Integer.compare(o1.length, o2.length);
296+
}
297+
});
298+
299+
byte[] longestDelimiter = delimiters.get(delimiters.size() - 1);
300+
this.longestDelimiterLength = longestDelimiter.length;
301+
}
302+
303+
@Override
304+
byte[] getDelimiter(byte b, int pos, byte[] delimiterBuffer) {
305+
byte[] buffer = Arrays.copyOf(delimiterBuffer, delimiterBuffer.length);
306+
buffer[pos] = b;
307+
308+
return getDelimiter(pos, buffer);
309+
}
310+
311+
@Override
312+
byte[] getDelimiter(int pos, byte[] delimiterBuffer) {
313+
outer:
314+
for (byte[] delimiter: delimiters) {
315+
if (pos > delimiter.length) {
316+
continue;
317+
}
318+
319+
for (int i = 0; i <= pos && i < delimiter.length; i++) {
320+
if (delimiter[i] != delimiterBuffer[i]) {
321+
continue outer;
322+
} else if (pos == i) {
323+
return delimiter;
324+
}
325+
}
326+
}
327+
328+
return null;
329+
}
330+
331+
@Override
332+
int getDelimiterBufferSize() {
333+
return this.longestDelimiterLength;
334+
}
335+
}
336+
212337
/**
213338
* Package-private constructor used by the {@link ChunkedInputReader}.
214339
*

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
*
5353
* @author Petr Bouda (petr.bouda at oracle.com)
5454
**/
55-
public class ChunkedInputTest {
55+
public class FixedBoundaryParserTest {
5656

5757
public static final String DELIMITER_4 = "1234";
5858

0 commit comments

Comments
 (0)