Middlewares { public static <T> Middleware<AsyncHandler<Response<T>>, AsyncHandler<Response<T>>> exceptionAndRequestIdHandler() { return innerHandler -> requestContext -> { final String requestIdHeader = requestContext.request().headers().get(X_REQUEST_ID); final String requestId = (requestIdHeader != null) ? requestIdHeader : UUID.randomUUID().toString().replace("-", ""); try (MDC.MDCCloseable ignored = MDCUtil.safePutCloseable(REQUEST_ID, requestId)) { return innerHandler.invoke(requestContext).handle((r, t) -> { final Response<T> response; if (t != null) { if (t instanceof ResponseException) { response = ((ResponseException) t).getResponse(); } else { var internalServerErrorReason = internalServerErrorReason(requestId, t); LOG.warn(internalServerErrorReason, t); response = Response.forStatus(INTERNAL_SERVER_ERROR.withReasonPhrase(internalServerErrorReason)); } } else { response = r; } return response.withHeader(X_REQUEST_ID, requestId); }); } catch (ResponseException e) { return completedFuture(e.<T>getResponse() .withHeader(X_REQUEST_ID, requestId)); } catch (Throwable t) { var internalServerErrorReason = internalServerErrorReason(requestId, t); LOG.warn(internalServerErrorReason, t); return completedFuture(Response.<T>forStatus(INTERNAL_SERVER_ERROR .withReasonPhrase(internalServerErrorReason)) .withHeader(X_REQUEST_ID, requestId)); } }; } private  Middlewares(); static Middleware<SyncHandler<? extends Response<?>>, AsyncHandler<Response<ByteString>>> json(); static Middleware<AsyncHandler<Response<T>>, AsyncHandler<Response<T>>> clientValidator(
      Supplier<Set<String>> supplier); static Middleware<AsyncHandler<Response<T>>, AsyncHandler<Response<T>>> exceptionAndRequestIdHandler(); static Middleware<AsyncHandler<Response<T>>, AsyncHandler<Response<T>>> tracer(
      Tracer tracer, String service); static Middleware<Requested<Authenticated<Response<T>>>, SyncHandler<Response<T>>> authed(
      RequestAuthenticator authenticator); static Middleware<Authenticated<EntityResponseHandler<E, R>>, SyncHandler<Response<ByteString>>> authedEntity(
      Middleware<EntityResponseHandler<E, R>, SyncHandler<Response<ByteString>>> entityMiddleware,
      Middleware<Requested<Authenticated<Response<ByteString>>>, SyncHandler<Response<ByteString>>> authed); static Middleware<Authenticated<EntityResponseHandler<E, R>>, SyncHandler<Response<ByteString>>> authedEntity(
      RequestAuthenticator authenticator,
      Middleware<EntityResponseHandler<E, R>, SyncHandler<Response<ByteString>>> entityMiddleware); static Middleware<AsyncHandler<Response<T>>, AsyncHandler<Response<T>>> authenticator(
      RequestAuthenticator authenticator);  }
@Test public void testExceptionAndRequestIdHandlerOnImmediateResponseException() throws InterruptedException, ExecutionException, TimeoutException { final RequestContext requestContext = mock(RequestContext.class); final Request request = Request.forUri("/", "GET"); final AtomicReference<String> requestId = new AtomicReference<>(); when(requestContext.request()).thenReturn(request); Response<Object> response = Middlewares.exceptionAndRequestIdHandler() .apply(rc -> { requestId.set(MDC.get("request-id")); LoggerFactory.getLogger(MiddlewaresTest.class).error("I'm a teapot!"); throw new ResponseException(Response.forStatus(Status.IM_A_TEAPOT)); }) .invoke(requestContext) .toCompletableFuture().get(5, SECONDS); assertThat(response, hasStatus(is(Status.IM_A_TEAPOT))); assertThat(response, hasHeader("X-Request-Id", is(requestId.get()))); assertThat(requestId.get(), isValidUuid()); }
@Test public void testExceptionAndRequestIdHandlerOnFutureResponseException() throws InterruptedException, ExecutionException, TimeoutException { final RequestContext requestContext = mock(RequestContext.class); final Request request = Request.forUri("/", "GET"); final AtomicReference<String> requestId = new AtomicReference<>(); when(requestContext.request()).thenReturn(request); Response<Object> response = Middlewares.exceptionAndRequestIdHandler() .apply(rc -> { requestId.set(MDC.get("request-id")); LoggerFactory.getLogger(MiddlewaresTest.class).error("I'm a teapot!"); final CompletableFuture<Response<Object>> failure = new CompletableFuture<>(); LoggerFactory.getLogger(MiddlewaresTest.class).error("deadbeef"); failure.completeExceptionally(new ResponseException(Response.forStatus(Status.IM_A_TEAPOT))); return failure; }) .invoke(requestContext) .toCompletableFuture().get(5, SECONDS); assertThat(response, hasStatus(is(Status.IM_A_TEAPOT))); assertThat(response, hasHeader("X-Request-Id", is(requestId.get()))); assertThat(requestId.get(), isValidUuid()); }
@Test public void testExceptionAndRequestIdHandlerOnFutureUnhandledException() throws InterruptedException, ExecutionException, TimeoutException { final RequestContext requestContext = mock(RequestContext.class); final Request request = Request.forUri("/", "GET"); final AtomicReference<String> requestId = new AtomicReference<>(); when(requestContext.request()).thenReturn(request); Response<Object> response = Middlewares.exceptionAndRequestIdHandler() .apply(rc -> { requestId.set(MDC.get("request-id")); final CompletableFuture<Response<Object>> failure = new CompletableFuture<>(); LoggerFactory.getLogger(MiddlewaresTest.class).error("deadbeef"); failure.completeExceptionally(new RuntimeException("fubar", new IOException("deadbeef"))); return failure; }) .invoke(requestContext) .toCompletableFuture().get(5, SECONDS); assertThat(response, hasStatus(withCode(Status.INTERNAL_SERVER_ERROR))); assertThat(response, hasStatus(withReasonPhrase(is( "Internal Server Error (Request ID: " + requestId.get() + "): RuntimeException: fubar: IOException: deadbeef")))); assertThat(response, hasHeader("X-Request-Id", is(requestId.get()))); assertThat(requestId.get(), isValidUuid()); }
@Test public void testExceptionAndRequestIdHandlerOnResponse() throws InterruptedException, ExecutionException, TimeoutException { final RequestContext requestContext = mock(RequestContext.class); final Request request = Request.forUri("/", "GET"); final AtomicReference<String> requestId = new AtomicReference<>(); when(requestContext.request()).thenReturn(request); Response<Object> response = Middlewares.exceptionAndRequestIdHandler() .apply(rc -> { requestId.set(MDC.get("request-id")); LoggerFactory.getLogger(MiddlewaresTest.class).info("I'm OK!"); return completedFuture(Response.forStatus(Status.OK)); }) .invoke(requestContext) .toCompletableFuture().get(5, SECONDS); assertThat(response, hasStatus(withCode(Status.OK))); assertThat(response, hasHeader("X-Request-Id", is(requestId.get()))); assertThat(requestId.get(), isValidUuid()); }
@Test public void testExceptionAndRequestIdHandlerAcceptsRequestIdHeader() throws InterruptedException, ExecutionException, TimeoutException { final RequestContext requestContext = mock(RequestContext.class); final String requestId = UUID.randomUUID().toString(); final Request request = Request.forUri("/", "GET") .withHeader("X-Request-Id", requestId); final AtomicReference<String> propagatedRequestId = new AtomicReference<>(); when(requestContext.request()).thenReturn(request); Response<Object> response = Middlewares.exceptionAndRequestIdHandler() .apply(rc -> { propagatedRequestId.set(MDC.get("request-id")); LoggerFactory.getLogger(MiddlewaresTest.class).info("I'm OK!"); return completedFuture(Response.forStatus(Status.OK)); }) .invoke(requestContext) .toCompletableFuture().get(5, SECONDS); assertThat(response, hasStatus(withCode(Status.OK))); assertThat(response, hasHeader("X-Request-Id", is(requestId))); assertThat(propagatedRequestId.get(), is(requestId)); }
@Test public void testExceptionHandlerForAsyncException() throws InterruptedException, ExecutionException, TimeoutException { RequestContext requestContext = mock(RequestContext.class); Request request = Request.forUri("/", "GET"); when(requestContext.request()).thenReturn(request); CompletableFuture<?> failedFuture = new CompletableFuture(); failedFuture.completeExceptionally(new ResponseException(Response.forStatus(Status.IM_A_TEAPOT))); Response<Object> response = Middlewares.exceptionAndRequestIdHandler() .apply(mockInnerHandler(requestContext, failedFuture)) .invoke(requestContext) .toCompletableFuture().get(5, SECONDS); assertThat(response, hasStatus(withCode(Status.IM_A_TEAPOT))); }