下面列出了io.grpc.InternalStatus#com.linecorp.armeria.common.ResponseHeaders 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@Override
public HttpResponse convertResponse(ServiceRequestContext ctx, ResponseHeaders headers,
@Nullable Object resObj,
HttpHeaders trailingHeaders) throws Exception {
try {
final ResponseHeadersBuilder builder = headers.toBuilder();
if (builder.contentType() == null) {
builder.contentType(MediaType.JSON_UTF_8);
}
final JsonNode jsonNode = Jackson.valueToTree(resObj);
if (builder.get(HttpHeaderNames.LOCATION) == null) {
final String url = jsonNode.get("url").asText();
// Remove the url field and send it with the LOCATION header.
((ObjectNode) jsonNode).remove("url");
builder.add(HttpHeaderNames.LOCATION, url);
}
return HttpResponse.of(builder.build(), HttpData.wrap(Jackson.writeValueAsBytes(jsonNode)),
trailingHeaders);
} catch (JsonProcessingException e) {
logger.debug("Failed to convert a response:", e);
return HttpApiUtil.newResponse(ctx, HttpStatus.INTERNAL_SERVER_ERROR, e);
}
}
@Test
void doNotEncodeWhenContentShouldBeEmpty() {
final ResponseHeaders headers = ResponseHeaders.builder(HttpStatus.NO_CONTENT).contentType(
MediaType.PLAIN_TEXT_UTF_8).build();
// Add CONTINUE not to validate when creating HttpResponse.
final HttpResponse orig = HttpResponse.of(ResponseHeaders.of(HttpStatus.CONTINUE), headers,
HttpData.ofUtf8("foo"));
final HttpEncodedResponse encoded = new HttpEncodedResponse(
orig, HttpEncodingType.DEFLATE, mediaType -> true, 1);
StepVerifier.create(encoded)
.expectNext(ResponseHeaders.of(HttpStatus.CONTINUE))
.expectNext(headers)
.expectNext(HttpData.ofUtf8("foo"))
.expectComplete()
.verify();
}
/**
* POST /tokens
*
* <p>Returns a newly-generated token belonging to the current login user.
*/
@Post("/tokens")
@StatusCode(201)
@ResponseConverter(CreateApiResponseConverter.class)
public CompletableFuture<HttpResult<Token>> createToken(@Param String appId,
@Param boolean isAdmin,
Author author, User loginUser) {
checkArgument(!isAdmin || loginUser.isAdmin(),
"Only administrators are allowed to create an admin-level token.");
return mds.createToken(author, appId, isAdmin)
.thenCompose(unused -> mds.findTokenByAppId(appId))
.thenApply(token -> {
final ResponseHeaders headers = ResponseHeaders.of(HttpStatus.CREATED,
HttpHeaderNames.LOCATION,
"/tokens/" + appId);
return HttpResult.of(headers, token);
});
}
@Test
void updateUsingPatch() {
final WebClient client = WebClient.of(server.httpUri());
// Make unhealthy.
final AggregatedHttpResponse res1 = client.execute(
RequestHeaders.of(HttpMethod.PATCH, "/hc_updatable"),
"[{\"op\":\"replace\",\"path\":\"/healthy\",\"value\":false}]").aggregate().join();
assertThat(res1).isEqualTo(AggregatedHttpResponse.of(
ResponseHeaders.of(HttpStatus.SERVICE_UNAVAILABLE,
HttpHeaderNames.CONTENT_TYPE, MediaType.JSON_UTF_8,
"armeria-lphc", "60, 5"),
HttpData.ofUtf8("{\"healthy\":false}")));
// Make healthy.
final AggregatedHttpResponse res2 = client.execute(
RequestHeaders.of(HttpMethod.PATCH, "/hc_updatable"),
"[{\"op\":\"replace\",\"path\":\"/healthy\",\"value\":true}]").aggregate().join();
assertThat(res2).isEqualTo(AggregatedHttpResponse.of(
ResponseHeaders.of(HttpStatus.OK,
HttpHeaderNames.CONTENT_TYPE, MediaType.JSON_UTF_8,
"armeria-lphc", "60, 5"),
HttpData.ofUtf8("{\"healthy\":true}")));
}
@Test
void waitUntilHealthy() throws Exception {
// Make the server unhealthy.
checker.setHealthy(false);
final CompletableFuture<AggregatedHttpResponse> f = sendLongPollingGet("unhealthy");
// Should not wake up until the server becomes unhealthy.
assertThatThrownBy(() -> f.get(1, TimeUnit.SECONDS))
.isInstanceOf(TimeoutException.class);
// Make the server healthy so the response comes in.
checker.setHealthy(true);
assertThat(f.get()).isEqualTo(AggregatedHttpResponse.of(
ImmutableList.of(ResponseHeaders.builder(HttpStatus.PROCESSING)
.set("armeria-lphc", "60, 5")
.build()),
ResponseHeaders.of(HttpStatus.OK,
HttpHeaderNames.CONTENT_TYPE, MediaType.JSON_UTF_8,
"armeria-lphc", "60, 5"),
HttpData.ofUtf8("{\"healthy\":true}"),
HttpHeaders.of()));
}
@Override
public ChannelFuture doWriteHeaders(int id, int streamId, ResponseHeaders headers, boolean endStream,
boolean isTrailersEmpty) {
if (!isWritable(id)) {
return newClosedSessionFuture();
}
final HttpResponse converted = convertHeaders(headers, endStream, isTrailersEmpty);
if (headers.status().isInformational()) {
return write(id, converted, false);
}
if (keepAliveHandler != null && keepAliveHandler.isMaxConnectionAgeExceeded()) {
converted.headers().set(HttpHeaderNames.CONNECTION, "close");
sentConnectionCloseHeader = true;
}
return writeNonInformationalHeaders(id, converted, endStream);
}
private static <T> EurekaHttpResponse<T> convertResponse(HttpResponse response, Class<T> type) {
final AggregatedHttpResponse aggregatedRes = response.aggregate().join();
T t = null;
final ResponseHeaders headers = aggregatedRes.headers();
if (headers.status() == HttpStatus.OK) {
final EntityBodyConverter converter = new EntityBodyConverter();
try {
// noinspection unchecked
t = (T) converter.read(
aggregatedRes.content().toInputStream(), type,
MediaType.valueOf(headers.contentType().toString()));
} catch (IOException e) {
throw new RuntimeException("Unexpected exception while converting response body:", e);
}
}
return anEurekaHttpResponse(aggregatedRes.status().code(), t)
.headers(headersOf(headers))
.build();
}
@Test
void deleteFile() throws IOException {
final WebClient client = dogma.httpClient();
addFooJson(client);
addBarTxt(client);
final String body =
'{' +
" \"path\": \"/foo.json\"," +
" \"type\": \"REMOVE\"," +
" \"commitMessage\" : {" +
" \"summary\" : \"Delete foo.json\"" +
" }" +
'}';
final RequestHeaders headers = RequestHeaders.of(HttpMethod.POST, CONTENTS_PREFIX,
HttpHeaderNames.CONTENT_TYPE, MediaType.JSON);
final AggregatedHttpResponse res1 = client.execute(headers, body).aggregate().join();
assertThat(ResponseHeaders.of(res1.headers()).status()).isEqualTo(HttpStatus.OK);
final AggregatedHttpResponse res2 = client.get(CONTENTS_PREFIX + "/**").aggregate().join();
// /a directory and /a/bar.txt file are left
assertThat(Jackson.readTree(res2.contentUtf8()).size()).isEqualTo(2);
}
@Test
void infoLevel() throws Exception {
final ServiceRequestContext ctx = ServiceRequestContext.of(HttpRequest.of(HttpMethod.GET, "/"));
ctx.logBuilder().responseHeaders(ResponseHeaders.of(200));
final Logger logger = LoggingTestUtil.newMockLogger(ctx, capturedCause);
when(logger.isInfoEnabled()).thenReturn(true);
final LoggingService service =
LoggingService.builder()
.logger(logger)
.requestLogLevel(LogLevel.INFO)
.successfulResponseLogLevel(LogLevel.INFO)
.newDecorator().apply(delegate);
service.serve(ctx, ctx.request());
verify(logger).info(eq(REQUEST_FORMAT), same(ctx),
matches(".*headers=\\[:method=GET, :path=/].*"));
verify(logger).info(eq(RESPONSE_FORMAT), same(ctx),
matches(".*headers=\\[:status=200].*"));
}
@Test
public void getCookies() {
final HttpHeaders httpHeaders = ResponseHeaders.of(HttpStatus.OK,
HttpHeaderNames.of("blahblah"), "armeria",
HttpHeaderNames.SET_COOKIE, "a=1; b=2");
final HttpResponse httpResponse = HttpResponse.of(httpHeaders);
final ArmeriaClientHttpResponse response =
response(new ArmeriaHttpClientResponseSubscriber(httpResponse), httpHeaders);
// HttpResponse would be completed after ResponseHeader is completed, because there's no body.
assertThat(httpResponse.whenComplete().isDone()).isTrue();
assertThat(response.getStatusCode()).isEqualTo(org.springframework.http.HttpStatus.OK);
assertThat(response.getHeaders().getFirst("blahblah")).isEqualTo("armeria");
final ResponseCookie cookie = response.getCookies().getFirst("a");
assertThat(cookie).isNotNull();
assertThat(cookie.getValue()).isEqualTo("1");
}
@Test
public void cancel() throws Exception {
when(armeriaCall.tryFinish()).thenReturn(false);
when(armeriaCall.isCanceled()).thenReturn(false, false, true);
final ManualMockCallback callback = new ManualMockCallback();
final StreamingCallSubscriber subscriber = new StreamingCallSubscriber(
armeriaCall, callback, new Request.Builder().url("http://foo.com").build(),
MoreExecutors.directExecutor());
subscriber.onSubscribe(subscription);
subscriber.onNext(ResponseHeaders.of(200));
subscriber.onNext(HttpData.ofUtf8("{\"name\":\"foo\"}"));
subscriber.onComplete();
verify(subscription, times(2)).request(1L);
await().untilAsserted(() -> assertThat(callback.callbackCallingCount).isEqualTo(1));
await().untilAsserted(() -> assertThat(callback.exception.getMessage()).isEqualTo("cancelled"));
}
@Test
void onStatus() {
final Backoff backoff500 = Backoff.fixed(1000);
final Backoff backoff502 = Backoff.fixed(1000);
final RetryRule rule =
RetryRule.builder()
.onStatus(HttpStatus.INTERNAL_SERVER_ERROR)
.thenBackoff(backoff500)
.orElse(RetryRule.builder()
.onStatus((unused, status) -> HttpStatus.BAD_GATEWAY.equals(status))
.thenBackoff(backoff502));
final ClientRequestContext ctx1 = ClientRequestContext.of(HttpRequest.of(HttpMethod.GET, "/"));
ctx1.logBuilder().responseHeaders(ResponseHeaders.of(HttpStatus.INTERNAL_SERVER_ERROR));
assertBackoff(rule.shouldRetry(ctx1, null)).isSameAs(backoff500);
final ClientRequestContext ctx2 = ClientRequestContext.of(HttpRequest.of(HttpMethod.GET, "/"));
ctx2.logBuilder().responseHeaders(ResponseHeaders.of(HttpStatus.BAD_GATEWAY));
assertBackoff(rule.shouldRetry(ctx2, null)).isSameAs(backoff502);
final ClientRequestContext ctx3 = ClientRequestContext.of(HttpRequest.of(HttpMethod.GET, "/"));
ctx3.logBuilder().responseHeaders(ResponseHeaders.of(HttpStatus.GATEWAY_TIMEOUT));
assertBackoff(rule.shouldRetry(ctx3, null)).isNull();
}
@Test
void copyFromHeadersTest() {
final HttpHeaders trailers =
ResponseHeaders.builder()
.endOfStream(true)
.add(HttpHeaderNames.STATUS, HttpStatus.OK.codeAsText())
.add(HttpHeaderNames.CONTENT_TYPE, "application/grpc+proto")
.add(GrpcHeaderNames.GRPC_STATUS, "3")
.add(GrpcHeaderNames.GRPC_MESSAGE, "test_grpc_message")
.add(TEST_ASCII_KEY.originalName(), "test_message")
.add(GrpcHeaderNames.ARMERIA_GRPC_THROWABLEPROTO_BIN,
Base64.getEncoder().encodeToString(THROWABLE_PROTO.toByteArray()))
.build();
final Metadata metadata = MetadataUtil.copyFromHeaders(trailers);
assertThat(metadata.get(TEST_ASCII_KEY)).isEqualTo("test_message");
// MUST not copy values of :status, grpc-status, grpc-message, armeria.grpc.ThrowableProto-bin
assertThat(metadata.get(STATUS_KEY)).isNull();
assertThat(metadata.get(InternalStatus.CODE_KEY)).isNull();
assertThat(metadata.get(InternalStatus.MESSAGE_KEY)).isNull();
assertThat(metadata.get(THROWABLE_PROTO_METADATA_KEY)).isNull();
}
static ResponseHeaders ensureHttpStatus(ResponseHeaders headers) {
final HttpStatus status = headers.status();
if (status.equals(HttpStatus.OK)) {
return headers;
}
if (!warnedStatusCode) {
logger.warn(
"Overwriting the HTTP status code from '{}' to '{}' for JSON Text Sequences. " +
"Do not set an HTTP status code on the HttpHeaders when calling factory methods in '{}', " +
"or set '{}' if you want to specify its status code. " +
"Please refer to https://tools.ietf.org/html/rfc7464 for more information.",
status, HttpStatus.OK, JsonTextSequences.class.getSimpleName(), HttpStatus.OK);
warnedStatusCode = true;
}
return headers.toBuilder().status(HttpStatus.OK).build();
}
@Test
public void cancel_duringReadingData() throws Exception {
when(armeriaCall.tryFinish()).thenReturn(false);
when(armeriaCall.isCanceled()).thenReturn(false, false, false, true);
final ManualMockCallback callback = new ManualMockCallback();
final StreamingCallSubscriber subscriber = new StreamingCallSubscriber(
armeriaCall, callback, new Request.Builder().url("http://foo.com").build(),
MoreExecutors.directExecutor());
subscriber.onSubscribe(subscription);
subscriber.onNext(ResponseHeaders.of(200));
subscriber.onNext(HttpData.ofUtf8("{\"name\":"));
subscriber.onNext(HttpData.ofUtf8("\"foo\"}"));
subscriber.onComplete();
verify(subscription, times(2)).request(1L);
await().untilAsserted(() -> assertThat(callback.callbackCallingCount).isEqualTo(1));
await().untilAsserted(() -> assertThat(callback.exception).isNull());
await().untilAsserted(
() -> assertThatThrownBy(() -> callback.response.body().string()).hasMessage("closed"));
}
@Test
void dataIsIgnoreAfterSecondHeaders() throws Exception {
final DecodedHttpResponse res = new DecodedHttpResponse(CommonPools.workerGroup().next());
final HttpResponseWrapper wrapper = httpResponseWrapper(res);
assertThat(wrapper.tryWrite(ResponseHeaders.of(200))).isTrue();
assertThat(wrapper.tryWrite(
HttpHeaders.of(HttpHeaderNames.of("bar"), "baz"))).isTrue(); // Second header is trailers.
assertThat(wrapper.tryWrite(HttpData.ofUtf8("foo"))).isFalse();
wrapper.close();
StepVerifier.create(res)
.expectNext(ResponseHeaders.of(200))
.expectNext(HttpHeaders.of(HttpHeaderNames.of("bar"), "baz"))
.expectComplete()
.verify();
}
@Override
public ChannelFuture doWriteHeaders(int id, int streamId, ResponseHeaders headers, boolean endStream,
boolean isTrailersEmpty) {
if (!isStreamPresentAndWritable(streamId)) {
// One of the following cases:
// - Stream has been closed already.
// - (bug) Server tried to send a response HEADERS frame before receiving a request HEADERS frame.
return newFailedFuture(ClosedStreamException.get());
}
if (!isGoAwaySent && keepAliveHandler != null && keepAliveHandler.isMaxConnectionAgeExceeded()) {
final int lastStreamId = encoder().connection().remote().lastStreamCreated();
encoder().writeGoAway(ctx(), lastStreamId, Http2Error.NO_ERROR.code(),
MAX_CONNECTION_AGE_DEBUG.retain(), ctx().newPromise());
isGoAwaySent = true;
}
final Http2Headers converted = convertHeaders(headers, isTrailersEmpty);
onKeepAliveReadOrWrite();
return encoder().writeHeaders(ctx(), streamId, converted, 0, endStream, ctx().newPromise());
}
private static HttpResponse httpResponse(HttpData data) {
final HttpResponseWriter res = HttpResponse.streaming();
final long current = System.currentTimeMillis();
final ResponseHeadersBuilder headers = ResponseHeaders.builder(HttpStatus.OK);
headers.setInt(HttpHeaderNames.CONTENT_LENGTH, data.length());
headers.setTimeMillis(HttpHeaderNames.DATE, current);
final MediaType contentType = ServiceRequestContext.current().negotiatedResponseMediaType();
if (contentType != null) {
headers.contentType(contentType);
}
res.write(headers.build());
res.write(data);
res.close();
return res;
}
@Test
void pathMissingSlash() throws Exception {
final HttpRequest req = HttpRequest.of(
RequestHeaders.of(HttpMethod.POST, "/grpc.testing.TestService.UnaryCall",
HttpHeaderNames.CONTENT_TYPE, "application/grpc+proto"));
final RoutingResult routingResult = RoutingResult.builder()
.path("grpc.testing.TestService.UnaryCall")
.build();
final ServiceRequestContext ctx = ServiceRequestContext.builder(req)
.routingResult(routingResult)
.build();
final HttpResponse response = grpcService.doPost(ctx, PooledHttpRequest.of(req));
assertThat(response.aggregate().get()).isEqualTo(AggregatedHttpResponse.of(
ResponseHeaders.of(HttpStatus.BAD_REQUEST,
HttpHeaderNames.CONTENT_TYPE, MediaType.PLAIN_TEXT_UTF_8,
HttpHeaderNames.CONTENT_LENGTH, 13),
HttpData.ofUtf8("Invalid path.")));
}
/**
* Returns a {@link Service} which handles a login request from a web browser. By default,
* the browser would bring a user to the built-in web login page served on {@value BUILTIN_WEB_LOGIN_PATH}.
*/
default HttpService webLoginService() {
// Redirect to the default page: /link/auth/login -> /web/auth/login
return (ctx, req) -> HttpResponse.of(
ResponseHeaders.of(HttpStatus.MOVED_PERMANENTLY, HttpHeaderNames.LOCATION,
BUILTIN_WEB_LOGIN_PATH));
}
@Test
void requestLogAvailabilityException() {
final String fullName = AccessLogFormatsTest.class.getSimpleName() + "/rpcMethod";
final String expectedLogMessage = "\"GET /armeria/log#" + fullName + " h2c\" 200 1024";
final ServiceRequestContext ctx = ServiceRequestContext.builder(
HttpRequest.of(RequestHeaders.of(HttpMethod.GET, "/armeria/log",
HttpHeaderNames.USER_AGENT, "armeria/x.y.z",
HttpHeaderNames.REFERER, "http://log.example.com",
HttpHeaderNames.COOKIE, "a=1;b=2"))).build();
final RequestLog log = ctx.log().partial();
final RequestLogBuilder logBuilder = ctx.logBuilder();
// AccessLogger#format will be called after response is finished.
final AtomicReference<RequestLog> logHolder = new AtomicReference<>();
log.whenComplete().thenAccept(logHolder::set);
// RequestLogAvailabilityException will be raised inside AccessLogger#format before injecting each
// component to RequestLog. So we cannot get the expected log message here.
assertThat(AccessLogger.format(AccessLogFormats.COMMON, log)).doesNotEndWith(expectedLogMessage);
logBuilder.requestContent(RpcRequest.of(AccessLogFormatsTest.class, "rpcMethod"), null);
assertThat(AccessLogger.format(AccessLogFormats.COMMON, log)).doesNotEndWith(expectedLogMessage);
logBuilder.endRequest();
assertThat(AccessLogger.format(AccessLogFormats.COMMON, log)).doesNotEndWith(expectedLogMessage);
logBuilder.responseHeaders(ResponseHeaders.of(HttpStatus.OK));
assertThat(AccessLogger.format(AccessLogFormats.COMMON, log)).doesNotEndWith(expectedLogMessage);
logBuilder.responseLength(1024);
assertThat(AccessLogger.format(AccessLogFormats.COMMON, log)).doesNotEndWith(expectedLogMessage);
logBuilder.endResponse();
assertThat(AccessLogger.format(AccessLogFormats.COMMON, logHolder.get()))
.endsWith(expectedLogMessage);
}
@Test
void createRepository() throws IOException {
final WebClient client = dogma.httpClient();
final AggregatedHttpResponse aRes = createRepository(client, "myRepo");
final ResponseHeaders headers = ResponseHeaders.of(aRes.headers());
assertThat(headers.status()).isEqualTo(HttpStatus.CREATED);
final String location = headers.get(HttpHeaderNames.LOCATION);
assertThat(location).isEqualTo("/api/v1/projects/myPro/repos/myRepo");
final JsonNode jsonNode = Jackson.readTree(aRes.contentUtf8());
assertThat(jsonNode.get("name").asText()).isEqualTo("myRepo");
assertThat(jsonNode.get("headRevision").asInt()).isOne();
assertThat(jsonNode.get("createdAt").asText()).isNotNull();
}
@Override
public HttpResponse convertResponse(ServiceRequestContext ctx,
ResponseHeaders headers,
@Nullable Object result,
HttpHeaders trailers) throws Exception {
if (result instanceof String && "hello foo".equals(result)) {
assertThat(responseCounter.getAndIncrement()).isEqualTo(3);
return HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, HttpData.ofUtf8(
(String) result));
}
return ResponseConverterFunction.fallthrough();
}
@Test
void fillHeadersTest() {
final HttpHeadersBuilder trailers =
ResponseHeaders.builder()
.endOfStream(true)
.add(HttpHeaderNames.STATUS, HttpStatus.OK.codeAsText())
.add(HttpHeaderNames.CONTENT_TYPE, "application/grpc+proto")
.add(GrpcHeaderNames.GRPC_STATUS, "3")
.add(GrpcHeaderNames.GRPC_MESSAGE, "test_grpc_message");
final Metadata metadata = new Metadata();
// be copied into HttpHeaderBuilder trailers
metadata.put(TEST_ASCII_KEY, "metadata_test_string");
metadata.put(TEST_BIN_KEY, "metadata_test_string".getBytes());
// must not be copied into HttpHeaderBuilder trailers
metadata.put(STATUS_KEY, "200");
metadata.put(InternalStatus.CODE_KEY, Status.OK);
metadata.put(InternalStatus.MESSAGE_KEY, "grpc_message_must_not_be_copied");
metadata.put(THROWABLE_PROTO_METADATA_KEY, THROWABLE_PROTO);
MetadataUtil.fillHeaders(metadata, trailers);
assertThat(trailers.getAll(TEST_ASCII_KEY.originalName())).containsExactly("metadata_test_string");
assertThat(Base64.getDecoder().decode(trailers.get(TEST_BIN_KEY.originalName())))
.containsExactly("metadata_test_string".getBytes());
assertThat(trailers.getAll(HttpHeaderNames.STATUS)).containsExactly(HttpStatus.OK.codeAsText());
assertThat(trailers.getAll(HttpHeaderNames.CONTENT_TYPE)).containsExactly("application/grpc+proto");
assertThat(trailers.getAll(GrpcHeaderNames.GRPC_STATUS)).containsExactly("3");
assertThat(trailers.getAll(GrpcHeaderNames.GRPC_MESSAGE)).containsOnly("test_grpc_message");
assertThat(trailers.getAll(GrpcHeaderNames.ARMERIA_GRPC_THROWABLEPROTO_BIN)).isEmpty();
}
@Override
protected void configure(ServerBuilder sb) throws Exception {
sb.service("/", (ctx, req) -> HttpResponse.from(
req.aggregate()
.thenApply(aggregated -> {
final ResponseHeaders responseHeaders =
ResponseHeaders.of(HttpStatus.OK,
HttpHeaderNames.CONTENT_TYPE, MediaType.PLAIN_TEXT_UTF_8);
return HttpResponse.of(responseHeaders,
HttpData.ofUtf8("Hello " + aggregated.contentUtf8() + '!'));
})));
sb.decorator(EncodingService.builder()
.minBytesToForceChunkedEncoding(1)
.newDecorator());
}
@Test
void removeProject() {
final WebClient client = dogma.httpClient();
createProject(client, "foo");
final AggregatedHttpResponse aRes = client.delete(PROJECTS_PREFIX + "/foo")
.aggregate().join();
final ResponseHeaders headers = ResponseHeaders.of(aRes.headers());
assertThat(ResponseHeaders.of(headers).status()).isEqualTo(HttpStatus.NO_CONTENT);
}
@Test
void removeAbsentProject() {
final WebClient client = dogma.httpClient();
final AggregatedHttpResponse aRes = client.delete(PROJECTS_PREFIX + "/foo")
.aggregate().join();
assertThat(ResponseHeaders.of(aRes.headers()).status()).isEqualTo(HttpStatus.NOT_FOUND);
}
CommonComponent(AccessLogType type, boolean addQuote,
@Nullable Function<ResponseHeaders, Boolean> condition,
@Nullable String variable) {
super(condition, addQuote);
checkArgument(isSupported(requireNonNull(type, "type")),
"Type '%s' is not acceptable by %s", type, CommonComponent.class.getName());
this.type = type;
this.variable = variable;
}
@Test
void unremoveAbsentProject() {
final String projectPath = PROJECTS_PREFIX + "/bar";
final RequestHeaders headers = RequestHeaders.of(HttpMethod.PATCH, projectPath,
HttpHeaderNames.CONTENT_TYPE,
"application/json-patch+json");
final String unremovePatch = "[{\"op\":\"replace\",\"path\":\"/status\",\"value\":\"active\"}]";
final WebClient client = dogma.httpClient();
final AggregatedHttpResponse aRes = client.execute(headers, unremovePatch).aggregate().join();
assertThat(ResponseHeaders.of(aRes.headers()).status()).isEqualTo(HttpStatus.NOT_FOUND);
}
/**
* Returns a new {@link HttpResponseWriter} which has a content converted from the collected objects.
*
* @param publisher publishes objects
* @param headers to be written to the returned {@link HttpResponseWriter}
* @param trailers to be written to the returned {@link HttpResponseWriter}
* @param contentConverter converts the collected objects into a content of the response
*/
public static HttpResponseWriter aggregateFrom(Publisher<?> publisher,
ResponseHeaders headers, HttpHeaders trailers,
Function<Object, HttpData> contentConverter) {
requireNonNull(publisher, "publisher");
requireNonNull(headers, "headers");
requireNonNull(trailers, "trailers");
requireNonNull(contentConverter, "contentConverter");
return aggregateFrom(collectFrom(publisher), headers, trailers, contentConverter);
}