|
6 | 6 | import static java.net.HttpURLConnection.HTTP_UNSUPPORTED_TYPE; |
7 | 7 | import static java.nio.charset.StandardCharsets.UTF_8; |
8 | 8 | import static org.assertj.core.api.Assertions.assertThat; |
| 9 | +import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; |
9 | 10 |
|
10 | 11 | import com.retailsvc.http.start.TextEchoHandler; |
11 | 12 | import java.io.ByteArrayInputStream; |
|
17 | 18 | import java.net.http.HttpRequest.BodyPublishers; |
18 | 19 | import java.net.http.HttpResponse; |
19 | 20 | import java.net.http.HttpResponse.BodyHandlers; |
| 21 | +import java.time.Duration; |
20 | 22 | import java.util.Map; |
| 23 | +import java.util.concurrent.CountDownLatch; |
| 24 | +import java.util.concurrent.TimeUnit; |
21 | 25 | import java.util.function.UnaryOperator; |
22 | 26 | import java.util.zip.GZIPInputStream; |
23 | 27 | import java.util.zip.GZIPOutputStream; |
@@ -240,6 +244,52 @@ void headKeepsContentLengthWithoutAcceptEncoding() throws Exception { |
240 | 244 | } |
241 | 245 | } |
242 | 246 |
|
| 247 | + @Test |
| 248 | + void flushedEventOfGzippedStreamReachesClientBeforeStreamEnds() throws Exception { |
| 249 | + byte[] first = "{\"event\":1}\n".getBytes(UTF_8); |
| 250 | + byte[] second = "{\"event\":2}\n".getBytes(UTF_8); |
| 251 | + CountDownLatch clientSawFirst = new CountDownLatch(1); |
| 252 | + RequestHandler events = |
| 253 | + req -> |
| 254 | + Response.stream( |
| 255 | + HTTP_OK, |
| 256 | + "application/x-ndjson", |
| 257 | + out -> { |
| 258 | + out.write(first); |
| 259 | + out.flush(); |
| 260 | + try { |
| 261 | + clientSawFirst.await(10, TimeUnit.SECONDS); |
| 262 | + } catch (InterruptedException e) { |
| 263 | + Thread.currentThread().interrupt(); |
| 264 | + throw new IOException(e); |
| 265 | + } |
| 266 | + out.write(second); |
| 267 | + }); |
| 268 | + try (var s = serverWith(Map.of(), builder -> builder.extraRoute("/events", events)); |
| 269 | + var client = httpClient()) { |
| 270 | + var request = |
| 271 | + HttpRequest.newBuilder() |
| 272 | + .uri(URI.create("http://localhost:%d/events".formatted(s.listenPort()))) |
| 273 | + .header(ACCEPT_ENCODING, "gzip") |
| 274 | + .GET() |
| 275 | + .build(); |
| 276 | + |
| 277 | + HttpResponse<InputStream> response = client.send(request, BodyHandlers.ofInputStream()); |
| 278 | + |
| 279 | + assertThat(response.headers().firstValue(CONTENT_ENCODING)).contains("gzip"); |
| 280 | + try (var in = new GZIPInputStream(response.body())) { |
| 281 | + try { |
| 282 | + byte[] got = |
| 283 | + assertTimeoutPreemptively(Duration.ofSeconds(5), () -> in.readNBytes(first.length)); |
| 284 | + assertThat(got).isEqualTo(first); |
| 285 | + } finally { |
| 286 | + clientSawFirst.countDown(); |
| 287 | + } |
| 288 | + assertThat(in.readAllBytes()).isEqualTo(second); |
| 289 | + } |
| 290 | + } |
| 291 | + } |
| 292 | + |
243 | 293 | // -- fixtures -- |
244 | 294 |
|
245 | 295 | private OpenApiServer echoServer() { |
|
0 commit comments