Search in sources :

Example 1 with ApiError

use of com.nike.backstopper.apierror.ApiError in project riposte by Nike-Inc.

the class BackstopperRiposteFrameworkErrorHandlerListenerTest method constructor_sets_projectApiErrors_to_passed_in_arg.

@Test
public void constructor_sets_projectApiErrors_to_passed_in_arg() {
    // given
    ProjectApiErrors projectErrorsMock = mock(ProjectApiErrors.class);
    ApiError temporaryError = new ApiErrorBase("temp_error_for_test", 42, "temporary error", 503);
    doReturn(temporaryError).when(projectErrorsMock).getTemporaryServiceProblemApiError();
    // when
    BackstopperRiposteFrameworkErrorHandlerListener impl = new BackstopperRiposteFrameworkErrorHandlerListener(projectErrorsMock);
    // then
    assertThat(impl.projectApiErrors).isSameAs(projectErrorsMock);
}
Also used : ApiErrorBase(com.nike.backstopper.apierror.ApiErrorBase) ApiError(com.nike.backstopper.apierror.ApiError) ProjectApiErrors(com.nike.backstopper.apierror.projectspecificinfo.ProjectApiErrors) Test(org.junit.Test)

Example 2 with ApiError

use of com.nike.backstopper.apierror.ApiError in project riposte by Nike-Inc.

the class VerifyMiscellaneousFunctionalityComponentTest method verify_proxy_router_response_modification_works_as_expected.

@Test
public void verify_proxy_router_response_modification_works_as_expected() throws IOException, InterruptedException {
    ExtractableResponse response = given().baseUri("http://127.0.0.1").port(serverConfig.endpointsPort()).basePath(ProxyRouterResponseModificationEndpoint.MATCHING_PATH).log().all().when().get().then().log().headers().extract();
    ApiError unmodifiedError = EmptyMetadataErrorThrower.ERROR_NO_METADATA;
    assertThat(response.statusCode()).isEqualTo(ProxyRouterResponseModificationEndpoint.MODIFIED_HTTP_STATUS_RESPONSE_CODE);
    assertThat(response.header(ProxyRouterResponseModificationEndpoint.ORIG_HTTP_STATUS_CODE_RESPONSE_HEADER_KEY)).isEqualTo(String.valueOf(unmodifiedError.getHttpStatusCode()));
    ApiError expectedModifiedError = new ApiErrorBase(unmodifiedError.getName(), unmodifiedError.getErrorCode(), unmodifiedError.getMessage(), ProxyRouterResponseModificationEndpoint.MODIFIED_HTTP_STATUS_RESPONSE_CODE);
    verifyErrorReceived(response, expectedModifiedError);
    assertThat(response.asString()).doesNotContain("metadata");
}
Also used : ExtractableResponse(io.restassured.response.ExtractableResponse) ApiErrorBase(com.nike.backstopper.apierror.ApiErrorBase) ApiError(com.nike.backstopper.apierror.ApiError) Test(org.junit.Test)

Example 3 with ApiError

use of com.nike.backstopper.apierror.ApiError in project riposte by Nike-Inc.

the class VerifyDecoderFailedResultIsHandledTest method assertErrorResponse.

private void assertErrorResponse(Pair<Integer, String> serverResponse) throws IOException {
    ApiError expectedApiError = new ApiErrorWithMetadata(MALFORMED_REQUEST, Pair.of("cause", "Invalid HTTP request"));
    verifyErrorReceived(serverResponse.getRight(), serverResponse.getLeft(), expectedApiError);
}
Also used : ApiErrorWithMetadata(com.nike.backstopper.apierror.ApiErrorWithMetadata) ApiError(com.nike.backstopper.apierror.ApiError)

Example 4 with ApiError

use of com.nike.backstopper.apierror.ApiError in project riposte by Nike-Inc.

the class VerifyTimeoutsAndProxyConnectionPoolingWorksComponentTest method verify_incomplete_call_is_timed_out.

@Test
public void verify_incomplete_call_is_timed_out() throws InterruptedException, TimeoutException, ExecutionException, IOException {
    Bootstrap bootstrap = new Bootstrap();
    EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
    try {
        CompletableFuture<Pair<String, String>> responseFromServer = new CompletableFuture<>();
        // Create a raw netty HTTP client so we can fiddle with headers and intentionally create a bad request
        //      that should trigger the bad call timeout.
        bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {

            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ChannelPipeline p = ch.pipeline();
                p.addLast(new HttpClientCodec());
                p.addLast(new HttpObjectAggregator(Integer.MAX_VALUE));
                p.addLast(new SimpleChannelInboundHandler<HttpObject>() {

                    @Override
                    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
                        if (msg instanceof FullHttpResponse) {
                            // Store the server response for asserting on later.
                            FullHttpResponse responseMsg = (FullHttpResponse) msg;
                            responseFromServer.complete(Pair.of(responseMsg.content().toString(CharsetUtil.UTF_8), responseMsg.headers().get(HttpHeaders.Names.CONNECTION)));
                        } else {
                            // Should never happen.
                            throw new RuntimeException("Received unexpected message type: " + msg.getClass());
                        }
                    }
                });
            }
        });
        // Connect to the server.
        Channel ch = bootstrap.connect("localhost", downstreamServerConfig.endpointsPort()).sync().channel();
        // Create a bad HTTP request. This one will be bad because it has a non-zero content-length header,
        //      but we're sending no payload. The server should (correctly) sit and wait for payload bytes to
        //      arrive until it hits the timeout, at which point it should return the correct error response.
        HttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, LongDelayTestEndpoint.MATCHING_PATH);
        request.headers().set(HttpHeaders.Names.HOST, "localhost");
        request.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
        request.headers().set(HttpHeaders.Names.CONTENT_LENGTH, "100");
        long beforeCallTimeNanos = System.nanoTime();
        // Send the bad request.
        ch.writeAndFlush(request);
        // Wait for the response to be received and the connection to be closed.
        try {
            ch.closeFuture().get(incompleteCallTimeoutMillis * 10, TimeUnit.MILLISECONDS);
            responseFromServer.get(incompleteCallTimeoutMillis * 10, TimeUnit.MILLISECONDS);
        } catch (TimeoutException ex) {
            fail("The call took much longer than expected without receiving a response. " + "Cancelling this test - it's not working properly", ex);
        }
        // If we reach here then the call should be complete.
        long totalCallTimeMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - beforeCallTimeNanos);
        // Verify that we got back the correct error response.
        //      It should be a MALFORMED_REQUEST with extra metadata explaining that the call was bad.
        Pair<String, String> responseInfo = responseFromServer.get();
        DefaultErrorContractDTO errorContract = objectMapper.readValue(responseInfo.getLeft(), DefaultErrorContractDTO.class);
        assertThat(errorContract).isNotNull();
        assertThat(errorContract.errors.size()).isEqualTo(1);
        DefaultErrorDTO error = errorContract.errors.get(0);
        ApiError expectedApiError = SampleCoreApiError.MALFORMED_REQUEST;
        Map<String, Object> expectedMetadata = MapBuilder.builder("cause", (Object) "Unfinished/invalid HTTP request").build();
        assertThat(error.code).isEqualTo(expectedApiError.getErrorCode());
        assertThat(error.message).isEqualTo(expectedApiError.getMessage());
        assertThat(error.metadata).isEqualTo(expectedMetadata);
        // The server should have closed the connection even though we asked for keep-alive.
        assertThat(responseInfo.getRight()).isEqualTo(HttpHeaders.Values.CLOSE);
        // Total call time should be pretty close to incompleteCallTimeoutMillis give or take a few
        //      milliseconds, but due to the inability to account for slow machines running the unit tests,
        //      a server that isn't warmed up, etc, we can't put a ceiling on the wiggle room we'd need, so
        //      we'll just verify it took at least the minimum necessary amount of time.
        assertThat(totalCallTimeMillis).isGreaterThanOrEqualTo(incompleteCallTimeoutMillis);
    } finally {
        eventLoopGroup.shutdownGracefully();
    }
}
Also used : SocketChannel(io.netty.channel.socket.SocketChannel) NioSocketChannel(io.netty.channel.socket.nio.NioSocketChannel) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) HttpClientCodec(io.netty.handler.codec.http.HttpClientCodec) CompletableFuture(java.util.concurrent.CompletableFuture) HttpObject(io.netty.handler.codec.http.HttpObject) Bootstrap(io.netty.bootstrap.Bootstrap) DefaultErrorDTO(com.nike.backstopper.model.DefaultErrorDTO) FullHttpResponse(io.netty.handler.codec.http.FullHttpResponse) NioEventLoopGroup(io.netty.channel.nio.NioEventLoopGroup) Pair(com.nike.internal.util.Pair) TimeoutException(java.util.concurrent.TimeoutException) SimpleChannelInboundHandler(io.netty.channel.SimpleChannelInboundHandler) DefaultFullHttpRequest(io.netty.handler.codec.http.DefaultFullHttpRequest) HttpRequest(io.netty.handler.codec.http.HttpRequest) DefaultFullHttpRequest(io.netty.handler.codec.http.DefaultFullHttpRequest) DefaultErrorContractDTO(com.nike.backstopper.model.DefaultErrorContractDTO) SocketChannel(io.netty.channel.socket.SocketChannel) NioSocketChannel(io.netty.channel.socket.nio.NioSocketChannel) Channel(io.netty.channel.Channel) TimeoutException(java.util.concurrent.TimeoutException) IOException(java.io.IOException) ExecutionException(java.util.concurrent.ExecutionException) ChannelPipeline(io.netty.channel.ChannelPipeline) NioSocketChannel(io.netty.channel.socket.nio.NioSocketChannel) HttpObjectAggregator(io.netty.handler.codec.http.HttpObjectAggregator) NioEventLoopGroup(io.netty.channel.nio.NioEventLoopGroup) EventLoopGroup(io.netty.channel.EventLoopGroup) HttpObject(io.netty.handler.codec.http.HttpObject) ApiError(com.nike.backstopper.apierror.ApiError) SampleCoreApiError(com.nike.backstopper.apierror.sample.SampleCoreApiError) Test(org.junit.Test)

Example 5 with ApiError

use of com.nike.backstopper.apierror.ApiError in project riposte by Nike-Inc.

the class BackstopperRiposteFrameworkErrorHandlerListener method shouldHandleException.

@Override
public ApiExceptionHandlerListenerResult shouldHandleException(Throwable ex) {
    if (ex instanceof CircuitBreakerException) {
        CircuitBreakerException cbe = ((CircuitBreakerException) ex);
        return ApiExceptionHandlerListenerResult.handleResponse(singletonError(getApiErrorForCircuitBreakerException(cbe)), singletonList(Pair.of("circuit_breaker_id", String.valueOf(cbe.circuitBreakerId))));
    }
    if (ex instanceof NonblockingEndpointCompletableFutureTimedOut) {
        return ApiExceptionHandlerListenerResult.handleResponse(singletonError(projectApiErrors.getTemporaryServiceProblemApiError()), singletonList(Pair.of("completable_future_timeout_value_millis", String.valueOf(((NonblockingEndpointCompletableFutureTimedOut) ex).timeoutValueMillis))));
    }
    if (ex instanceof DownstreamIdleChannelTimeoutException) {
        DownstreamIdleChannelTimeoutException idleEx = (DownstreamIdleChannelTimeoutException) ex;
        return ApiExceptionHandlerListenerResult.handleResponse(singletonError(projectApiErrors.getTemporaryServiceProblemApiError()), Arrays.asList(Pair.of("async_downstream_call_timeout_value_millis", String.valueOf(idleEx.timeoutValueMillis)), Pair.of("idle_channel_id", String.valueOf(idleEx.channelId))));
    }
    if (ex instanceof DownstreamChannelClosedUnexpectedlyException) {
        DownstreamChannelClosedUnexpectedlyException dsClosedEx = (DownstreamChannelClosedUnexpectedlyException) ex;
        return ApiExceptionHandlerListenerResult.handleResponse(singletonError(projectApiErrors.getTemporaryServiceProblemApiError()), singletonList(Pair.of("closed_channel_id", String.valueOf(dsClosedEx.channelId))));
    }
    if (ex instanceof DecoderException) {
        // TODO: TooLongFrameException should result in a 413 Payload Too Large error instead of generic 400 malformed request.
        //       For now, we can at least let the caller know why it failed via error metadata.
        ApiError errorToUse = (ex instanceof TooLongFrameException) ? new ApiErrorWithMetadata(projectApiErrors.getMalformedRequestApiError(), Pair.of("cause", "The request exceeded the maximum payload size allowed")) : projectApiErrors.getMalformedRequestApiError();
        return ApiExceptionHandlerListenerResult.handleResponse(singletonError(errorToUse), Arrays.asList(Pair.of("decoder_exception", "true"), Pair.of("decoder_exception_message", ex.getMessage())));
    }
    if (ex instanceof HostnameResolutionException) {
        return ApiExceptionHandlerListenerResult.handleResponse(singletonError(projectApiErrors.getTemporaryServiceProblemApiError()));
    }
    if (ex instanceof NativeIoExceptionWrapper) {
        return ApiExceptionHandlerListenerResult.handleResponse(singletonError(projectApiErrors.getTemporaryServiceProblemApiError()));
    }
    if (ex instanceof RequestContentDeserializationException) {
        RequestContentDeserializationException theEx = (RequestContentDeserializationException) ex;
        return ApiExceptionHandlerListenerResult.handleResponse(singletonError(projectApiErrors.getMalformedRequestApiError()), Arrays.asList(Pair.of("method", theEx.httpMethod), Pair.of("request_path", theEx.requestPath), Pair.of("desired_object_type", theEx.desiredObjectType.getType().toString())));
    }
    if (ex instanceof PathNotFound404Exception) {
        return ApiExceptionHandlerListenerResult.handleResponse(singletonError(projectApiErrors.getNotFoundApiError()));
    }
    if (ex instanceof MethodNotAllowed405Exception) {
        MethodNotAllowed405Exception theEx = (MethodNotAllowed405Exception) ex;
        return ApiExceptionHandlerListenerResult.handleResponse(singletonError(projectApiErrors.getMethodNotAllowedApiError()), Arrays.asList(Pair.of("incoming_request_path", theEx.requestPath), Pair.of("incoming_request_method", theEx.requestMethod)));
    }
    if (ex instanceof Unauthorized401Exception) {
        Unauthorized401Exception theEx = (Unauthorized401Exception) ex;
        List<Pair<String, String>> extraDetails = new ArrayList<>();
        extraDetails.add(Pair.of("message", ex.getMessage()));
        extraDetails.add(Pair.of("incoming_request_path", theEx.requestPath));
        extraDetails.add(Pair.of("authorization_header", theEx.authorizationHeader));
        extraDetails.addAll((theEx).extraDetailsForLogging);
        return ApiExceptionHandlerListenerResult.handleResponse(singletonError(projectApiErrors.getUnauthorizedApiError()), extraDetails);
    }
    if (ex instanceof Forbidden403Exception) {
        Forbidden403Exception theEx = (Forbidden403Exception) ex;
        List<Pair<String, String>> extraDetails = new ArrayList<>();
        extraDetails.add(Pair.of("message", ex.getMessage()));
        extraDetails.add(Pair.of("incoming_request_path", theEx.requestPath));
        extraDetails.add(Pair.of("authorization_header", theEx.authorizationHeader));
        extraDetails.addAll((theEx).extraDetailsForLogging);
        return ApiExceptionHandlerListenerResult.handleResponse(singletonError(projectApiErrors.getForbiddenApiError()), extraDetails);
    }
    if (ex instanceof MultipleMatchingEndpointsException) {
        MultipleMatchingEndpointsException theEx = (MultipleMatchingEndpointsException) ex;
        return ApiExceptionHandlerListenerResult.handleResponse(singletonError(projectApiErrors.getGenericServiceError()), Arrays.asList(Pair.of("incoming_request_path", theEx.requestPath), Pair.of("incoming_request_method", theEx.requestMethod), Pair.of("matching_endpoints", StringUtils.join(theEx.matchingEndpointsDetails, ","))));
    }
    if (ex instanceof PathParameterMatchingException) {
        PathParameterMatchingException theEx = (PathParameterMatchingException) ex;
        return ApiExceptionHandlerListenerResult.handleResponse(singletonError(projectApiErrors.getGenericServiceError()), Arrays.asList(Pair.of("path_template", theEx.pathTemplate), Pair.of("non_matching_uri_path", theEx.nonMatchingUriPath)));
    }
    if (ex instanceof InvalidCharsetInContentTypeHeaderException) {
        return ApiExceptionHandlerListenerResult.handleResponse(singletonError(projectApiErrors.getUnsupportedMediaTypeApiError()), singletonList(Pair.of("invalid_content_type_header", ((InvalidCharsetInContentTypeHeaderException) ex).invalidContentTypeHeader)));
    }
    if (ex instanceof TooManyOpenChannelsException) {
        TooManyOpenChannelsException theEx = (TooManyOpenChannelsException) ex;
        return ApiExceptionHandlerListenerResult.handleResponse(singletonError(projectApiErrors.getTemporaryServiceProblemApiError()), Arrays.asList(Pair.of("num_current_open_channels", String.valueOf(theEx.actualOpenChannelsCount)), Pair.of("max_open_channels_limit", String.valueOf(theEx.maxOpenChannelsLimit))));
    }
    if (ex instanceof IncompleteHttpCallTimeoutException) {
        IncompleteHttpCallTimeoutException theEx = (IncompleteHttpCallTimeoutException) ex;
        return ApiExceptionHandlerListenerResult.handleResponse(singletonError(new ApiErrorWithMetadata(projectApiErrors.getMalformedRequestApiError(), Pair.of("cause", "Unfinished/invalid HTTP request"))), Arrays.asList(Pair.of("incomplete_http_call_timeout_millis", String.valueOf(theEx.timeoutMillis)), Pair.of("exception_message", theEx.getMessage())));
    }
    if (ex instanceof InvalidHttpRequestException) {
        InvalidHttpRequestException theEx = (InvalidHttpRequestException) ex;
        Throwable cause = theEx.getCause();
        String causeAsString = cause == null ? "null" : cause.toString();
        return ApiExceptionHandlerListenerResult.handleResponse(singletonError(new ApiErrorWithMetadata(projectApiErrors.getMalformedRequestApiError(), Pair.of("cause", "Invalid HTTP request"))), Arrays.asList(Pair.of("exception_message", theEx.getMessage()), Pair.of("cause_details", causeAsString)));
    }
    return ApiExceptionHandlerListenerResult.ignoreResponse();
}
Also used : TooLongFrameException(io.netty.handler.codec.TooLongFrameException) HostnameResolutionException(com.nike.riposte.server.error.exception.HostnameResolutionException) ArrayList(java.util.ArrayList) Forbidden403Exception(com.nike.riposte.server.error.exception.Forbidden403Exception) TooManyOpenChannelsException(com.nike.riposte.server.error.exception.TooManyOpenChannelsException) IncompleteHttpCallTimeoutException(com.nike.riposte.server.error.exception.IncompleteHttpCallTimeoutException) RequestContentDeserializationException(com.nike.riposte.server.error.exception.RequestContentDeserializationException) MultipleMatchingEndpointsException(com.nike.riposte.server.error.exception.MultipleMatchingEndpointsException) ApiErrorWithMetadata(com.nike.backstopper.apierror.ApiErrorWithMetadata) PathParameterMatchingException(com.nike.riposte.server.error.exception.PathParameterMatchingException) Unauthorized401Exception(com.nike.riposte.server.error.exception.Unauthorized401Exception) Pair(com.nike.internal.util.Pair) NativeIoExceptionWrapper(com.nike.riposte.server.error.exception.NativeIoExceptionWrapper) InvalidCharsetInContentTypeHeaderException(com.nike.riposte.server.error.exception.InvalidCharsetInContentTypeHeaderException) PathNotFound404Exception(com.nike.riposte.server.error.exception.PathNotFound404Exception) InvalidHttpRequestException(com.nike.riposte.server.error.exception.InvalidHttpRequestException) CircuitBreakerException(com.nike.fastbreak.exception.CircuitBreakerException) NonblockingEndpointCompletableFutureTimedOut(com.nike.riposte.server.error.exception.NonblockingEndpointCompletableFutureTimedOut) DecoderException(io.netty.handler.codec.DecoderException) MethodNotAllowed405Exception(com.nike.riposte.server.error.exception.MethodNotAllowed405Exception) DownstreamChannelClosedUnexpectedlyException(com.nike.riposte.server.error.exception.DownstreamChannelClosedUnexpectedlyException) ApiError(com.nike.backstopper.apierror.ApiError) DownstreamIdleChannelTimeoutException(com.nike.riposte.server.error.exception.DownstreamIdleChannelTimeoutException)

Aggregations

ApiError (com.nike.backstopper.apierror.ApiError)7 Test (org.junit.Test)4 ApiErrorBase (com.nike.backstopper.apierror.ApiErrorBase)2 ApiErrorWithMetadata (com.nike.backstopper.apierror.ApiErrorWithMetadata)2 DefaultErrorContractDTO (com.nike.backstopper.model.DefaultErrorContractDTO)2 DefaultErrorDTO (com.nike.backstopper.model.DefaultErrorDTO)2 Pair (com.nike.internal.util.Pair)2 ProjectApiErrors (com.nike.backstopper.apierror.projectspecificinfo.ProjectApiErrors)1 SampleCoreApiError (com.nike.backstopper.apierror.sample.SampleCoreApiError)1 CircuitBreakerException (com.nike.fastbreak.exception.CircuitBreakerException)1 DownstreamChannelClosedUnexpectedlyException (com.nike.riposte.server.error.exception.DownstreamChannelClosedUnexpectedlyException)1 DownstreamIdleChannelTimeoutException (com.nike.riposte.server.error.exception.DownstreamIdleChannelTimeoutException)1 Forbidden403Exception (com.nike.riposte.server.error.exception.Forbidden403Exception)1 HostnameResolutionException (com.nike.riposte.server.error.exception.HostnameResolutionException)1 IncompleteHttpCallTimeoutException (com.nike.riposte.server.error.exception.IncompleteHttpCallTimeoutException)1 InvalidCharsetInContentTypeHeaderException (com.nike.riposte.server.error.exception.InvalidCharsetInContentTypeHeaderException)1 InvalidHttpRequestException (com.nike.riposte.server.error.exception.InvalidHttpRequestException)1 MethodNotAllowed405Exception (com.nike.riposte.server.error.exception.MethodNotAllowed405Exception)1 MultipleMatchingEndpointsException (com.nike.riposte.server.error.exception.MultipleMatchingEndpointsException)1 NativeIoExceptionWrapper (com.nike.riposte.server.error.exception.NativeIoExceptionWrapper)1