/** * Run a provided action when a consumer cancels this ByteStream. * * @param action the action runnable * @return a new ByteStream with the action attached */ public ByteStream doOnCancel(Runnable action) { return new ByteStream(Flux.from(this.stream).doOnCancel(action)); }
@Test public void contentAggregationOverflow() throws ExecutionException, InterruptedException { ByteStream stream = new ByteStream(Flux.just(buf1, buf2, buf3)); Throwable cause = stream.aggregate(2) .handle((result, throwable) -> throwable) .get(); assertThat(cause, instanceOf(ContentOverflowException.class)); }
@Test public void concatenatesEmptyStreams() { ByteStream stream = new ByteStream(Flux.empty()) .concat(new ByteStream(Flux.just(buf3))) .concat(new ByteStream(Flux.empty())); StepVerifier.create(stream) .expectNext(buf3) .verifyComplete(); }
/** * Replaces this {@link ByteStream} with a new {@link ByteStream}. * * Consumes this stream by safely disposing each {@link Buffer} object * and then emits {@link Buffer} objects from the provided * {@code byteStream}. * * @param byteStream a replacement byte stream. * @return a {@link ByteStream} object. */ public ByteStream replaceWith(ByteStream byteStream) { return this.drop().concat(byteStream); }
/** * Transform the stream by performing a mapping operation on each {@link Buffer} object. * * The mapping operation automatically maintains the @{link Buffer} reference counts as * follows: * * <ul> * <li> When the mapping function returns a new {@link Buffer} instance, the reference count for * the old one is automatically decremented.</li> * <li> When the mapping function modifies the {@link Buffer} in place, returning the same instance * back, the reference count is unchanged.</li> * </ul> * * @param mapping a mapping function * * @return a new, mapped {@code ByteStream} object */ public ByteStream map(Function<Buffer, Buffer> mapping) { return new ByteStream(Flux.from(stream).map(releaseOldBuffers(mapping))); }
/** * Consume the message by discarding the message body. * <p> * This method reads the entire message body from the networks and black holes * all the traffic. This has the benefit of keeping the underlying TCP connection * open for connection pooling. * <p> */ default void consume() { body().drop().aggregate(1) .thenApply(buffer -> { buffer.delegate().release(); return null; }); }
@Test public void mapsContent() { ByteStream stream = new ByteStream(Flux.just(buf1, buf2, buf3)); ByteStream mapped = stream.map(this::toUpperCase); StepVerifier.create(Flux.from(mapped).map(this::decodeUtf8String)) .expectSubscription() .expectNext("A", "B", "C") .verifyComplete(); }
@Test public void deliversAtEndOfStreamNotification() { AtomicBoolean terminated = new AtomicBoolean(); ByteStream stream = new ByteStream(Flux.just(buf1, buf2)) .doOnEnd(maybeCause -> terminated.set(maybeCause == Optional.<Throwable>empty())); StepVerifier.create(new ByteStream(stream), 0) .thenRequest(1) .expectNext(buf1) .then(() -> assertThat(terminated.get(), is(false))) .thenRequest(1) .expectNext(buf2) .then(() -> assertThat(terminated.get(), is(true))) .expectComplete() .verify(); }
@Test public void runsOnCancelActionWhenCancelled() { AtomicBoolean cancelled = new AtomicBoolean(); ByteStream stream = new ByteStream(Flux.just(buf1, buf2)) .doOnCancel(() -> cancelled.set(true)); StepVerifier.create(stream) .thenRequest(1) .expectNext(buf1) .then(() -> assertThat(cancelled.get(), is(false))) .thenCancel() .verify(); assertThat(cancelled.get(), is(true)); }
@Test public void discardsContent() { ByteStream stream = new ByteStream(Flux.just(buf1, buf2, buf3)); ByteStream discarded = stream.drop(); StepVerifier.create(discarded) .expectSubscription() .verifyComplete(); assertThat(buf1.delegate().refCnt(), is(0)); assertThat(buf2.delegate().refCnt(), is(0)); assertThat(buf3.delegate().refCnt(), is(0)); }
@Test public void transformerReplacesBody() { Buffer buf1 = new Buffer("chunk 1, ", UTF_8); Buffer buf2 = new Buffer("chunk 2.", UTF_8); LiveHttpResponse response1 = response(NO_CONTENT) .body(new ByteStream(Flux.just(buf1, buf2))) .build() .newBuilder() .body(body -> body.replaceWith(ByteStream.from("replacement", UTF_8))) .build(); HttpResponse response2 = Mono.from(response1.aggregate(100)).block(); assertEquals(response2.bodyAs(UTF_8), "replacement"); assertEquals(buf1.delegate().refCnt(), 0); assertEquals(buf2.delegate().refCnt(), 0); }
@Test public void replacesStream() { ByteStream stream = new ByteStream(Flux.just(buf1, buf2)) .replaceWith(new ByteStream(Flux.just(buf3))); StepVerifier.create(stream) .expectNext(buf3) .then(() -> { assertEquals(buf1.delegate().refCnt(), 0); assertEquals(buf2.delegate().refCnt(), 0); }) .verifyComplete(); }
public static LiveHttpRequest doFinally(LiveHttpRequest request, Consumer<Optional<Throwable>> action) { return request.newBuilder() .body(it -> it.doOnEnd(action)) .build(); }
private static String bytesToString(ByteStream body) { try { return new String(body.aggregate(100000).get().content(), UTF_8); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e); } }
public static LiveHttpResponse doOnCancel(LiveHttpResponse response, Runnable action) { return response.newBuilder().body(it -> it.doOnCancel(action)).build(); }
/** * Removes message body content from this request. * <p> * Transforms the content {@link ByteStream} to an empty stream such * that all received content events are discarded. * * @return {@code this} */ public Builder removeBody() { this.body = body.drop(); return this; }
@Test public void mapRetainsRefcountsForInlineBufferChanges() { ByteStream stream = new ByteStream(Flux.just(buf1, buf2, buf3)); ByteStream mapped = stream.map(buf -> buf); StepVerifier.create(Flux.from(mapped).map(this::decodeUtf8String)) .expectSubscription() .expectNextCount(3) .verifyComplete(); assertThat(buf1.delegate().refCnt(), is(1)); assertThat(buf2.delegate().refCnt(), is(1)); assertThat(buf3.delegate().refCnt(), is(1)); }
@Test public void deliversAtEndOfStreamNotificationWhenTerminated() { AtomicBoolean terminated = new AtomicBoolean(); ByteStream stream = new ByteStream(Flux.just(buf1, buf2).concatWith(Flux.error(new RuntimeException("bang!")))) .doOnEnd(maybeCause -> terminated.set(maybeCause.isPresent())); StepVerifier.create(new ByteStream(stream), 0) .thenRequest(1) .expectNext(buf1) .then(() -> assertThat(terminated.get(), is(false))) .thenRequest(1) .expectNext(buf2) .then(() -> assertThat(terminated.get(), is(true))) .expectError() .verify(); }
public static LiveHttpResponse doFinally(LiveHttpResponse response, Consumer<Optional<Throwable>> action) { return response.newBuilder() .body(it -> it.doOnEnd(action::accept)) .build(); }
private static String bytesToString(ByteStream body) throws Exception { return new String(body.aggregate(100000).get().content(), UTF_8); }