/** * Creates a new HTTP response of the specified headers. */ static HttpResponse of(HttpHeaders headers) { return of(headers, HttpData.EMPTY_DATA); }
@Override protected Deferred<HttpResponse> defer(ClientRequestContext ctx, HttpRequest req) throws Exception { return new Deferred<HttpResponse>() { private final CompletableFuture<HttpResponse> responseFuture = new CompletableFuture<>(); private final HttpResponse res = HttpResponse.from(responseFuture); @Override public HttpResponse response() { return res; } @Override public void delegate(HttpResponse response) { responseFuture.complete(response); } @Override public void close(Throwable cause) { responseFuture.completeExceptionally(cause); } }; } }
ArmeriaHttpClientResponseSubscriber(HttpResponse httpResponse) { httpResponse.completionFuture().whenComplete(this); httpResponse.subscribe(this, eventLoop); }
private static HttpResponse respond(CompletableFuture<HttpResponse> future, Disposable disposable) { final HttpResponse response = HttpResponse.from(future); response.completionFuture().exceptionally(cause -> { disposable.dispose(); return null; }); return response; } }
/** * Closes the {@link HttpResponseWriter} if it is opened. */ private Mono<Void> cleanup(@Nullable Throwable cause) { if (future.isDone()) { return Mono.empty(); } if (cause != null) { future.completeExceptionally(cause); logger.debug("{} Response future has been completed with a cause", ctx, cause); return Mono.empty(); } final HttpResponse response = HttpResponse.of(headers); future.complete(response); logger.debug("{} Response future has been completed with an HttpResponse", ctx); return Mono.fromFuture(response.completionFuture()); }
return HttpResponse.of(HttpStatus.UNSUPPORTED_MEDIA_TYPE, MediaType.PLAIN_TEXT_UTF_8, PROTOCOL_NOT_SUPPORTED); return HttpResponse.of(HttpStatus.NOT_ACCEPTABLE, MediaType.PLAIN_TEXT_UTF_8, ACCEPT_THRIFT_PROTOCOL_MUST_MATCH_CONTENT_TYPE); final HttpResponse res = HttpResponse.from(responseFuture); ctx.logBuilder().serializationFormat(serializationFormat); ctx.logBuilder().deferRequestContent(); final HttpResponse errorRes; if (ctx.server().config().verboseResponses()) { errorRes = HttpResponse.of(HttpStatus.INTERNAL_SERVER_ERROR, MediaType.PLAIN_TEXT_UTF_8, Exceptions.traceText(cause)); } else { errorRes = HttpResponse.of(HttpStatus.INTERNAL_SERVER_ERROR);
@Test public void success() { final AggregatedHttpMessage response = client.get("/hello/Spring").aggregate().join(); assertThat(response.status()).isEqualTo(HttpStatus.OK); assertThat(response.content().toStringUtf8()) .isEqualTo("Hello, Spring! This message is from Armeria annotated service!"); }
/** * Creates a new HTTP response of the specified {@link HttpStatus} and closes the stream if the * {@link HttpStatusClass} is not {@linkplain HttpStatusClass#INFORMATIONAL informational} (1xx). */ static HttpResponse of(HttpStatus status) { requireNonNull(status, "status"); if (status.codeClass() == HttpStatusClass.INFORMATIONAL) { final HttpResponseWriter res = streaming(); res.write(HttpHeaders.of(status)); return res; } else if (isContentAlwaysEmpty(status)) { return new OneElementFixedHttpResponse(HttpHeaders.of(status)); } else { return of(status, MediaType.PLAIN_TEXT_UTF_8, status.toHttpData()); } }
/** * Handles a {@link HttpMethod#HEAD HEAD} request. * This method sends a {@link HttpStatus#METHOD_NOT_ALLOWED 405 Method Not Allowed} response by default. */ protected HttpResponse doHead(ServiceRequestContext ctx, HttpRequest req) throws Exception { final HttpResponseWriter res = HttpResponse.streaming(); doHead(ctx, req, res); return res; }
@Override public void enqueue(Callback callback) { createRequest(); httpResponse.subscribe(callFactory.subscriberFactory.create(this, callback, request)); }
@Override default CompletableFuture<Void> closeFuture() { return completionFuture(); }
@Override public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) throws Exception { final SamlServiceFunction func = serviceMap.get(req.path()); if (func == null) { return HttpResponse.of(HttpStatus.BAD_REQUEST); } final CompletionStage<AggregatedHttpMessage> f; if (portConfigHolder.isDone()) { f = req.aggregate(); } else { f = portConfigHolder.future().thenCompose(unused -> req.aggregate()); } return HttpResponse.from(f.handle((msg, cause) -> { if (cause != null) { return HttpResponse.of(HttpStatus.BAD_REQUEST); } final SamlPortConfig portConfig = portConfigHolder.config().get(); if (portConfig.scheme().isTls() != ctx.sessionProtocol().isTls()) { return HttpResponse.of(HttpStatus.BAD_REQUEST); } // Use user-specified hostname if it exists. // If there's no hostname set by a user, the default virtual hostname will be used. final String defaultHostname = firstNonNull(sp.hostname(), ctx.virtualHost().defaultHostname()); return func.serve(ctx, msg, defaultHostname, portConfig); })); }
@Test public void healthCheck() throws Exception { final AggregatedHttpMessage res = client.get("/internal/healthcheck").aggregate().join(); assertThat(res.status()).isEqualTo(HttpStatus.OK); assertThat(res.content().toStringUtf8()).isEqualTo("ok"); } }
private Mono<Void> write(Flux<? extends DataBuffer> publisher) { return Mono.defer(() -> { final HttpResponse response = HttpResponse.of( Flux.concat(Mono.just(headers), publisher.map(factoryWrapper::toHttpData)) // Publish the response stream on the event loop in order to avoid the possibility of // calling subscription.request() from multiple threads while publishing messages // with onNext signals or starting the subscription with onSubscribe signal. .publishOn(Schedulers.fromExecutor(ctx.eventLoop()))); future.complete(response); return Mono.fromFuture(response.completionFuture()); }); }
final SerializationFormat serializationFormat = findSerializationFormat(contentType); if (serializationFormat == null) { return HttpResponse.of(HttpStatus.UNSUPPORTED_MEDIA_TYPE, MediaType.PLAIN_TEXT_UTF_8, "Missing or invalid Content-Type header."); return HttpResponse.of(HttpStatus.BAD_REQUEST, MediaType.PLAIN_TEXT_UTF_8, "Invalid path."); return HttpResponse.of( ArmeriaServerCall.statusToTrailers( ctx, ctx.setRequestTimeout(Duration.ofNanos(timeout)); } catch (IllegalArgumentException e) { return HttpResponse.of(ArmeriaServerCall.statusToTrailers(ctx, Status.fromThrowable(e), false)); ctx.logBuilder().deferResponseContent(); final HttpResponseWriter res = HttpResponse.streaming(); final ArmeriaServerCall<?, ?> call = startCall( methodName, method, ctx, req.headers(), res, serializationFormat);
private static ServerBuilder configureService(ServerBuilder sb, HttpHandler httpHandler, DataBufferFactoryWrapper<?> factoryWrapper, @Nullable String serverHeader) { final ArmeriaHttpHandlerAdapter handler = new ArmeriaHttpHandlerAdapter(httpHandler, factoryWrapper); return sb.service(PathMapping.ofCatchAll(), (ctx, req) -> { final CompletableFuture<HttpResponse> future = new CompletableFuture<>(); final HttpResponse response = HttpResponse.from(future); final Disposable disposable = handler.handle(ctx, req, future, serverHeader).subscribe(); response.completionFuture().whenComplete((unused, cause) -> { if (cause != null) { logger.debug("{} Response stream has been cancelled.", ctx, cause); disposable.dispose(); } }); return response; }); }
/** * Handles a {@link HttpMethod#PUT PUT} request. * This method sends a {@link HttpStatus#METHOD_NOT_ALLOWED 405 Method Not Allowed} response by default. */ protected HttpResponse doPut(ServiceRequestContext ctx, HttpRequest req) throws Exception { final HttpResponseWriter res = HttpResponse.streaming(); doPut(ctx, req, res); return res; }
AtomicReference<Throwable> error = new AtomicReference<>(); client.get("/trailers").subscribe(new Subscriber<HttpObject>() { @Override public void onSubscribe(Subscription s) {
@Test public void returnHeadersOnly() throws Exception { final CompletableFuture<HttpResponse> future = new CompletableFuture<>(); final ArmeriaServerHttpResponse response = response(ctx, future); response.setStatusCode(HttpStatus.NOT_FOUND); assertThat(future.isDone()).isFalse(); // Create HttpResponse. response.setComplete().subscribe(); await().until(future::isDone); assertThat(future.isCompletedExceptionally()).isFalse(); final HttpResponse httpResponse = future.get(); // Every message has not been consumed yet. assertThat(httpResponse.completionFuture().isDone()).isFalse(); StepVerifier.create(httpResponse) .assertNext(o -> { assertThat(o.isEndOfStream()).isFalse(); assertThat(o).isInstanceOf(HttpHeaders.class); final HttpHeaders headers = (HttpHeaders) o; assertThat(headers.status()) .isEqualTo(com.linecorp.armeria.common.HttpStatus.NOT_FOUND); }) .expectComplete() .verify(); await().until(() -> httpResponse.completionFuture().isDone()); }
/** * Creates a new HTTP response of OK status with the content as UTF_8 and closes the stream. * * @param content the content of the response */ static HttpResponse of(String content) { return of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, content); }