if (!isValidCommand(cmd) || isNullOrEmpty(appId) || isNullOrEmpty(originId)) { return response(BAD_REQUEST) .body(MISSING_ERROR_MESSAGE, UTF_8) .build() .stream(); .body(format(INVALID_APP_ID_FORMAT, appId), UTF_8) .build() .stream(); .body(format(INVALID_ORIGIN_ID_FORMAT, originId, appId), UTF_8) .build() .stream(); .header(LOCATION, "/admin/origins/status") .header(CONTENT_LENGTH, 0) .build() .stream();
@Override protected LiveHttpResponse doHandle(LiveHttpRequest request) { try { String jsonContent = jsonSupplier(request).get(); return response(OK) .disableCaching() .addHeader(CONTENT_TYPE, JSON_UTF_8.toString()) .body(jsonContent, UTF_8) .build() .stream(); } catch (Exception e) { return response(INTERNAL_SERVER_ERROR) .body(e.getMessage(), UTF_8) .build() .stream(); } }
@Override public Eventual<LiveHttpResponse> handle(LiveHttpRequest request, HttpInterceptor.Context context) { HttpResponse.Builder responseBuilder = standardResponse.newBuilder() .headers(request.headers()) .header(STUB_ORIGIN_INFO, origin.applicationInfo()); return Eventual.of(Optional.ofNullable(responseBuilder) .map(it -> request.queryParam("status") .map(status -> it.status(httpResponseStatus(status)) .body("Returning requested status (" + status + ")", UTF_8)) .orElse(it)) .map(it -> request.queryParam("length") .map(length -> it.body(generateContent(parseInt(length)), UTF_8)) .orElse(it)) .orElse(responseBuilder) .build() .stream()); }
@DataProvider(name = "emptyBodyResponses") private Object[][] emptyBodyResponses() { return new Object[][]{ {HttpResponse.response() .build()}, {HttpResponse.response() .body(null, UTF_8) .build()}, {HttpResponse.response() .body("", UTF_8) .build()}, {HttpResponse.response() .body(null, UTF_8, true) .build()}, {HttpResponse.response() .body("", UTF_8, true) .build()}, {HttpResponse.response() .body(null, true) .build()}, {HttpResponse.response() .body(new byte[0], true) .build()}, }; }
/** * Aggregates content stream and converts this response to a {@link HttpResponse}. * <p> * Returns a {@link Eventual <HttpResponse>} that eventually produces a * {@link HttpResponse}. The resulting full response object has the same * response line, headers, and content as this response. * <p> * The content stream is aggregated asynchronously. The stream may be connected * to a network socket or some other content producer. Once aggregated, a * HttpResponse object is emitted on the returned {@link Eventual}. * <p> * A sole {@code maxContentBytes} argument is a backstop defence against excessively * long content streams. The {@code maxContentBytes} should be set to a sensible * value according to your application requirements and heap size. When the content * size stream exceeds the {@code maxContentBytes}, a @{link ContentOverflowException} * is emitted on the returned observable. * * @param maxContentBytes maximum expected content size * @return a {@link Eventual} */ public Eventual<HttpResponse> aggregate(int maxContentBytes) { return Eventual.from(body.aggregate(maxContentBytes)) .map(it -> new HttpResponse.Builder(this, decodeAndRelease(it)) .disableValidation() .build() ); }
@Override public Eventual<LiveHttpResponse> intercept(LiveHttpRequest request, Chain chain) { String header = xHcomPluginsHeader(request); final String configPath = environment.pluginConfig(String.class); String pluginsList = environment.configuration().get("plugins.active").get(); LiveHttpRequest newRequest = request.newBuilder() .header(X_HCOM_PLUGINS_HEADER, header) .header(X_HCOM_PLUGINS_CONFIGURATION_PATH, configPath) .header(X_HCOM_PLUGINS_LIST, pluginsList) .header("X-Hcom-Styx-Started", styxStarted) .header("X-Hcom-Styx-Stopped", styxStopped) .build(); Function<ByteBuf, String> byteBufStringFunction = byteBuf -> byteBuf.toString(Charsets.UTF_8); return chain.proceed(newRequest) .flatMap(response -> response.aggregate(1 * 1024 * 1024)) .map(response -> response.newBuilder() .header(X_HCOM_PLUGINS_HEADER, header) .header(X_HCOM_PLUGINS_CONFIGURATION_PATH, configPath) .header(X_HCOM_PLUGINS_LIST, pluginsList) .header("X-Hcom-Styx-Started", styxStarted) .header("X-Hcom-Styx-Stopped", styxStopped) .build()) .map(HttpResponse::stream); }
@Test public void convertsToStreamingHttpResponse() throws Exception { HttpResponse response = response(CREATED) .version(HTTP_1_1) .header("HeaderName", "HeaderValue") .cookies(responseCookie("CookieName", "CookieValue").build()) .body("message content", UTF_8) .build(); LiveHttpResponse streaming = response.stream(); assertThat(streaming.version(), is(HTTP_1_1)); assertThat(streaming.status(), is(CREATED)); assertThat(streaming.headers(), containsInAnyOrder( header("Content-Length", "15"), header("HeaderName", "HeaderValue"), header("Set-Cookie", "CookieName=CookieValue") )); assertThat(streaming.cookies(), contains(responseCookie("CookieName", "CookieValue").build())); StepVerifier.create(streaming.aggregate(0x100000).map(it -> it.bodyAs(UTF_8))) .expectNext("message content") .verifyComplete(); }
/** * Sets the response body. * <p> * This method encodes the content to a byte array using the specified * charset, and sets the Content-Length header *if* the setContentLength * argument is true. * * @param content response body * @param charset charset used for encoding response body * @param setContentLength If true, Content-Length header is set, otherwise it is not set. * @return {@code this} */ public Builder body(String content, Charset charset, boolean setContentLength) { requireNonNull(charset, "Charset is not provided."); String sanitised = content == null ? "" : content; return body(sanitised.getBytes(charset), setContentLength); }
@Override protected LiveHttpResponse doHandle(LiveHttpRequest request) { try { String path = request.path(); if (!path.startsWith(root) || path.contains("..")) { return error(FORBIDDEN); } return new HttpResponse.Builder(OK) .body(resourceBody(path), true) .header(CONTENT_TYPE, mediaTypeOf(path)) .build() .stream(); } catch (FileNotFoundException e) { return error(NOT_FOUND); } catch (IOException e) { return error(INTERNAL_SERVER_ERROR); } }
/** * Sets the response body. * <p> * This method encodes the content to a byte array provided, and * sets the Content-Length header *if* the setContentLength * argument is true. * * @param content response body * @param setContentLength If true, Content-Length header is set, otherwise it is not set. * @return {@code this} */ public Builder body(byte[] content, boolean setContentLength) { this.body = content == null ? new byte[0] : content.clone(); if (setContentLength) { header(CONTENT_LENGTH, this.body.length); } return this; }
/** * Adds cookies into this response by adding "Set-Cookie" headers. * * @param cookies cookies * @return {@code this} */ public Builder addCookies(Collection<ResponseCookie> cookies) { requireNonNull(cookies); if (cookies.isEmpty()) { return this; } removeCookies(cookies.stream().map(ResponseCookie::name).collect(toList())); encode(cookies).forEach(cookie -> addHeader(SET_COOKIE, cookie)); return this; }
private LiveHttpResponse jsonResponse(Object object, boolean prettyPrint) { try { String jsonContent = marshal(object, prettyPrint); return response(OK) .disableCaching() .addHeader(CONTENT_TYPE, JSON_UTF_8.toString()) .body(jsonContent, UTF_8) .build() .stream(); } catch (JsonProcessingException e) { return response(INTERNAL_SERVER_ERROR) .body(e.getMessage(), UTF_8) .build() .stream(); } }
@Override public Eventual<LiveHttpResponse> handle(LiveHttpRequest request, HttpInterceptor.Context context) { try { return resolveFile(request.path()) .map(ResolvedFile::new) .map(resolvedFile -> HttpResponse.response() .addHeader(CONTENT_TYPE, resolvedFile.mediaType) .body(resolvedFile.content, UTF_8) .build() .stream()) .map(Eventual::of) .orElseGet(() -> NOT_FOUND_HANDLER.handle(request, context)); } catch (IOException e) { return Eventual.of(HttpResponse.response(INTERNAL_SERVER_ERROR).build().stream()); } }
private HttpResponse readResponse(HttpURLConnection connection) throws IOException { int status = connection.getResponseCode(); HttpResponse.Builder response = response(HttpResponseStatus.valueOf(status)); try (InputStream stream = getInputStream(connection, status)) { byte[] content = toByteArray(stream); if (content.length > 0) { response.body(content); } connection.getHeaderFields().forEach((key, value) -> { if (!isNullOrEmpty(key)) { response.header(key, JOINER_ON_COMMA.join(value)); } }); } return response.build(); }
/** * Converts this response to a streaming form (HttpResponse). * * Converts this response to a HttpResponse object which represents the HTTP response as a * stream of bytes. * * @return A streaming HttpResponse object. */ public HttpResponse toStreamingResponse() { if (this.body.length == 0) { return new HttpResponse.Builder(this, Observable.empty()).build(); } else { return new HttpResponse.Builder(this, just(Unpooled.copiedBuffer(this.body))).build(); } }
@Override protected LiveHttpResponse doHandle(LiveHttpRequest request) { HttpRequest fullRequest = Mono.from(request.aggregate(0x100000)).block(); String responseBody = format("Response From %s - %s, received content digest: %s", origin.hostAndPortString(), randomUUID(), fullRequest.bodyAs(UTF_8).hashCode()); return HttpResponse.response(OK) .header(CONTENT_TYPE, HTML_UTF_8.toString()) .header(CONTENT_LENGTH, responseBody.getBytes(UTF_8).length) .body(responseBody, UTF_8) .build() .stream(); } }
/** * Sets the request body. * <p> * This method encodes a String content to a byte array using the specified * charset, and sets the Content-Length header accordingly. * * @param content request body * @param charset charset for string encoding * @return {@code this} */ public Builder body(String content, Charset charset) { return body(content, charset, true); }
private DecodedResponse(HttpResponse response, T content) { this.responseBuilder = response.newBuilder().body(NO_BODY); this.content = content; }
@Test public void contentFromByteArraySetsContentLengthIfRequired() { HttpResponse response1 = HttpResponse.response() .body("Response content.".getBytes(UTF_16), true) .build(); assertThat(response1.body(), is("Response content.".getBytes(UTF_16))); assertThat(response1.header("Content-Length"), is(Optional.of("36"))); HttpResponse response2 = HttpResponse.response() .body("Response content.".getBytes(UTF_8), false) .build(); assertThat(response2.body(), is("Response content.".getBytes(UTF_8))); assertThat(response2.header("Content-Length"), is(Optional.empty())); }