/* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package software.amazon.awssdk.http.apache; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.any; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static software.amazon.awssdk.http.SdkHttpConfigurationOption.TRUST_ALL_CERTIFICATES; import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.junit.WireMockRule; import com.github.tomakehurst.wiremock.matching.RequestPatternBuilder; import java.io.IOException; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.URI; import java.net.UnknownHostException; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.Credentials; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.conn.DnsResolver; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.routing.HttpRoute; import org.apache.http.impl.conn.SystemDefaultDnsResolver; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import software.amazon.awssdk.http.HttpExecuteRequest; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.SdkHttpClientTestSuite; import software.amazon.awssdk.http.SdkHttpFullRequest; import software.amazon.awssdk.http.SdkHttpMethod; import software.amazon.awssdk.http.apache.internal.ApacheHttpRequestConfig; import software.amazon.awssdk.http.apache.internal.impl.ConnectionManagerAwareHttpClient; import software.amazon.awssdk.utils.AttributeMap; @RunWith(MockitoJUnitRunner.class) public class ApacheHttpClientWireMockTest extends SdkHttpClientTestSuite { @Rule public WireMockRule mockProxyServer = new WireMockRule(wireMockConfig().dynamicPort().dynamicHttpsPort()); @Mock private ConnectionManagerAwareHttpClient httpClient; @Mock private HttpClientConnectionManager connectionManager; @Override protected SdkHttpClient createSdkHttpClient(SdkHttpClientOptions options) { ApacheHttpClient.Builder builder = ApacheHttpClient.builder(); AttributeMap.Builder attributeMap = AttributeMap.builder(); if (options.tlsTrustManagersProvider() != null) { builder.tlsTrustManagersProvider(options.tlsTrustManagersProvider()); } if (options.trustAll()) { attributeMap.put(TRUST_ALL_CERTIFICATES, options.trustAll()); } return builder.buildWithDefaults(attributeMap.build()); } @Test public void closeClient_shouldCloseUnderlyingResources() { ApacheHttpClient client = new ApacheHttpClient(httpClient, ApacheHttpRequestConfig.builder().build(), AttributeMap.empty()); when(httpClient.getHttpClientConnectionManager()).thenReturn(connectionManager); client.close(); verify(connectionManager).shutdown(); } @Test public void routePlannerIsInvoked() throws Exception { mockProxyServer.resetToDefaultMappings(); mockProxyServer.addStubMapping(WireMock.any(urlPathEqualTo("/")) .willReturn(aResponse().proxiedFrom("http://localhost:" + mockServer.port())) .build()); SdkHttpClient client = ApacheHttpClient.builder() .httpRoutePlanner( (host, request, context) -> new HttpRoute( new HttpHost("localhost", mockProxyServer.httpsPort(), "https") ) ) .buildWithDefaults(AttributeMap.builder() .put(TRUST_ALL_CERTIFICATES, Boolean.TRUE) .build()); testForResponseCodeUsingHttps(client, HttpURLConnection.HTTP_OK); mockProxyServer.verify(1, RequestPatternBuilder.allRequests()); } @Test public void credentialPlannerIsInvoked() throws Exception { mockProxyServer.addStubMapping(WireMock.any(urlPathEqualTo("/")) .willReturn(aResponse() .withHeader("WWW-Authenticate", "Basic realm=\"proxy server\"") .withStatus(401)) .build()); mockProxyServer.addStubMapping(WireMock.any(urlPathEqualTo("/")) .withBasicAuth("foo", "bar") .willReturn(aResponse() .proxiedFrom("http://localhost:" + mockServer.port())) .build()); SdkHttpClient client = ApacheHttpClient.builder() .credentialsProvider(new CredentialsProvider() { @Override public void setCredentials(AuthScope authScope, Credentials credentials) { } @Override public Credentials getCredentials(AuthScope authScope) { return new UsernamePasswordCredentials("foo", "bar"); } @Override public void clear() { } }) .httpRoutePlanner( (host, request, context) -> new HttpRoute( new HttpHost("localhost", mockProxyServer.httpsPort(), "https") ) ) .buildWithDefaults(AttributeMap.builder() .put(TRUST_ALL_CERTIFICATES, Boolean.TRUE) .build()); testForResponseCodeUsingHttps(client, HttpURLConnection.HTTP_OK); mockProxyServer.verify(2, RequestPatternBuilder.allRequests()); } @Test public void overrideDnsResolver_WithDnsMatchingResolver_successful() throws Exception { overrideDnsResolver("magic.local.host"); } @Test(expected = UnknownHostException.class) public void overrideDnsResolver_WithUnknownHost_throwsException() throws Exception { overrideDnsResolver("sad.local.host"); } @Test public void overrideDnsResolver_WithLocalhost_successful() throws Exception { overrideDnsResolver("localhost"); } @Test public void explicitNullDnsResolver_WithLocalhost_successful() throws Exception { overrideDnsResolver("localhost", true); } private void overrideDnsResolver(String hostName) throws IOException { overrideDnsResolver(hostName, false); } private void overrideDnsResolver(String hostName, boolean nullifyResolver) throws IOException { DnsResolver dnsResolver = new SystemDefaultDnsResolver() { @Override public InetAddress[] resolve(final String host) throws UnknownHostException { if (host.equalsIgnoreCase("magic.local.host")) { return new InetAddress[] { InetAddress.getByName("127.0.0.1") }; } else { return super.resolve(host); } } }; if (nullifyResolver) { dnsResolver = null; } SdkHttpClient client = ApacheHttpClient.builder() .dnsResolver(dnsResolver) .buildWithDefaults(AttributeMap.builder() .put(TRUST_ALL_CERTIFICATES, Boolean.TRUE) .build()); mockProxyServer.resetToDefaultMappings(); mockProxyServer.stubFor(any(urlPathEqualTo("/")).willReturn(aResponse().withStatus(HttpURLConnection.HTTP_OK))); URI uri = URI.create("https://" + hostName + ":" + mockProxyServer.httpsPort()); SdkHttpFullRequest req = SdkHttpFullRequest.builder() .uri(uri) .method(SdkHttpMethod.POST) .putHeader("Host", uri.getHost()) .build(); client.prepareRequest(HttpExecuteRequest.builder() .request(req) .contentStreamProvider(req.contentStreamProvider().orElse(null)) .build()) .call(); mockProxyServer.verify(1, RequestPatternBuilder.allRequests()); } }