Skip to content

Streaming

Contents | Previous: Authentication, TLS, and Proxy | Next: Client and Request Lifecycle

After reading this chapter

This chapter processes a response in chunks and sends a request body in parts. Its code comes from each language's HttpClient tutorial.

Streaming moves a large body through a caller callback or provider. Unlike a JSON body, it does not serialize or accumulate the whole body. Response Rules covers status and compression rules.

↗ 크게 보기

The provider and sink are each consumed once while the body moves. This is why the streaming path does not apply replay or transparent decompression.

1. Response Chunks Flow Through a Download Sink

A download sink is a callback invoked for each received body chunk. A raw response carries status, headers, and the body before JSON decoding. The final raw response from a download preserves status and headers; bodies from intermediate redirect responses do not reach the sink.

AtomicInteger chunks = new AtomicInteger();
AtomicInteger bytes = new AtomicInteger();
client.get("/rooms/" + roomId + "/export")
        .download(
                chunk -> {
                    chunks.incrementAndGet();
                    bytes.addAndGet(chunk.length);
                })
        .toCompletableFuture()
        .join();
System.out.println("download stream: chunks " + chunks.get() + " bytes " + bytes.get());

The example counts each export-body chunk and accumulates the total number of chunks and bytes.

2. Request Chunks Come from a Body Stream Provider

A body stream provider returns the next chunk to send and signals completion with the language's empty value. Its configuration includes the content type.

byte[][] lines = {
    "{\"playerId\":\"p2\",\"text\":\"import-1\"}\n".getBytes(StandardCharsets.UTF_8),
    "{\"playerId\":\"p2\",\"text\":\"import-2\"}\n".getBytes(StandardCharsets.UTF_8),
    "{\"playerId\":\"p2\",\"text\":\"import-3\"}\n".getBytes(StandardCharsets.UTF_8),
};
AtomicInteger nextLine = new AtomicInteger();
HttpResponse<Imported> imported =
        client.post("/rooms/" + roomId + "/import")
                .bodyStream(
                        () -> {
                            int index = nextLine.getAndIncrement();
                            return index < lines.length ? lines[index] : null;
                        },
                        "application/x-ndjson")
                .submit(Imported.class)
                .toCompletableFuture()
                .join();
System.out.println("upload stream: imported " + imported.body().imported());

The example supplies three NDJSON lines in sequence and confirms that the server imported three items.

3. Retry and Decompression Are Excluded

A download sink and a body stream provider consume their body once and cannot recreate it. A streaming request is therefore excluded from retry and redirect replay. A download sink receives compressed response bytes unchanged; decompression is not applied.

4. Next Chapter

Client and Request Lifecycle covers client reuse, close timing, response completion, and timeout boundaries.