Skip to content

Commit 5f6e72d

Browse files
committed
Issue #5605 - Adding more gzip consume all tests
Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
1 parent 14f94f7 commit 5f6e72d

2 files changed

Lines changed: 243 additions & 7 deletions

File tree

tests/test-integration/src/test/java/org/eclipse/jetty/test/GzipWithSendErrorTest.java

Lines changed: 243 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,43 +20,73 @@
2020

2121
import java.io.ByteArrayOutputStream;
2222
import java.io.IOException;
23+
import java.io.InputStream;
2324
import java.net.URI;
25+
import java.nio.ByteBuffer;
26+
import java.nio.file.Files;
27+
import java.nio.file.Path;
28+
import java.util.concurrent.CountDownLatch;
29+
import java.util.concurrent.TimeUnit;
30+
import java.util.concurrent.atomic.AtomicLong;
31+
import java.util.concurrent.atomic.AtomicReference;
32+
import java.util.zip.GZIPInputStream;
2433
import java.util.zip.GZIPOutputStream;
2534
import javax.servlet.http.HttpServlet;
2635
import javax.servlet.http.HttpServletRequest;
2736
import javax.servlet.http.HttpServletResponse;
2837

2938
import org.eclipse.jetty.client.HttpClient;
3039
import org.eclipse.jetty.client.api.ContentResponse;
40+
import org.eclipse.jetty.client.api.Response;
41+
import org.eclipse.jetty.client.api.Result;
3142
import org.eclipse.jetty.client.util.BytesContentProvider;
43+
import org.eclipse.jetty.client.util.DeferredContentProvider;
3244
import org.eclipse.jetty.http.HttpHeader;
3345
import org.eclipse.jetty.http.HttpMethod;
46+
import org.eclipse.jetty.server.HttpChannel;
47+
import org.eclipse.jetty.server.HttpConnection;
48+
import org.eclipse.jetty.server.HttpInput;
49+
import org.eclipse.jetty.server.Request;
3450
import org.eclipse.jetty.server.Server;
3551
import org.eclipse.jetty.server.ServerConnector;
3652
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
3753
import org.eclipse.jetty.servlet.ServletContextHandler;
54+
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
3855
import org.eclipse.jetty.util.IO;
3956
import org.eclipse.jetty.util.component.LifeCycle;
4057
import org.junit.jupiter.api.AfterEach;
4158
import org.junit.jupiter.api.BeforeEach;
4259
import org.junit.jupiter.api.Test;
60+
import org.junit.jupiter.params.ParameterizedTest;
61+
import org.junit.jupiter.params.provider.ValueSource;
4362

4463
import static java.nio.charset.StandardCharsets.UTF_8;
4564
import static org.hamcrest.MatcherAssert.assertThat;
4665
import static org.hamcrest.Matchers.containsString;
66+
import static org.hamcrest.Matchers.greaterThan;
67+
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
68+
import static org.hamcrest.Matchers.is;
69+
import static org.hamcrest.Matchers.lessThanOrEqualTo;
4770
import static org.junit.jupiter.api.Assertions.assertEquals;
71+
import static org.junit.jupiter.api.Assertions.assertNotEquals;
72+
import static org.junit.jupiter.api.Assertions.assertTrue;
4873

4974
public class GzipWithSendErrorTest
5075
{
5176
private Server server;
5277
private HttpClient client;
78+
private ServerConnector connector;
79+
80+
private static void onComplete(Result result)
81+
{
82+
}
5383

5484
@BeforeEach
5585
public void setup() throws Exception
5686
{
5787
server = new Server();
5888

59-
ServerConnector connector = new ServerConnector(server);
89+
connector = new ServerConnector(server);
6090
connector.setPort(0);
6191
server.addConnector(connector);
6292

@@ -100,7 +130,6 @@ public void testGzipNormalErrorNormal() throws Exception
100130
response = client.newRequest(serverURI.resolve("/submit"))
101131
.method(HttpMethod.POST)
102132
.header(HttpHeader.CONTENT_ENCODING, "gzip")
103-
.header(HttpHeader.ACCEPT_ENCODING, "gzip")
104133
.content(new BytesContentProvider("text/plain", compressed("normal-A")))
105134
.send();
106135

@@ -110,7 +139,6 @@ public void testGzipNormalErrorNormal() throws Exception
110139
response = client.newRequest(serverURI.resolve("/fail"))
111140
.method(HttpMethod.POST)
112141
.header(HttpHeader.CONTENT_ENCODING, "gzip")
113-
.header(HttpHeader.ACCEPT_ENCODING, "gzip")
114142
.content(new BytesContentProvider("text/plain", compressed("normal-B")))
115143
.send();
116144

@@ -120,7 +148,6 @@ public void testGzipNormalErrorNormal() throws Exception
120148
response = client.newRequest(serverURI.resolve("/submit"))
121149
.method(HttpMethod.POST)
122150
.header(HttpHeader.CONTENT_ENCODING, "gzip")
123-
.header(HttpHeader.ACCEPT_ENCODING, "gzip")
124151
.content(new BytesContentProvider("text/plain", compressed("normal-C")))
125152
.send();
126153

@@ -139,15 +166,219 @@ private byte[] compressed(String content) throws IOException
139166
}
140167
}
141168

169+
/**
170+
* Make request with compressed content.
171+
* <p>
172+
* Request contains (roughly) 1 MB of request network data.
173+
* Which unpacks to 1 GB of zeros.
174+
* </p>
175+
* <p>
176+
* This test is to ensure that consumeAll only reads the network data,
177+
* and doesn't process it through the interceptors.
178+
* </p>
179+
*/
180+
@Test
181+
public void testGzipConsumeAllContentLengthBlocking() throws Exception
182+
{
183+
URI serverURI = server.getURI();
184+
185+
CountDownLatch serverRequestCompleteLatch = new CountDownLatch(1);
186+
// count of bytes against network read
187+
AtomicLong inputBytesIn = new AtomicLong(0L);
188+
AtomicLong inputContentReceived = new AtomicLong(0L);
189+
// count of bytes against API read
190+
AtomicLong inputContentConsumed = new AtomicLong(0L);
191+
192+
connector.addBean(new HttpChannel.Listener()
193+
{
194+
@Override
195+
public void onComplete(Request request)
196+
{
197+
HttpConnection connection = (HttpConnection)request.getHttpChannel().getConnection();
198+
HttpInput httpInput = request.getHttpInput();
199+
inputContentConsumed.set(httpInput.getContentConsumed());
200+
inputContentReceived.set(httpInput.getContentReceived());
201+
inputBytesIn.set(connection.getBytesIn());
202+
serverRequestCompleteLatch.countDown();
203+
}
204+
});
205+
206+
// This is a doubly-compressed (with gzip) test resource.
207+
// There's no point putting into SCM the full 1MB file, when the
208+
// 3KB version is adequate.
209+
Path zerosCompressed = MavenTestingUtils.getTestResourcePathFile("zeros.gz.gz");
210+
byte[] compressedRequest;
211+
try (InputStream in = Files.newInputStream(zerosCompressed);
212+
GZIPInputStream gzipIn = new GZIPInputStream(in);
213+
ByteArrayOutputStream out = new ByteArrayOutputStream())
214+
{
215+
IO.copy(gzipIn, out);
216+
compressedRequest = out.toByteArray();
217+
}
218+
219+
int sizeActuallySent = compressedRequest.length / 2;
220+
ByteBuffer start = ByteBuffer.wrap(compressedRequest, 0, sizeActuallySent);
221+
DeferredContentProvider contentProvider = new DeferredContentProvider(start)
222+
{
223+
@Override
224+
public long getLength()
225+
{
226+
return compressedRequest.length;
227+
}
228+
};
229+
AtomicReference<Response> clientResponseRef = new AtomicReference<>();
230+
CountDownLatch clientResponseSuccessLatch = new CountDownLatch(1);
231+
CountDownLatch clientResultComplete = new CountDownLatch(1);
232+
233+
client.newRequest(serverURI.resolve("/fail"))
234+
.method(HttpMethod.POST)
235+
.header(HttpHeader.CONTENT_TYPE, "application/octet-stream")
236+
.header(HttpHeader.CONTENT_ENCODING, "gzip")
237+
.content(contentProvider)
238+
.onResponseSuccess((response) ->
239+
{
240+
clientResponseRef.set(response);
241+
clientResponseSuccessLatch.countDown();
242+
})
243+
.send((result) -> clientResultComplete.countDown());
244+
245+
assertTrue(clientResponseSuccessLatch.await(5, TimeUnit.SECONDS), "Result not received");
246+
Response response = clientResponseRef.get();
247+
assertEquals(400, response.getStatus(), "Response status on /fail");
248+
249+
assertEquals("close", response.getHeaders().get(HttpHeader.CONNECTION), "Response Connection header");
250+
251+
// Await for server side to complete the request
252+
assertTrue(serverRequestCompleteLatch.await(5, TimeUnit.SECONDS), "Request complete never occurred?");
253+
254+
// System.out.printf("Input Content Consumed: %,d%n", inputContentConsumed.get());
255+
// System.out.printf("Input Content Received: %,d%n", inputContentReceived.get());
256+
// System.out.printf("Input BytesIn Count: %,d%n", inputBytesIn.get());
257+
258+
// Servlet didn't read body content
259+
assertThat("Request Input Content Consumed not have been used", inputContentConsumed.get(), is(0L));
260+
// Network reads
261+
assertThat("Request Input Content Received should have seen content", inputContentReceived.get(), greaterThan(0L));
262+
assertThat("Request Input Content Received less then initial buffer", inputContentReceived.get(), lessThanOrEqualTo((long)sizeActuallySent));
263+
assertThat("Request Connection BytesIn should have some minimal data", inputBytesIn.get(), greaterThanOrEqualTo(1024L));
264+
assertThat("Request Connection BytesIn read should not have read all of the data", inputBytesIn.get(), lessThanOrEqualTo((long)sizeActuallySent));
265+
266+
// Now provide rest
267+
contentProvider.offer(ByteBuffer.wrap(compressedRequest, sizeActuallySent, compressedRequest.length - sizeActuallySent));
268+
contentProvider.close();
269+
270+
assertTrue(clientResultComplete.await(5, TimeUnit.SECONDS));
271+
}
272+
273+
/**
274+
* Make request with compressed content.
275+
* <p>
276+
* Request contains (roughly) 1 MB of request network data.
277+
* Which unpacks to 1 GB of zeros.
278+
* </p>
279+
* <p>
280+
* This test is to ensure that consumeAll only reads the network data,
281+
* and doesn't process it through the interceptors.
282+
* </p>
283+
*/
284+
@ParameterizedTest
285+
@ValueSource(booleans = {true, false})
286+
public void testGzipConsumeAllChunkedBlockingOnLastBuffer(boolean read) throws Exception
287+
{
288+
URI serverURI = server.getURI();
289+
290+
CountDownLatch serverRequestCompleteLatch = new CountDownLatch(1);
291+
// count of bytes against network read
292+
AtomicLong inputBytesIn = new AtomicLong(0L);
293+
AtomicLong inputContentReceived = new AtomicLong(0L);
294+
// count of bytes against API read
295+
AtomicLong inputContentConsumed = new AtomicLong(0L);
296+
297+
connector.addBean(new HttpChannel.Listener()
298+
{
299+
@Override
300+
public void onComplete(Request request)
301+
{
302+
HttpConnection connection = (HttpConnection)request.getHttpChannel().getConnection();
303+
HttpInput httpInput = request.getHttpInput();
304+
inputContentConsumed.set(httpInput.getContentConsumed());
305+
inputContentReceived.set(httpInput.getContentReceived());
306+
inputBytesIn.set(connection.getBytesIn());
307+
serverRequestCompleteLatch.countDown();
308+
}
309+
});
310+
311+
// This is a doubly-compressed (with gzip) test resource.
312+
// There's no point putting into SCM the full 1MB file, when the
313+
// 3KB version is adequate.
314+
Path zerosCompressed = MavenTestingUtils.getTestResourcePathFile("zeros.gz.gz");
315+
byte[] compressedRequest;
316+
try (InputStream in = Files.newInputStream(zerosCompressed);
317+
GZIPInputStream gzipIn = new GZIPInputStream(in);
318+
ByteArrayOutputStream out = new ByteArrayOutputStream())
319+
{
320+
IO.copy(gzipIn, out);
321+
compressedRequest = out.toByteArray();
322+
}
323+
324+
int sizeActuallySent = compressedRequest.length / 2;
325+
ByteBuffer start = ByteBuffer.wrap(compressedRequest, 0, sizeActuallySent);
326+
DeferredContentProvider contentProvider = new DeferredContentProvider(start);
327+
AtomicReference<Response> clientResponseRef = new AtomicReference<>();
328+
CountDownLatch clientResponseSuccessLatch = new CountDownLatch(1);
329+
CountDownLatch clientResultComplete = new CountDownLatch(1);
330+
331+
URI uri = serverURI.resolve("/fail?read=" + read);
332+
333+
client.newRequest(uri)
334+
.method(HttpMethod.POST)
335+
.header(HttpHeader.CONTENT_TYPE, "application/octet-stream")
336+
.header(HttpHeader.CONTENT_ENCODING, "gzip")
337+
.content(contentProvider)
338+
.onResponseSuccess((response) ->
339+
{
340+
clientResponseRef.set(response);
341+
clientResponseSuccessLatch.countDown();
342+
})
343+
.send((result) -> clientResultComplete.countDown());
344+
345+
assertTrue(clientResponseSuccessLatch.await(5, TimeUnit.SECONDS), "Result not received");
346+
Response response = clientResponseRef.get();
347+
assertEquals(400, response.getStatus(), "Response status on /fail");
348+
349+
assertEquals("close", response.getHeaders().get(HttpHeader.CONNECTION), "Response Connection header");
350+
351+
// Await for server side to complete the request
352+
assertTrue(serverRequestCompleteLatch.await(5, TimeUnit.SECONDS), "Request complete never occurred?");
353+
354+
// System.out.printf("Input Content Consumed: %,d%n", inputContentConsumed.get());
355+
// System.out.printf("Input Content Received: %,d%n", inputContentReceived.get());
356+
// System.out.printf("Input BytesIn Count: %,d%n", inputBytesIn.get());
357+
358+
long readCount = read ? 1L : 0L;
359+
360+
// Servlet read of body content
361+
assertThat("Request Input Content Consumed not have been used", inputContentConsumed.get(), is(readCount));
362+
// Network reads
363+
assertThat("Request Input Content Received should have seen content", inputContentReceived.get(), greaterThan(0L));
364+
assertThat("Request Input Content Received less then initial buffer", inputContentReceived.get(), lessThanOrEqualTo((long)sizeActuallySent));
365+
assertThat("Request Connection BytesIn should have some minimal data", inputBytesIn.get(), greaterThanOrEqualTo(1024L));
366+
assertThat("Request Connection BytesIn read should not have read all of the data", inputBytesIn.get(), lessThanOrEqualTo((long)sizeActuallySent));
367+
368+
// Now provide rest
369+
contentProvider.offer(ByteBuffer.wrap(compressedRequest, sizeActuallySent, compressedRequest.length - sizeActuallySent));
370+
contentProvider.close();
371+
372+
assertTrue(clientResultComplete.await(5, TimeUnit.SECONDS));
373+
}
374+
142375
public static class PostServlet extends HttpServlet
143376
{
144377
@Override
145378
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException
146379
{
147380
resp.setCharacterEncoding("utf-8");
148381
resp.setContentType("text/plain");
149-
resp.setHeader("X-Servlet", req.getServletPath());
150-
151382
String reqBody = IO.toString(req.getInputStream(), UTF_8);
152383
resp.getWriter().append(reqBody);
153384
}
@@ -158,7 +389,12 @@ public static class FailServlet extends HttpServlet
158389
@Override
159390
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException
160391
{
161-
resp.setHeader("X-Servlet", req.getServletPath());
392+
boolean read = Boolean.parseBoolean(req.getParameter("read"));
393+
if (read)
394+
{
395+
int val = req.getInputStream().read();
396+
assertNotEquals(-1, val);
397+
}
162398
// intentionally do not read request body here.
163399
resp.sendError(400);
164400
}
2.71 KB
Binary file not shown.

0 commit comments

Comments
 (0)