/** * Transforms request body. * * @param transformation a Function from ByteStream to ByteStream. * @return a HttpResponhse builder with a transformed message body. */ public Transformer body(Function<ByteStream, ByteStream> transformation) { this.builder.body(requireNonNull(transformation.apply(this.builder.body))); return this; }
@Test public void doesntFireEventsThatNeverOccurred() { Mono<LiveHttpResponse> publisher = Mono.just(response(OK).body(new ByteStream(Flux.just(new Buffer("hey", UTF_8)))).build()); Flux<LiveHttpResponse> listener = ResponseEventListener.from(publisher) .whenCancelled(() -> cancelled.set(true)) .whenResponseError(cause -> responseError.set(cause)) .whenContentError(cause -> contentError.set(cause)) .whenCompleted(() -> completed.set(true)) .apply(); StepVerifier.create(listener) .consumeNextWith(LiveHttpMessage::consume) .then(() -> { assertFalse(cancelled.get()); assertNull(responseError.get()); assertNull(contentError.get()); assertTrue(completed.get()); }) .verifyComplete(); }
@Test public void firesResponseContentError() { Mono<LiveHttpResponse> publisher = Mono.just( response(OK) .body(new ByteStream(Flux.error(new RuntimeException()))) .build()); Publisher<LiveHttpResponse> listener = ResponseEventListener.from(publisher) .whenContentError(cause -> responseError.set(cause)) .apply(); StepVerifier.create(listener) .consumeNextWith(LiveHttpMessage::consume) .verifyComplete(); assertTrue(responseError.get() instanceof RuntimeException); }
@Test public void firesWhenContentCancelled() { EmitterProcessor<Buffer> contentPublisher = EmitterProcessor.create(); Flux<LiveHttpResponse> listener = ResponseEventListener.from( Flux.just(response(OK) .body(new ByteStream(contentPublisher)) .build())) .whenCancelled(() -> cancelled.set(true)) .apply(); StepVerifier.create(listener) .consumeNextWith(response -> StepVerifier.create(response.body()) .then(() -> assertFalse(cancelled.get())) .thenCancel() .verify()) .verifyComplete(); assertTrue(cancelled.get()); }
@Test public void ignoresResponseErrorAfterHeaders() { Flux<LiveHttpResponse> publisher = Flux.just( response(OK) .body(new ByteStream(Flux.just(new Buffer("hey", UTF_8)))) .build()) .concatWith(Flux.error(new RuntimeException())); Publisher<LiveHttpResponse> listener = ResponseEventListener.from(publisher) .whenCancelled(() -> cancelled.set(true)) .whenResponseError(cause -> responseError.set(cause)) .whenContentError(cause -> contentError.set(cause)) .apply(); StepVerifier.create(listener) .consumeNextWith(LiveHttpMessage::consume) .verifyError(); assertFalse(cancelled.get()); assertNull(responseError.get()); assertNull(contentError.get()); }
@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 encodesToFullHttpResponse() { LiveHttpResponse response = response(CREATED) .version(HTTP_1_0) .header("HeaderName", "HeaderValue") .cookies(responseCookie("CookieName", "CookieValue").build()) .body(new ByteStream(Flux.just("foo", "bar").map(it -> new Buffer(copiedBuffer(it, UTF_8))))) .build(); HttpResponse full = Mono.from(response.aggregate(0x1000)).block(); assertThat(full.status(), is(CREATED)); assertThat(full.version(), is(HTTP_1_0)); assertThat(full.headers(), containsInAnyOrder(header("HeaderName", "HeaderValue"), header("Set-Cookie", "CookieName=CookieValue"))); assertThat(full.cookies(), contains(responseCookie("CookieName", "CookieValue").build())); assertThat(full.body(), is(bytes("foobar"))); }
private LiveHttpResponse exceptionToResponse(Throwable exception, LiveHttpRequest request) { HttpResponseStatus status = status(exception instanceof PluginException ? exception.getCause() : exception); String message = status.code() >= 500 ? "Site temporarily unavailable." : status.description(); return responseEnhancer.enhance( LiveHttpResponse .response(status) .body(new ByteStream(Flux.just(new Buffer(message, UTF_8)))) .build() .newBuilder(), request) .header(CONTENT_LENGTH, message.getBytes(UTF_8).length) .build(); }
@Override public Eventual<LiveHttpResponse> handle(LiveHttpRequest request, HttpInterceptor.Context context) { return Eventual.of(response(statusWithCode(status)).body(new ByteStream(Flux.just(new Buffer(text, UTF_8)))).build()); }
@Test public void transformsBody() throws ExecutionException, InterruptedException { Buffer buffer = new Buffer("I'm going to get removed.", UTF_8); LiveHttpResponse response = response(NO_CONTENT) .body(new ByteStream(Flux.just(buffer))) .build(); HttpResponse fullResponse = Mono.from(response.newBuilder() .body(ByteStream::drop) .build() .aggregate(1000)).block(); assertThat(fullResponse.body().length, is(0)); assertThat(buffer.delegate().refCnt(), is(0)); }
@BeforeMethod public void setUp() { publisher = TestPublisher.create(); request = get("/").body(new ByteStream(publisher)).build(); response = response(OK).body(new ByteStream(publisher)).build(); completed = new AtomicReference<>(); }
@Test public void consumesBody() { Buffer buf1 = new Buffer("foo", UTF_8); Buffer buf2 = new Buffer("bar", UTF_8); LiveHttpResponse response = response() .body(new ByteStream(Flux.just(buf1, buf2))) .build(); response.consume(); assertEquals(buf1.delegate().refCnt(), 0); assertEquals(buf2.delegate().refCnt(), 0); }
@Test public void toFullResponseReleasesOriginalRefCountedBuffers() throws ExecutionException, InterruptedException { Buffer content = new Buffer(Unpooled.copiedBuffer("original", UTF_8)); LiveHttpResponse original = LiveHttpResponse.response(OK) .body(new ByteStream(Flux.just(content))) .build(); StepVerifier.create(original.aggregate(100)) .expectNextCount(1) .then(() -> assertThat(content.delegate().refCnt(), is(0))) .verifyComplete(); }
/** * Creates an HTTP response builder with a given status and body. * * @param status response status * @param body response body * @return a new builder */ public static Builder response(HttpResponseStatus status, ByteStream body) { return new Builder(status).body(body); }
private static LiveHttpResponse error(HttpResponseStatus status) { return new LiveHttpResponse.Builder(status) .body(new ByteStream(Flux.just(new Buffer(status.description(), UTF_8)))) .build(); }
@DataProvider(name = "emptyBodyResponses") private Object[][] emptyBodyResponses() { return new Object[][]{ {response().build()}, {response().body(new ByteStream(Flux.empty())).build()}, }; }
private static LiveHttpResponse toStyxResponse(io.netty.handler.codec.http.HttpResponse nettyResponse, Observable<ByteBuf> contentObservable, Origin origin) { try { return toStyxResponse(nettyResponse) .body(new ByteStream(toPublisher(contentObservable.map(Buffers::fromByteBuf)))) .build(); } catch (IllegalArgumentException e) { throw new BadHttpResponseException(origin, e); } }