/** * Returns the scheme of this request. This method is a shortcut of {@code headers().scheme()}. */ default String scheme() { return headers().scheme(); }
/** * Creates a new HTTP request and closes the stream. * * @param method the HTTP method of the request * @param path the path of the request * @param mediaType the {@link MediaType} of the request content * @param content the content of the request */ static HttpRequest of(HttpMethod method, String path, MediaType mediaType, HttpData content) { return of(method, path, mediaType, content, HttpHeaders.EMPTY_HEADERS); }
/** * Judge the turning mode. * True means turn on, False is turn off. * If not present, Not supported. * Default implementation is check content is on/off for PUT method * * @param req HttpRequest */ protected CompletionStage<Optional<Boolean>> mode(@SuppressWarnings("unused") ServiceRequestContext ctx, HttpRequest req) { return req.aggregate() .thenApply(AggregatedHttpMessage::content) .thenApply(HttpData::toStringAscii) .thenApply(content -> { switch (Ascii.toUpperCase(content)) { case "ON": return Optional.of(true); case "OFF": return Optional.of(false); default: return Optional.empty(); } }); } }
/** * Aggregates this request. The returned {@link CompletableFuture} will be notified when the content and * the trailing headers of the request is received fully. */ default CompletableFuture<AggregatedHttpMessage> aggregate() { final CompletableFuture<AggregatedHttpMessage> future = new CompletableFuture<>(); final HttpRequestAggregator aggregator = new HttpRequestAggregator(this, future, null); completionFuture().handle(aggregator); subscribe(aggregator); return future; }
/** * Returns {@code true} if the specified {@code request} is a CORS preflight request. */ public static boolean isCorsPreflightRequest(com.linecorp.armeria.common.HttpRequest request) { requireNonNull(request, "request"); return request.method() == HttpMethod.OPTIONS && request.headers().contains(HttpHeaderNames.ORIGIN) && request.headers().contains(HttpHeaderNames.ACCESS_CONTROL_REQUEST_METHOD); }
ArmeriaServerHttpRequest(ServiceRequestContext ctx, HttpRequest req, DataBufferFactoryWrapper<?> factoryWrapper) { super(URI.create(requireNonNull(req, "req").path()), null, fromArmeriaHttpHeaders(req.headers())); this.ctx = requireNonNull(ctx, "ctx"); this.req = req; body = Flux.from(req).cast(HttpData.class).map(factoryWrapper::toDataBuffer) // Guarantee that the context is accessible from a controller method // when a user specify @RequestBody in order to convert a request body into an object. .publishOn(Schedulers.fromExecutor(ctx.contextAwareExecutor())); }
@Override protected HttpResponse doPost(ServiceRequestContext ctx, HttpRequest req) throws Exception { final MediaType contentType = req.headers().contentType(); final SerializationFormat serializationFormat = findSerializationFormat(contentType); if (serializationFormat == null) { final String timeoutHeader = req.headers().get(GrpcHeaderNames.GRPC_TIMEOUT); if (timeoutHeader != null) { try { methodName, method, ctx, req.headers(), res, serializationFormat); if (call != null) { ctx.setRequestTimeoutHandler(() -> call.close(Status.DEADLINE_EXCEEDED, EMPTY_METADATA)); req.subscribe(call.messageReader(), ctx.eventLoop(), true); req.completionFuture().handleAsync(call.messageReader(), ctx.eventLoop());
private boolean handleEarlyCancellation(ClientRequestContext ctx, HttpRequest req, DecodedHttpResponse res) { if (res.isOpen()) { return false; } // The response has been closed even before its request is sent. assert protocol != null; req.abort(); ctx.logBuilder().startRequest(channel, protocol); ctx.logBuilder().requestHeaders(req.headers()); req.completionFuture().handle((unused, cause) -> { if (cause == null) { ctx.logBuilder().endRequest(); } else { ctx.logBuilder().endRequest(cause); } return null; }); res.completionFuture().handle((unused, cause) -> { if (cause == null) { ctx.logBuilder().endResponse(); } else { ctx.logBuilder().endResponse(cause); } return null; }); return true; }
@Override public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) throws Exception { final HttpHeaders clientHeaders = req.headers(); final MediaType contentType = clientHeaders.contentType(); if (contentType == null) { req.aggregateWithPooledObjects(ctx.eventLoop(), ctx.alloc()).handle((clientRequest, t) -> { if (t != null) { responseFuture.completeExceptionally(t);
final HttpHeaders headers = httpRequest.headers(); assertThat(headers.method()).isEqualTo(com.linecorp.armeria.common.HttpMethod.GET); assertThat(headers.path()).isEqualTo(TEST_PATH_AND_QUERY); assertThat(headers.get(HttpHeaderNames.COOKIE)).isEqualTo("a=1"); assertThat(httpRequest.completionFuture().isDone()).isFalse(); .verify(); await().until(() -> httpRequest.completionFuture().isDone());
@Test public void cancel() { final HttpRequest httpRequest = HttpRequest.of(HttpHeaders.of(HttpMethod.POST, "/"), Flux.just("a", "b", "c", "d", "e") .map(HttpData::ofUtf8)); final ServiceRequestContext ctx = newRequestContext(httpRequest); final ArmeriaServerHttpRequest req = request(ctx); assertThat(httpRequest.completionFuture().isDone()).isFalse(); final Flux<String> body = req.getBody().map(TestUtil::bufferToString); StepVerifier.create(body, 1) .expectNext("a").thenRequest(1) .expectNext("b").thenRequest(1) .thenCancel() .verify(); final CompletableFuture<Void> f = httpRequest.completionFuture(); assertThat(f.isDone()).isTrue(); assertThat(f.isCompletedExceptionally()).isTrue(); assertThatThrownBy(f::get).isInstanceOf(ExecutionException.class) .hasCauseInstanceOf(CancelledSubscriptionException.class); }
@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); })); }
@Override public String getMethodValue() { return req.method().name(); }
private HttpResponse execute(@Nullable EventLoop eventLoop, HttpRequest req) { final String concatPaths = concatPaths(uri().getRawPath(), req.path()); req.path(concatPaths); final PathAndQuery pathAndQuery = PathAndQuery.parse(concatPaths); if (pathAndQuery == null) { req.abort(); return HttpResponse.ofFailure(new IllegalArgumentException("invalid path: " + concatPaths)); } return execute(eventLoop, req.method(), pathAndQuery.path(), pathAndQuery.query(), null, req, (ctx, cause) -> { if (ctx != null && !ctx.log().isAvailable(RequestLogAvailability.REQUEST_START)) { // An exception is raised even before sending a request, so abort the request to // release the elements. req.abort(); } return HttpResponse.ofFailure(cause); }); }
private static boolean isValidPath(HttpRequest req) { return PathAndQuery.parse(req.path()) != null; }
ctx.logBuilder().serializationFormat(serializationFormat); ctx.logBuilder().deferRequestContent(); req.aggregateWithPooledObjects(ctx.eventLoop(), ctx.alloc()).handle((aReq, cause) -> { if (cause != null) { final HttpResponse errorRes;
private Supplier<Mono<Void>> execute(Supplier<HttpRequest> supplier) { return () -> Mono.defer(() -> { assert request == null : request; request = supplier.get(); future.complete(client.execute(request)); return Mono.fromFuture(request.completionFuture()); }); }
@Override protected HttpResponse doPost(ServiceRequestContext ctx, HttpRequest req) throws Exception { final MediaType contentType = req.headers().contentType(); final SerializationFormat serializationFormat = findSerializationFormat(contentType); if (serializationFormat == null) { final String timeoutHeader = req.headers().get(GrpcHeaderNames.GRPC_TIMEOUT); if (timeoutHeader != null) { try { methodName, method, ctx, req.headers(), res, serializationFormat); if (call != null) { ctx.setRequestTimeoutHandler(() -> call.close(Status.DEADLINE_EXCEEDED, EMPTY_METADATA)); req.subscribe(call.messageReader(), ctx.eventLoop(), true); req.completionFuture().handleAsync(call.messageReader(), ctx.eventLoop());
private void doExecute0(ClientRequestContext ctx, HttpRequestDuplicator rootReqDuplicator, HttpRequest originalReq, HttpResponse returnedRes, CompletableFuture<HttpResponse> future, boolean hasInitialAuthority) { if (originalReq.completionFuture().isCompletedExceptionally() || returnedRes.isComplete()) { duplicateReq.abort(); duplicateReq.headers().remove(HttpHeaderNames.AUTHORITY);