@Test public void should_return_expected_response_when_chunked_request_not_exceeding_global_request_size() throws Exception { NettyHttpClientRequestBuilder request = request() .withMethod(HttpMethod.POST) .withUri(BasicEndpoint.MATCHING_PATH) .withPaylod(generatePayloadOfSizeInBytes(GLOBAL_MAX_REQUEST_SIZE)) .withHeader(HttpHeaders.Names.TRANSFER_ENCODING, CHUNKED); // when NettyHttpClientResponse serverResponse = request.execute(serverConfig.endpointsPort(), incompleteCallTimeoutMillis); // then assertThat(serverResponse.statusCode).isEqualTo(HttpResponseStatus.OK.code()); assertThat(serverResponse.payload).isEqualTo(BasicEndpoint.RESPONSE_PAYLOAD); }
@Test public void should_return_expected_response_when_chunked_request_not_exceeding_endpoint_overridden_request_size() throws Exception { NettyHttpClientRequestBuilder request = request() .withMethod(HttpMethod.POST) .withUri(BasicEndpointWithRequestSizeValidationOverride.MATCHING_PATH) .withPaylod(generatePayloadOfSizeInBytes(BasicEndpointWithRequestSizeValidationOverride.MAX_REQUEST_SIZE)) .withHeader(HttpHeaders.Names.TRANSFER_ENCODING, CHUNKED); // when NettyHttpClientResponse serverResponse = request.execute(serverConfig.endpointsPort(), incompleteCallTimeoutMillis); // then assertThat(serverResponse.statusCode).isEqualTo(HttpResponseStatus.OK.code()); assertThat(serverResponse.payload).isEqualTo(BasicEndpointWithRequestSizeValidationOverride.RESPONSE_PAYLOAD); }
@Test public void should_return_expected_response_when_endpoint_disabled_chunked_request_size_validation() throws Exception { NettyHttpClientRequestBuilder request = request() .withMethod(HttpMethod.POST) .withUri(BasicEndpointWithRequestSizeValidationDisabled.MATCHING_PATH) .withPaylod(generatePayloadOfSizeInBytes(GLOBAL_MAX_REQUEST_SIZE + 100)) .withHeader(HttpHeaders.Names.TRANSFER_ENCODING, CHUNKED); // when NettyHttpClientResponse serverResponse = request.execute(serverConfig.endpointsPort(), incompleteCallTimeoutMillis); // then assertThat(serverResponse.statusCode).isEqualTo(HttpResponseStatus.OK.code()); assertThat(serverResponse.payload).isEqualTo(BasicEndpointWithRequestSizeValidationDisabled.RESPONSE_PAYLOAD); }
@Test public void should_return_bad_request_when_chunked_request_exceeds_endpoint_overridden_configured_max_request_size() throws Exception { NettyHttpClientRequestBuilder request = request() .withMethod(HttpMethod.POST) .withUri(BasicEndpointWithRequestSizeValidationOverride.MATCHING_PATH) .withPaylod(generatePayloadOfSizeInBytes(BasicEndpointWithRequestSizeValidationOverride.MAX_REQUEST_SIZE + 1)) .withHeader(HttpHeaders.Names.TRANSFER_ENCODING, CHUNKED); // when NettyHttpClientResponse serverResponse = request.execute(serverConfig.endpointsPort(), incompleteCallTimeoutMillis); // then assertThat(serverResponse.statusCode).isEqualTo(HttpResponseStatus.BAD_REQUEST.code()); assertBadRequestErrorMessageAndMetadata(serverResponse.payload); }
@Test public void should_return_bad_request_when_chunked_request_exceeds_global_configured_max_request_size() throws Exception { NettyHttpClientRequestBuilder request = request() .withMethod(HttpMethod.POST) .withUri(BasicEndpoint.MATCHING_PATH) .withPaylod(generatePayloadOfSizeInBytes(GLOBAL_MAX_REQUEST_SIZE + 1)) .withHeader(HttpHeaders.Names.TRANSFER_ENCODING, CHUNKED); // when NettyHttpClientResponse serverResponse = request.execute(serverConfig.endpointsPort(), incompleteCallTimeoutMillis); // then assertThat(serverResponse.statusCode).isEqualTo(HttpResponseStatus.BAD_REQUEST.code()); assertBadRequestErrorMessageAndMetadata(serverResponse.payload); }
@DataProvider(value = { "GZIP", "DEFLATE", "IDENTITY" }, splitBy = "\\|") @Test public void response_should_not_be_compressed_when_ResponseInfo_disables_compression( CompressionType compressionType ) throws Exception { // given NettyHttpClientRequestBuilder request = request() .withMethod(HttpMethod.GET) .withUri(BasicEndpoint.MATCHING_PATH) .withHeader(ACCEPT_ENCODING, compressionType.contentEncodingHeaderValue) .withHeader(BasicEndpoint.DESIRED_UNCOMPRESSED_PAYLOAD_SIZE_HEADER_KEY, 1000) .withHeader(BasicEndpoint.DISABLE_COMPRESSION_HEADER_KEY, "true"); // when NettyHttpClientResponse serverResponse = request.execute(serverConfig.endpointsPort(), incompleteCallTimeoutMillis); // then assertThat(serverResponse.statusCode).isEqualTo(HttpResponseStatus.OK.code()); assertThat(serverResponse.headers.get(CONTENT_ENCODING)).isNull(); assertThat(serverResponse.payload).hasSize(1000); assertThat(serverResponse.payload).startsWith(BasicEndpoint.RESPONSE_PAYLOAD_PREFIX); }
) throws Exception { NettyHttpClientRequestBuilder request = request() .withMethod(HttpMethod.GET) .withUri(BasicEndpoint.MATCHING_PATH)
@DataProvider(value = { "STANDARD_ENDPOINT", "PROXY_ENDPOINT" }) @Test public void endpoints_should_be_reachable_with_barely_valid_initial_line_length_values( EndpointTypeScenario scenario ) throws Exception { // given String barelyAcceptableUri = generateUriForInitialLineLength( HttpMethod.GET, scenario.matchingPathBase, CUSTOM_REQUEST_DECODER_CONFIG.maxInitialLineLength() ); Pair<String, Object> barelyAcceptableHeader = generateHeaderForHeaderLineLength(CUSTOM_REQUEST_DECODER_CONFIG.maxHeaderSize()); NettyHttpClientRequestBuilder request = request() .withMethod(HttpMethod.GET) .withUri(barelyAcceptableUri) .withHeaders(barelyAcceptableHeader); // when NettyHttpClientResponse serverResponse = request.execute(scenario.serverPort, incompleteCallTimeoutMillis); // then assertThat(serverResponse.statusCode).isEqualTo(200); assertThat(serverResponse.payload).isEqualTo(scenario.successfulResponsePayload); }
NettyHttpClientRequestBuilder request = request() .withMethod(scenario.requestMethod) .withUri(RouterEndpoint.MATCHING_PATH)
@DataProvider(value = { "STANDARD_ENDPOINT", "PROXY_ENDPOINT" }) @Test public void endpoints_should_throw_decode_exception_for_initial_line_length_that_is_too_long( EndpointTypeScenario scenario ) throws Exception { // given String tooLongUri = generateUriForInitialLineLength( HttpMethod.GET, scenario.matchingPathBase, CUSTOM_REQUEST_DECODER_CONFIG.maxInitialLineLength() + 1 ); NettyHttpClientRequestBuilder request = request() .withMethod(HttpMethod.GET) .withUri(tooLongUri); // when NettyHttpClientResponse serverResponse = request.execute(scenario.serverPort, incompleteCallTimeoutMillis); // then assertTooLongFrameErrorResponse(serverResponse, EXPECTED_TOO_LONG_FRAME_LINE_API_ERROR); // The EXPECTED_TOO_LONG_FRAME_LINE_API_ERROR check above should have verified 400 status code, but do a // sanity check here just for test readability. assertThat(serverResponse.statusCode).isEqualTo(400); }
@DataProvider(value = { "STANDARD_ENDPOINT", "PROXY_ENDPOINT" }) @Test public void endpoints_should_handle_decode_exception_for_invalid_http_request( EndpointTypeScenario scenario ) throws Exception { // given int payloadSize = CUSTOM_REQUEST_DECODER_CONFIG.maxInitialLineLength() + 1; String payload = generatePayload(payloadSize); //leave off content-length and transfer-encoding headers to trigger DecoderFailedResult NettyHttpClientRequestBuilder request = request() .withMethod(HttpMethod.POST) .withUri(scenario.matchingPathBase) .withPaylod(payload); // when NettyHttpClientResponse serverResponse = request.execute(scenario.serverPort, incompleteCallTimeoutMillis); // then assertTooLongFrameErrorResponse(serverResponse, EXPECTED_TOO_LONG_FRAME_LINE_API_ERROR); // The EXPECTED_TOO_LONG_FRAME_LINE_API_ERROR check above should have verified 400 status code, but do a // sanity check here just for test readability. assertThat(serverResponse.statusCode).isEqualTo(400); }
@DataProvider(value = { "STANDARD_ENDPOINT", "PROXY_ENDPOINT" }) @Test public void endpoints_should_throw_decode_exception_for_single_header_that_is_too_long( EndpointTypeScenario scenario ) throws Exception { // given Pair<String, Object> tooLongHeader = generateHeaderForHeaderLineLength( CUSTOM_REQUEST_DECODER_CONFIG.maxHeaderSize() + 1 ); NettyHttpClientRequestBuilder request = request() .withMethod(HttpMethod.GET) .withUri(scenario.matchingPathBase) .withHeaders(tooLongHeader); // when NettyHttpClientResponse serverResponse = request.execute(scenario.serverPort, incompleteCallTimeoutMillis); // then assertTooLongFrameErrorResponse(serverResponse, EXPECTED_TOO_LONG_FRAME_HEADER_API_ERROR); // The EXPECTED_TOO_LONG_FRAME_HEADER_API_ERROR check above should have verified 431 status code, but do a // sanity check here just for test readability. assertThat(serverResponse.statusCode).isEqualTo(431); }
@DataProvider(value = { "STANDARD_ENDPOINT", "PROXY_ENDPOINT" }) @Test public void endpoints_should_throw_decode_exception_for_multiple_headers_that_are_too_long_when_summed( EndpointTypeScenario scenario ) throws Exception { // given Pair<String, Object> halfMaxLengthHeader = generateHeaderForHeaderLineLength( "foo", CUSTOM_REQUEST_DECODER_CONFIG.maxHeaderSize() / 2 ); Pair<String, Object> halfMaxLengthHeaderPlusOne = generateHeaderForHeaderLineLength( "bar", (CUSTOM_REQUEST_DECODER_CONFIG.maxHeaderSize() / 2) + 1 ); NettyHttpClientRequestBuilder request = request() .withMethod(HttpMethod.GET) .withUri(scenario.matchingPathBase) .withHeaders(halfMaxLengthHeader, halfMaxLengthHeaderPlusOne); // when NettyHttpClientResponse serverResponse = request.execute(scenario.serverPort, incompleteCallTimeoutMillis); // then assertTooLongFrameErrorResponse(serverResponse, EXPECTED_TOO_LONG_FRAME_HEADER_API_ERROR); // The EXPECTED_TOO_LONG_FRAME_HEADER_API_ERROR check above should have verified 431 status code, but do a // sanity check here just for test readability. assertThat(serverResponse.statusCode).isEqualTo(431); }
String origRequestPayload = generatePayload(payloadSize); NettyHttpClientRequestBuilder request = request() .withMethod(HttpMethod.POST) .withUri(RouterEndpoint.MATCHING_PATH)
@Test public void invalid_http_call_with_invalid_URL_escaping_should_result_in_expected_400_error() throws Exception { // given // Incorrectly escaped URLs cause a blowup in RequestInfoImpl when it tries to decode the URL. We can trigger // this by putting a % character that is not followed by a proper escape sequence. NettyHttpClientRequestBuilder request = request() .withMethod(HttpMethod.GET) .withUri("%notAnEscapeSequence"); // when NettyHttpClientResponse response = request.execute(downstreamServerConfig.endpointsPort(), 3000); // then verifyErrorReceived(response.payload, response.statusCode, new ApiErrorWithMetadata(SampleCoreApiError.MALFORMED_REQUEST, Pair.of("cause", "Invalid HTTP request")) ); }
) throws Exception { NettyHttpClientRequestBuilder request = request() .withMethod(HttpMethod.POST) .withUri(RouterEndpoint.MATCHING_PATH)
@Test public void http_call_with_bad_content_type_header_should_result_in_expected_400_error() throws Exception { // given NettyHttpClientRequestBuilder request = request() .withMethod(HttpMethod.GET) .withUri(BasicEndpoint.MATCHING_PATH) .withHeader(HttpHeaderNames.CONTENT_TYPE.toString(), ";"); // when NettyHttpClientResponse response = request.execute(downstreamServerConfig.endpointsPort(), 3000); // then verifyErrorReceived(response.payload, response.statusCode, new ApiErrorWithMetadata(SampleCoreApiError.MALFORMED_REQUEST, Pair.of("cause", "Invalid HTTP request")) ); }
/** * This test verifies the corner case where the downstream system short circuits and returns a response before * it receives the full request (i.e. the request chunks are still streaming when the response is returned). * This should cause request chunks to fail to stream with an error, but the downstream response should still * be returned to the original caller successfully. */ @Test public void proxy_endpoints_should_successfully_return_short_circuited_downstream_response() throws Exception { // Do this test a bunch of times to try and catch all the race condition possibilities. for (int i = 0; i < 20; i++) { // given int payloadSize = 1024 * 1000; String payload = generatePayload(payloadSize); NettyHttpClientRequestBuilder request = request() .withMethod(HttpMethod.POST) .withUri(RouterEndpointForwardingToShortCircuitError.MATCHING_PATH) .withPaylod(payload) .withHeader(HttpHeaders.Names.CONTENT_LENGTH, payloadSize); // when NettyHttpClientResponse serverResponse = request.execute(proxyServerConfig.endpointsPort(), 3000); // then verifyErrorReceived(serverResponse.payload, serverResponse.statusCode, FAIL_FAST_API_ERROR); } }
@Test public void invalid_http_call_that_causes_Netty_DecoderFailure_should_result_in_expected_400_error() throws Exception { // given // Normal request, but fiddle with the first chunk as it's going out to remove the HTTP version and make it an // invalid HTTP call. This will cause Netty to mark the HttpRequest with a DecoderFailure. NettyHttpClientRequestBuilder request = request() .withMethod(HttpMethod.GET) .withUri(BasicEndpoint.MATCHING_PATH) .withPipelineAdjuster( p -> p.addFirst(new ChannelOutboundHandlerAdapter() { @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { String msgAsString = ((ByteBuf)msg).toString(CharsetUtil.UTF_8); if (msgAsString.contains("HTTP/1.1")) { msg = Unpooled.copiedBuffer(msgAsString.replace("HTTP/1.1", ""), CharsetUtil.UTF_8); } super.write(ctx, msg, promise); } }) ); // when NettyHttpClientResponse response = request.execute(downstreamServerConfig.endpointsPort(), 3000); // then verifyErrorReceived(response.payload, response.statusCode, new ApiErrorWithMetadata(SampleCoreApiError.MALFORMED_REQUEST, Pair.of("cause", "Invalid HTTP request")) ); }