/** * Produces a generic error response. Call this if you know the error is a non-normal unhandled error of the "how * did we get here, this should never happen" variety, or if other attempts to deal with the error failed and you * need a guaranteed fallback that will produce a generic error response that follows our error contract. If you * have an error that happened during normal processing you should try {@link #processError(HttpProcessingState, * Object, Throwable)} instead in order to get an error response that is better tailored to the given error rather * than this one which guarantees a somewhat unhelpful generic error response. */ ResponseInfo<ErrorResponseBody> processUnhandledError(HttpProcessingState state, Object msg, Throwable cause) { RequestInfo<?> requestInfo = getRequestInfo(state, msg); // Run the error through the riposteUnhandledErrorHandler ErrorResponseInfo contentFromErrorHandler = riposteUnhandledErrorHandler.handleError(cause, requestInfo); ResponseInfo<ErrorResponseBody> responseInfo = new FullResponseInfo<>(); setupResponseInfoBasedOnErrorResponseInfo(responseInfo, contentFromErrorHandler); return responseInfo; }
/** * Produces a generic error response. Call this if you know the error is a non-normal unhandled error of the "how * did we get here, this should never happen" variety, or if other attempts to deal with the error failed and you * need a guaranteed fallback that will produce a generic error response that follows our error contract. If you * have an error that happened during normal processing you should try {@link #processError(HttpProcessingState, * Object, Throwable)} instead in order to get an error response that is better tailored to the given error rather * than this one which guarantees a somewhat unhelpful generic error response. */ ResponseInfo<ErrorResponseBody> processUnhandledError(HttpProcessingState state, Object msg, Throwable cause) { RequestInfo<?> requestInfo = getRequestInfo(state, msg); // Run the error through the riposteUnhandledErrorHandler ErrorResponseInfo contentFromErrorHandler = riposteUnhandledErrorHandler.handleError(cause, requestInfo); ResponseInfo<ErrorResponseBody> responseInfo = new FullResponseInfo<>(); setupResponseInfoBasedOnErrorResponseInfo(responseInfo, contentFromErrorHandler); return responseInfo; }
@Test public void getRequestInfo_uses_requestInfo_from_state_if_available() { // given RequestInfo<?> requestInfoMock = mock(RequestInfo.class); state.setRequestInfo(requestInfoMock); // when RequestInfo<?> result = handler.getRequestInfo(state, null); // then assertThat(result, is(requestInfoMock)); }
@Test public void getRequestInfo_uses_dummy_instance_if_state_does_not_have_requestInfo_and_msg_is_not_a_FullHttpRequest() { // given assertThat(state.getRequestInfo(), nullValue()); // when RequestInfo<?> result = handler.getRequestInfo(state, new Object()); // then assertThat(result.getUri(), is(RequestInfo.NONE_OR_UNKNOWN_TAG)); }
@Test public void getRequestInfo_uses_dummy_instance_if_state_does_not_have_requestInfo_and_msg_is_null() { // given assertThat(state.getRequestInfo(), nullValue()); // when RequestInfo<?> result = handler.getRequestInfo(state, null); // then assertThat(result.getUri(), is(RequestInfo.NONE_OR_UNKNOWN_TAG)); }
@Test public void getRequestInfo_creates_new_RequestInfo_based_on_msg_if_state_requestInfo_is_null_and_msg_is_a_HttpRequest() { // given assertThat(state.getRequestInfo(), nullValue()); String expectedUri = "/some/uri/" + UUID.randomUUID().toString(); HttpRequest httpRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, expectedUri); // when RequestInfo<?> result = handler.getRequestInfo(state, httpRequest); // then assertThat(result.getUri(), is(expectedUri)); }
/** * Attempts to process the given error using the "normal" error handler {@link #riposteErrorHandler} to produce the * most specific error response possible for the given error. If that fails for any reason then the unhandled error * handler will take over to guarantee the user gets a generic error response that still follows our error contract. * If you already know your error is a non-normal unhandled error of the "how did we get here, this should never * happen" variety you can (and should) directly call {@link #processUnhandledError(HttpProcessingState, Object, * Throwable)} instead. */ protected ResponseInfo<ErrorResponseBody> processError(HttpProcessingState state, Object msg, Throwable cause) { RequestInfo<?> requestInfo = getRequestInfo(state, msg); try { ErrorResponseInfo contentFromErrorHandler = riposteErrorHandler.maybeHandleError(cause, requestInfo); if (contentFromErrorHandler != null) { // The regular error handler did handle the error. Setup our ResponseInfo. ResponseInfo<ErrorResponseBody> responseInfo = new FullResponseInfo<>(); setupResponseInfoBasedOnErrorResponseInfo(responseInfo, contentFromErrorHandler); return responseInfo; } } catch (Throwable errorHandlerFailed) { logger.error("An unexpected problem occurred while trying to handle an error.", errorHandlerFailed); } // If we reach here then it means the regular handler didn't handle the error (or blew up trying to handle it), // so the riposteUnhandledErrorHandler should take care of it. return processUnhandledError(state, msg, cause); }
/** * Attempts to process the given error using the "normal" error handler {@link #riposteErrorHandler} to produce the * most specific error response possible for the given error. If that fails for any reason then the unhandled error * handler will take over to guarantee the user gets a generic error response that still follows our error contract. * If you already know your error is a non-normal unhandled error of the "how did we get here, this should never * happen" variety you can (and should) directly call {@link #processUnhandledError(HttpProcessingState, Object, * Throwable)} instead. */ protected ResponseInfo<ErrorResponseBody> processError(HttpProcessingState state, Object msg, Throwable cause) { RequestInfo<?> requestInfo = getRequestInfo(state, msg); try { ErrorResponseInfo contentFromErrorHandler = riposteErrorHandler.maybeHandleError(cause, requestInfo); if (contentFromErrorHandler != null) { // The regular error handler did handle the error. Setup our ResponseInfo. ResponseInfo<ErrorResponseBody> responseInfo = new FullResponseInfo<>(); setupResponseInfoBasedOnErrorResponseInfo(responseInfo, contentFromErrorHandler); return responseInfo; } } catch (Throwable errorHandlerFailed) { logger.error("An unexpected problem occurred while trying to handle an error.", errorHandlerFailed); } // If we reach here then it means the regular handler didn't handle the error (or blew up trying to handle it), // so the riposteUnhandledErrorHandler should take care of it. return processUnhandledError(state, msg, cause); }
@Test public void processUnhandledError_uses_getRequestInfo_and_calls_riposteUnhandledErrorHandler_and_returns_value_of_setupResponseInfoBasedOnErrorResponseInfo() throws JsonProcessingException, UnexpectedMajorErrorHandlingError { // given HttpProcessingState stateMock = mock(HttpProcessingState.class); Object msg = new Object(); Throwable cause = new Exception(); ExceptionHandlingHandler handlerSpy = spy(handler); RequestInfo<?> requestInfoMock = mock(RequestInfo.class); ErrorResponseInfo errorResponseInfoMock = mock(ErrorResponseInfo.class); doReturn(requestInfoMock).when(handlerSpy).getRequestInfo(stateMock, msg); doReturn(errorResponseInfoMock).when(riposteUnhandledErrorHandlerMock).handleError(cause, requestInfoMock); // when ResponseInfo<ErrorResponseBody> response = handlerSpy.processUnhandledError(stateMock, msg, cause); // then verify(handlerSpy).getRequestInfo(stateMock, msg); verify(riposteUnhandledErrorHandlerMock).handleError(cause, requestInfoMock); ArgumentCaptor<ResponseInfo> responseInfoArgumentCaptor = ArgumentCaptor.forClass(ResponseInfo.class); verify(handlerSpy).setupResponseInfoBasedOnErrorResponseInfo(responseInfoArgumentCaptor.capture(), eq(errorResponseInfoMock)); ResponseInfo<ErrorResponseBody> responseInfoPassedIntoSetupMethod = responseInfoArgumentCaptor.getValue(); assertThat(response, is(responseInfoPassedIntoSetupMethod)); }
@Test public void processError_gets_requestInfo_then_calls_riposteErrorHandler_then_converts_to_response_using_setupResponseInfoBasedOnErrorResponseInfo() throws UnexpectedMajorErrorHandlingError, JsonProcessingException { // given HttpProcessingState stateMock = mock(HttpProcessingState.class); Object msg = new Object(); Throwable cause = new Exception(); ExceptionHandlingHandler handlerSpy = spy(handler); RequestInfo<?> requestInfoMock = mock(RequestInfo.class); ErrorResponseInfo errorResponseInfoMock = mock(ErrorResponseInfo.class); RiposteErrorHandler riposteErrorHandlerMock = mock(RiposteErrorHandler.class); Whitebox.setInternalState(handlerSpy, "riposteErrorHandler", riposteErrorHandlerMock); doReturn(requestInfoMock).when(handlerSpy).getRequestInfo(stateMock, msg); doReturn(errorResponseInfoMock).when(riposteErrorHandlerMock).maybeHandleError(cause, requestInfoMock); // when ResponseInfo<ErrorResponseBody> response = handlerSpy.processError(stateMock, msg, cause); // then verify(handlerSpy).getRequestInfo(stateMock, msg); verify(riposteErrorHandlerMock).maybeHandleError(cause, requestInfoMock); ArgumentCaptor<ResponseInfo> responseInfoArgumentCaptor = ArgumentCaptor.forClass(ResponseInfo.class); verify(handlerSpy).setupResponseInfoBasedOnErrorResponseInfo(responseInfoArgumentCaptor.capture(), eq(errorResponseInfoMock)); ResponseInfo<ErrorResponseBody> responseInfoPassedIntoSetupMethod = responseInfoArgumentCaptor.getValue(); assertThat(response, is(responseInfoPassedIntoSetupMethod)); }
@Test public void finalizeChannelPipeline_should_send_error_response_if_state_indicates_no_response_already_sent() throws JsonProcessingException { // given state.setResponseWriterFinalChunkChannelFuture(null); HttpProcessingState stateSpy = spy(state); doReturn(stateSpy).when(stateAttributeMock).get(); doReturn(false).when(responseInfoMock).isResponseSendingStarted(); Object msg = new Object(); Throwable cause = new Exception("intentional test exception"); RequestInfo<?> requestInfoMock = mock(RequestInfo.class); ResponseInfo<ErrorResponseBody> errorResponseMock = mock(ResponseInfo.class); doReturn(requestInfoMock).when(exceptionHandlingHandlerMock).getRequestInfo(stateSpy, msg); doReturn(errorResponseMock).when(exceptionHandlingHandlerMock).processUnhandledError(eq(stateSpy), eq(msg), any(Throwable.class)); // when handler.finalizeChannelPipeline(ctxMock, msg, stateSpy, cause); // then verify(responseSenderMock).sendErrorResponse(ctxMock, requestInfoMock, errorResponseMock); verify(metricsListenerMock).onEvent(ServerMetricsEvent.RESPONSE_WRITE_FAILED, stateSpy); verify(ctxMock).flush(); }
RequestInfo<?> requestInfo = exceptionHandlingHandler.getRequestInfo(state, msg);
RequestInfo<?> requestInfo = exceptionHandlingHandler.getRequestInfo(state, msg);
doReturn(proxyRouterStateMock).when(proxyRouterProcessingStateAttributeMock).get(); doReturn(proxyRouterProcessingStateAttributeMock).when(channelMock).attr(ChannelAttributes.PROXY_ROUTER_PROCESSING_STATE_ATTRIBUTE_KEY); doReturn(requestInfoMock).when(exceptionHandlingHandlerMock).getRequestInfo(any(HttpProcessingState.class), any(Object.class)); doReturn(true).when(responseInfoMock).isResponseSendingStarted(); doReturn(true).when(responseInfoMock).isResponseSendingLastChunkSent();
@Test public void getRequestInfo_uses_dummy_instance_if_an_exception_occurs_while_creating_RequestInfo_from_HttpRequest_msg() { // given assertThat(state.getRequestInfo(), nullValue()); RiposteHandlerInternalUtil explodingHandlerUtilMock = mock(RiposteHandlerInternalUtil.class); doThrow(new RuntimeException("intentional exception")) .when(explodingHandlerUtilMock) .createRequestInfoFromNettyHttpRequestAndHandleStateSetupIfNecessary(any(), any()); Whitebox.setInternalState(handler, "handlerUtils", explodingHandlerUtilMock); HttpRequest httpRequest = new DefaultHttpRequest( HttpVersion.HTTP_1_1, HttpMethod.GET, "/some/uri" ); // when RequestInfo<?> result = handler.getRequestInfo(state, httpRequest); // then assertThat(result.getUri(), is(RequestInfo.NONE_OR_UNKNOWN_TAG)); verify(explodingHandlerUtilMock) .createRequestInfoFromNettyHttpRequestAndHandleStateSetupIfNecessary(httpRequest, state); }