Migrated Docker based integration tests to TestContainers

This commit is contained in:
Oleg Kalnichevski 2024-10-14 11:19:12 +02:00
parent cc6901798f
commit 0335839e22
32 changed files with 1622 additions and 1222 deletions

View File

@ -49,4 +49,4 @@ jobs:
distribution: 'temurin'
java-version: ${{ matrix.java }}
- name: Build with Maven
run: mvn -V --file pom.xml --no-transfer-progress -DtrimStackTrace=false -P-use-toolchains
run: mvn -V --file pom.xml --no-transfer-progress -DtrimStackTrace=false -P-use-toolchains,docker

View File

@ -1,16 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License 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.
*/*/.svn

View File

@ -1,45 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License 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.
FROM httpd:2.4
MAINTAINER dev@hc.apache.org
ENV httpd_home /usr/local/apache2
ENV var_dir /var/httpd
ENV www_dir ${var_dir}/www
ENV private_dir ${www_dir}/private
RUN apt-get update
RUN apt-get install -y subversion
RUN mkdir -p ${var_dir}
RUN svn co --depth immediates http://svn.apache.org/repos/asf/httpcomponents/site ${www_dir}
RUN svn up --set-depth infinity ${www_dir}/images
RUN svn up --set-depth infinity ${www_dir}/css
RUN mkdir ${httpd_home}/ssl
COPY server-cert.pem ${httpd_home}/ssl/
COPY server-key.pem ${httpd_home}/ssl/
COPY httpd.conf ${httpd_home}/conf/
COPY httpd-ssl.conf ${httpd_home}/conf/extra/
RUN mkdir -p ${private_dir}
# user: testuser; pwd: nopassword
RUN echo "testuser:{SHA}0Ybo2sSKJNARW1aNCrLJ6Lguats=" > ${private_dir}/.htpasswd
RUN echo "testuser:Restricted Files:73deccd22e07066db8c405e5364335f5" > ${private_dir}/.htpasswd_digest
RUN echo "Big Secret" > ${private_dir}/big-secret.txt
EXPOSE 8080
EXPOSE 8443

View File

@ -1,32 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License 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.
version: '3.5'
services:
test-httpd:
container_name: "my-httpclient-tests-httpd"
image: "httpclient-tests-httpd:latest"
ports:
- "8080:8080"
- "8443:8443"
test-squid:
container_name: "my-httpclient-tests-squid"
image: "httpclient-tests-squid:latest"
ports:
- "8888:8888"
- "8889:8889"
links:
- "test-httpd"

View File

@ -1,29 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License 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.
FROM sameersbn/squid:3.3.8-22
MAINTAINER dev@hc.apache.org
ENV conf_dir /etc/squid3
RUN apt-get update
RUN apt-get install -y apache2-utils
COPY squid.conf ${conf_dir}/
RUN htpasswd -b -c ${conf_dir}/htpasswd squid nopassword
EXPOSE 8888
EXPOSE 8889

View File

@ -97,8 +97,43 @@
<artifactId>rxjava</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>docker</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${hc.surefire.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<reporting>
<plugins>
<plugin>

View File

@ -0,0 +1,326 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.compatibility;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.testing.compatibility.async.CachingHttpAsyncClientCompatibilityTest;
import org.apache.hc.client5.testing.compatibility.async.HttpAsyncClientCompatibilityTest;
import org.apache.hc.client5.testing.compatibility.async.HttpAsyncClientHttp1CompatibilityTest;
import org.apache.hc.client5.testing.compatibility.sync.CachingHttpClientCompatibilityTest;
import org.apache.hc.client5.testing.compatibility.sync.HttpClientCompatibilityTest;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.URIScheme;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
@Testcontainers(disabledWithoutDocker = true)
class ApacheHTTPDSquidCompatibilityIT {
private static Network NETWORK = Network.newNetwork();
@Container
static final GenericContainer<?> HTTPD_CONTAINER = ContainerImages.apacheHttpD(NETWORK);
@Container
static final GenericContainer<?> SQUID = ContainerImages.squid(NETWORK);
static HttpHost targetContainerHost() {
return new HttpHost(URIScheme.HTTP.id, HTTPD_CONTAINER.getHost(), HTTPD_CONTAINER.getMappedPort(ContainerImages.HTTP_PORT));
}
static HttpHost targetInternalHost() {
return new HttpHost(URIScheme.HTTP.id, ContainerImages.WEB_SERVER, ContainerImages.HTTP_PORT);
}
static HttpHost targetContainerTlsHost() {
return new HttpHost(URIScheme.HTTPS.id, HTTPD_CONTAINER.getHost(), HTTPD_CONTAINER.getMappedPort(ContainerImages.HTTPS_PORT));
}
static HttpHost targetInternalTlsHost() {
return new HttpHost(URIScheme.HTTPS.id, ContainerImages.WEB_SERVER, ContainerImages.HTTPS_PORT);
}
static HttpHost proxyContainerHost() {
return new HttpHost(URIScheme.HTTP.id, SQUID.getHost(), SQUID.getMappedPort(ContainerImages.PROXY_PORT));
}
static HttpHost proxyPwProtectedContainerHost() {
return new HttpHost(URIScheme.HTTP.id, SQUID.getHost(), SQUID.getMappedPort(ContainerImages.PROXY_PW_PROTECTED_PORT));
}
@AfterAll
static void cleanup() {
SQUID.close();
HTTPD_CONTAINER.close();
NETWORK.close();
}
@Nested
@DisplayName("Classic client: HTTP/1.1, plain, direct connection")
class ClassicDirectHttp extends HttpClientCompatibilityTest {
public ClassicDirectHttp() throws Exception {
super(targetContainerHost(), null, null);
}
}
@Nested
@DisplayName("Classic client: HTTP/1.1, plain, connection via proxy")
class ClassicViaProxyHttp extends HttpClientCompatibilityTest {
public ClassicViaProxyHttp() throws Exception {
super(targetInternalHost(), proxyContainerHost(), null);
}
}
@Nested
@DisplayName("Classic client: HTTP/1.1, plain, connection via password protected proxy")
class ClassicViaPwProtectedProxyHttp extends HttpClientCompatibilityTest {
public ClassicViaPwProtectedProxyHttp() throws Exception {
super(targetInternalHost(), proxyPwProtectedContainerHost(), new UsernamePasswordCredentials("squid", "nopassword".toCharArray()));
}
}
@Nested
@DisplayName("Classic client: HTTP/1.1, TLS, direct connection")
class ClassicDirectHttpTls extends HttpClientCompatibilityTest {
public ClassicDirectHttpTls() throws Exception {
super(targetContainerTlsHost(), null, null);
}
}
@Nested
@DisplayName("Classic client: HTTP/1.1, TLS, connection via proxy (tunnel)")
class ClassicViaProxyHttpTls extends HttpClientCompatibilityTest {
public ClassicViaProxyHttpTls() throws Exception {
super(targetInternalTlsHost(), proxyContainerHost(), null);
}
}
@Nested
@DisplayName("Classic client: HTTP/1.1, TLS, connection via password protected proxy (tunnel)")
class ClassicViaPwProtectedProxyHttpTls extends HttpClientCompatibilityTest {
public ClassicViaPwProtectedProxyHttpTls() throws Exception {
super(targetInternalTlsHost(), proxyPwProtectedContainerHost(), new UsernamePasswordCredentials("squid", "nopassword".toCharArray()));
}
}
@Nested
@DisplayName("Async client: HTTP/1.1, plain, direct connection")
class AsyncDirectHttp1 extends HttpAsyncClientHttp1CompatibilityTest {
public AsyncDirectHttp1() throws Exception {
super(targetContainerHost(), null, null);
}
}
@Nested
@DisplayName("Async client: HTTP/1.1, plain, connection via proxy")
class AsyncViaProxyHttp1 extends HttpAsyncClientHttp1CompatibilityTest {
public AsyncViaProxyHttp1() throws Exception {
super(targetInternalHost(), proxyContainerHost(), null);
}
}
@Nested
@DisplayName("Async client: HTTP/1.1, plain, connection via password protected proxy")
class AsyncViaPwProtectedProxyHttp1 extends HttpAsyncClientHttp1CompatibilityTest {
public AsyncViaPwProtectedProxyHttp1() throws Exception {
super(targetInternalHost(), proxyPwProtectedContainerHost(), new UsernamePasswordCredentials("squid", "nopassword".toCharArray()));
}
}
@Nested
@DisplayName("Async client: HTTP/1.1, TLS, direct connection")
class AsyncDirectHttp1Tls extends HttpAsyncClientHttp1CompatibilityTest {
public AsyncDirectHttp1Tls() throws Exception {
super(targetContainerTlsHost(), null, null);
}
}
@Nested
@DisplayName("Async client: HTTP/1.1, TLS, connection via proxy (tunnel)")
class AsyncViaProxyHttp1Tls extends HttpAsyncClientHttp1CompatibilityTest {
public AsyncViaProxyHttp1Tls() throws Exception {
super(targetInternalTlsHost(), proxyContainerHost(), null);
}
}
@Nested
@DisplayName("Async client: HTTP/1.1, TLS, connection via password protected proxy (tunnel)")
class AsyncViaPwProtectedProxyHttp1Tls extends HttpAsyncClientHttp1CompatibilityTest {
public AsyncViaPwProtectedProxyHttp1Tls() throws Exception {
super(targetInternalTlsHost(), proxyPwProtectedContainerHost(), new UsernamePasswordCredentials("squid", "nopassword".toCharArray()));
}
}
@Nested
@DisplayName("Async client: HTTP/2, plain, direct connection")
class AsyncDirectHttp2 extends HttpAsyncClientCompatibilityTest {
public AsyncDirectHttp2() throws Exception {
super(HttpVersionPolicy.FORCE_HTTP_2, targetContainerHost(), null, null);
}
}
@Nested
@DisplayName("Async client: HTTP/2, TLS, direct connection")
class AsyncDirectHttp2Tls extends HttpAsyncClientCompatibilityTest {
public AsyncDirectHttp2Tls() throws Exception {
super(HttpVersionPolicy.FORCE_HTTP_2, targetContainerTlsHost(), null, null);
}
}
@Nested
@DisplayName("Async client: HTTP/2, TLS, connection via proxy (tunnel)")
class AsyncViaProxyHttp2Tls extends HttpAsyncClientCompatibilityTest {
public AsyncViaProxyHttp2Tls() throws Exception {
super(HttpVersionPolicy.FORCE_HTTP_2, targetInternalTlsHost(), proxyContainerHost(), null);
}
}
@Nested
@DisplayName("Async client: HTTP/2, TLS, connection via password protected proxy (tunnel)")
class AsyncViaPwProtectedProxyHttp2Tls extends HttpAsyncClientCompatibilityTest {
public AsyncViaPwProtectedProxyHttp2Tls() throws Exception {
super(HttpVersionPolicy.FORCE_HTTP_2, targetInternalTlsHost(), proxyPwProtectedContainerHost(), new UsernamePasswordCredentials("squid", "nopassword".toCharArray()));
}
}
@Nested
@DisplayName("Async client: protocol negotiate, TLS, connection via proxy (tunnel)")
class AsyncViaProxyHttpNegotiateTls extends HttpAsyncClientCompatibilityTest {
public AsyncViaProxyHttpNegotiateTls() throws Exception {
super(HttpVersionPolicy.NEGOTIATE, targetInternalTlsHost(), proxyContainerHost(), null);
}
}
@Nested
@DisplayName("Async client: protocol negotiate, TLS, connection via password protected proxy (tunnel)")
class AsyncViaPwProtectedProxyHttpNegotiateTls extends HttpAsyncClientCompatibilityTest {
public AsyncViaPwProtectedProxyHttpNegotiateTls() throws Exception {
super(HttpVersionPolicy.NEGOTIATE, targetInternalTlsHost(), proxyPwProtectedContainerHost(), new UsernamePasswordCredentials("squid", "nopassword".toCharArray()));
}
}
@Nested
@DisplayName("Classic client: HTTP/1.1, caching, plain, direct connection")
class ClassicCachingHttp extends CachingHttpClientCompatibilityTest {
public ClassicCachingHttp() throws Exception {
super(targetContainerHost());
}
}
@Nested
@DisplayName("Classic client: HTTP/1.1, caching, TLS, direct connection")
class ClassicCachingHttpTls extends CachingHttpClientCompatibilityTest {
public ClassicCachingHttpTls() throws Exception {
super(targetContainerTlsHost());
}
}
@Nested
@DisplayName("Async client: HTTP/1.1, caching, plain, direct connection")
class AsyncCachingHttp1 extends CachingHttpAsyncClientCompatibilityTest {
public AsyncCachingHttp1() throws Exception {
super(HttpVersionPolicy.FORCE_HTTP_1, targetContainerHost());
}
}
@Nested
@DisplayName("Async client: HTTP/1.1, caching, TLS, direct connection")
class AsyncCachingHttp1Tls extends CachingHttpAsyncClientCompatibilityTest {
public AsyncCachingHttp1Tls() throws Exception {
super(HttpVersionPolicy.FORCE_HTTP_1, targetContainerTlsHost());
}
}
@Nested
@DisplayName("Async client HTTP/2, caching, plain, direct connection")
class AsyncCachingHttp2 extends CachingHttpAsyncClientCompatibilityTest {
public AsyncCachingHttp2() throws Exception {
super(HttpVersionPolicy.FORCE_HTTP_2, targetContainerHost());
}
}
@Nested
@DisplayName("Async client: HTTP/2, caching, TLS, direct connection")
class AsyncCachingHttp2Tls extends CachingHttpAsyncClientCompatibilityTest {
public AsyncCachingHttp2Tls() throws Exception {
super(HttpVersionPolicy.FORCE_HTTP_2, targetContainerTlsHost());
}
}
}

View File

@ -0,0 +1,119 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.compatibility;
import java.nio.charset.StandardCharsets;
import java.util.Random;
import org.apache.hc.client5.http.utils.ByteArrayBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.output.Slf4jLogConsumer;
import org.testcontainers.images.builder.ImageFromDockerfile;
import org.testcontainers.images.builder.Transferable;
public final class ContainerImages {
private static final Logger LOG = LoggerFactory.getLogger(ContainerImages.class);
public final static String WEB_SERVER = "test-httpd";
public final static int HTTP_PORT = 8080;
public final static int HTTPS_PORT = 8443;
public final static String PROXY = "test-proxy";
public final static int PROXY_PORT = 8888;
public final static int PROXY_PW_PROTECTED_PORT = 8889;
static final byte[] BYTES = "0123456789ABCDEF".getBytes(StandardCharsets.UTF_8);
static byte[] randomData(final int max) {
final Random random = new Random();
random.setSeed(System.currentTimeMillis());
final int n = random.nextInt(max);
final ByteArrayBuilder builder = new ByteArrayBuilder();
for (int i = 0; i < n; i++) {
builder.append(BYTES);
}
return builder.toByteArray();
}
public static GenericContainer<?> apacheHttpD(final Network network) {
return new GenericContainer<>(new ImageFromDockerfile()
.withFileFromClasspath("server-cert.pem", "docker/server-cert.pem")
.withFileFromClasspath("server-key.pem", "docker/server-key.pem")
.withFileFromClasspath("httpd.conf", "docker/httpd/httpd.conf")
.withFileFromClasspath("httpd-ssl.conf", "docker/httpd/httpd-ssl.conf")
.withFileFromTransferable("111", Transferable.of(randomData(10240)))
.withFileFromTransferable("222", Transferable.of(randomData(10240)))
.withFileFromTransferable("333", Transferable.of(randomData(10240)))
.withDockerfileFromBuilder(builder ->
builder
.from("httpd:2.4")
.env("httpd_home", "/usr/local/apache2")
.env("var_dir", "/var/httpd")
.env("www_dir", "${var_dir}/www")
.env("private_dir", "${www_dir}/private")
.run("mkdir ${httpd_home}/ssl")
.copy("server-cert.pem", "${httpd_home}/ssl/")
.copy("server-key.pem", "${httpd_home}/ssl/")
.copy("httpd.conf", "${httpd_home}/conf/")
.copy("httpd-ssl.conf", "${httpd_home}/conf/extra/")
.copy("111", "${www_dir}/")
.copy("222", "${www_dir}/")
.copy("333", "${www_dir}/")
.run("mkdir -p ${private_dir}")
//# user: testuser; pwd: nopassword
.run("echo \"testuser:{SHA}0Ybo2sSKJNARW1aNCrLJ6Lguats=\" > ${private_dir}/.htpasswd")
.run("echo \"testuser:Restricted Files:73deccd22e07066db8c405e5364335f5\" > ${private_dir}/.htpasswd_digest")
.run("echo \"Big Secret\" > ${private_dir}/big-secret.txt")
.build()))
.withNetwork(network)
.withNetworkAliases(WEB_SERVER)
.withLogConsumer(new Slf4jLogConsumer(LOG))
.withExposedPorts(HTTP_PORT, HTTPS_PORT);
}
public static GenericContainer<?> squid(final Network network) {
return new GenericContainer<>(new ImageFromDockerfile()
.withFileFromClasspath("squid.conf", "docker/squid/squid.conf")
.withDockerfileFromBuilder(builder ->
builder
.from("sameersbn/squid:3.3.8-22")
.env("conf_dir", "/etc/squid3")
.copy("squid.conf", "${conf_dir}/")
//# user: squid; pwd: nopassword
.run("echo \"squid:\\$apr1\\$.5saX63T\\$cMSoCJPqEfUw9br6zBdSO0\" > ${conf_dir}/htpasswd")
.build()))
.withNetwork(network)
.withNetworkAliases(PROXY)
.withLogConsumer(new Slf4jLogConsumer(LOG))
.withExposedPorts(PROXY_PORT, PROXY_PW_PROTECTED_PORT);
}
}

View File

@ -0,0 +1,179 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.compatibility.async;
import java.util.concurrent.Future;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.cache.CacheResponseStatus;
import org.apache.hc.client5.http.cache.HttpCacheContext;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.cache.RequestCacheControl;
import org.apache.hc.client5.http.cache.ResponseCacheControl;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.cache.CacheConfig;
import org.apache.hc.client5.http.impl.cache.HeapResourceFactory;
import org.apache.hc.client5.testing.extension.async.CachingHttpAsyncClientResource;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.util.Timeout;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
public abstract class CachingHttpAsyncClientCompatibilityTest {
static final Timeout TIMEOUT = Timeout.ofSeconds(5);
private final HttpHost target;
@RegisterExtension
private final CachingHttpAsyncClientResource clientResource;
public CachingHttpAsyncClientCompatibilityTest(final HttpVersionPolicy versionPolicy, final HttpHost target) throws Exception {
this.target = target;
this.clientResource = new CachingHttpAsyncClientResource(versionPolicy);
this.clientResource.configure(builder -> builder
.setCacheConfig(CacheConfig.custom()
.setMaxObjectSize(10240 * 16)
.build())
.setResourceFactory(HeapResourceFactory.INSTANCE));
}
CloseableHttpAsyncClient client() {
return clientResource.client();
}
@Test
@Disabled
void test_options_ping() throws Exception {
final CloseableHttpAsyncClient client = client();
final HttpCacheContext context = HttpCacheContext.create();
final SimpleHttpRequest options = SimpleRequestBuilder.options()
.setHttpHost(target)
.setPath("*")
.build();
final Future<SimpleHttpResponse> future = client.execute(options, context, null);
final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
}
@Test
void test_get_from_cache() throws Exception {
final CloseableHttpAsyncClient client = client();
final String[] resources1 = {"/111", "/222"};
for (final String r: resources1) {
final SimpleHttpRequest httpGet1 = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath(r)
.build();
final HttpCacheContext context1 = HttpCacheContext.create();
final Future<SimpleHttpResponse> future1 = client.execute(httpGet1, context1, null);
final SimpleHttpResponse response1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
Assertions.assertEquals(HttpStatus.SC_OK, response1.getCode());
Assertions.assertEquals(CacheResponseStatus.CACHE_MISS, context1.getCacheResponseStatus());
final ResponseCacheControl responseCacheControl1 = context1.getResponseCacheControl();
Assertions.assertNotNull(responseCacheControl1);
if (!r.equals("/333")) {
Assertions.assertEquals(600, responseCacheControl1.getMaxAge());
}
final HttpCacheEntry cacheEntry1 = context1.getCacheEntry();
Assertions.assertNotNull(cacheEntry1);
final SimpleHttpRequest httpGet2 = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath(r)
.build();
final HttpCacheContext context2 = HttpCacheContext.create();
final Future<SimpleHttpResponse> future2 = client.execute(httpGet2, context2, null);
final SimpleHttpResponse response2 = future2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
Assertions.assertEquals(HttpStatus.SC_OK, response2.getCode());
Assertions.assertEquals(CacheResponseStatus.CACHE_HIT, context2.getCacheResponseStatus());
final ResponseCacheControl responseCacheControl2 = context2.getResponseCacheControl();
Assertions.assertNotNull(responseCacheControl2);
Assertions.assertEquals(600, responseCacheControl2.getMaxAge());
final HttpCacheEntry cacheEntry2 = context2.getCacheEntry();
Assertions.assertNotNull(cacheEntry2);
Assertions.assertSame(cacheEntry2, context1.getCacheEntry());
Thread.sleep(2000);
final SimpleHttpRequest httpGet3 = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath(r)
.build();
final HttpCacheContext context3 = HttpCacheContext.create();
context3.setRequestCacheControl(RequestCacheControl.builder()
.setMaxAge(0)
.build());
final Future<SimpleHttpResponse> future3 = client.execute(httpGet3, context3, null);
final SimpleHttpResponse response3 = future3.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
Assertions.assertEquals(HttpStatus.SC_OK, response3.getCode());
Assertions.assertEquals(CacheResponseStatus.VALIDATED, context3.getCacheResponseStatus());
final HttpCacheEntry cacheEntry3 = context3.getCacheEntry();
Assertions.assertNotNull(cacheEntry3);
Assertions.assertNotSame(cacheEntry3, context1.getCacheEntry());
}
final String[] resources2 = {"/333"};
for (final String r: resources2) {
final SimpleHttpRequest httpGet1 = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath(r)
.build();
final HttpCacheContext context1 = HttpCacheContext.create();
final Future<SimpleHttpResponse> future1 = client.execute(httpGet1, context1, null);
final SimpleHttpResponse response1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
Assertions.assertEquals(HttpStatus.SC_OK, response1.getCode());
Assertions.assertEquals(CacheResponseStatus.CACHE_MISS, context1.getCacheResponseStatus());
final ResponseCacheControl responseCacheControl1 = context1.getResponseCacheControl();
Assertions.assertNotNull(responseCacheControl1);
Assertions.assertEquals(-1, responseCacheControl1.getMaxAge());
final HttpCacheEntry cacheEntry1 = context1.getCacheEntry();
Assertions.assertNotNull(cacheEntry1);
final SimpleHttpRequest httpGet2 = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath(r)
.build();
final HttpCacheContext context2 = HttpCacheContext.create();
final Future<SimpleHttpResponse> future2 = client.execute(httpGet2, context2, null);
final SimpleHttpResponse response2 = future2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
Assertions.assertEquals(HttpStatus.SC_OK, response2.getCode());
Assertions.assertEquals(CacheResponseStatus.VALIDATED, context2.getCacheResponseStatus());
final ResponseCacheControl responseCacheControl2 = context2.getResponseCacheControl();
Assertions.assertNotNull(responseCacheControl2);
Assertions.assertEquals(-1, responseCacheControl2.getMaxAge());
final HttpCacheEntry cacheEntry2 = context2.getCacheEntry();
Assertions.assertNotNull(cacheEntry2);
Assertions.assertNotSame(cacheEntry2, context1.getCacheEntry());
}
}
}

View File

@ -0,0 +1,176 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.compatibility.async;
import java.util.concurrent.Future;
import org.apache.hc.client5.http.ContextBuilder;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.Credentials;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.testing.extension.async.HttpAsyncClientResource;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.HttpVersion;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.util.Timeout;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
public abstract class HttpAsyncClientCompatibilityTest {
static final Timeout TIMEOUT = Timeout.ofSeconds(5);
private final HttpVersionPolicy versionPolicy;
private final HttpHost target;
@RegisterExtension
private final HttpAsyncClientResource clientResource;
private final BasicCredentialsProvider credentialsProvider;
public HttpAsyncClientCompatibilityTest(
final HttpVersionPolicy versionPolicy,
final HttpHost target,
final HttpHost proxy,
final Credentials proxyCreds) throws Exception {
this.versionPolicy = versionPolicy;
this.target = target;
this.clientResource = new HttpAsyncClientResource(versionPolicy);
this.clientResource.configure(builder -> builder.setProxy(proxy));
this.credentialsProvider = new BasicCredentialsProvider();
if (proxy != null && proxyCreds != null) {
this.credentialsProvider.setCredentials(new AuthScope(proxy), proxyCreds);
}
}
CloseableHttpAsyncClient client() {
return clientResource.client();
}
HttpClientContext context() {
return ContextBuilder.create()
.useCredentialsProvider(credentialsProvider)
.build();
}
void addCredentials(final AuthScope authScope, final Credentials credentials) {
credentialsProvider.setCredentials(authScope, credentials);
}
void assertProtocolVersion(final HttpClientContext context) {
switch (versionPolicy) {
case FORCE_HTTP_1:
Assertions.assertEquals(HttpVersion.HTTP_1_1, context.getProtocolVersion());
break;
case FORCE_HTTP_2:
case NEGOTIATE:
Assertions.assertEquals(HttpVersion.HTTP_2, context.getProtocolVersion());
break;
default:
throw new IllegalStateException("Unexpected version policy: " + versionPolicy);
}
}
@Test
void test_sequential_gets() throws Exception {
final CloseableHttpAsyncClient client = client();
final HttpClientContext context = context();
final String[] requestUris = new String[] {"/111", "/222", "/333"};
for (final String requestUri: requestUris) {
final SimpleHttpRequest httpGet = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath(requestUri)
.build();
final Future<SimpleHttpResponse> future = client.execute(httpGet, context, null);
final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
assertProtocolVersion(context);
}
}
@Test
void test_auth_failure_wrong_auth_scope() throws Exception {
addCredentials(
new AuthScope("http", "otherhost", -1, "Restricted Files", null),
new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
final CloseableHttpAsyncClient client = client();
final HttpClientContext context = context();
final SimpleHttpRequest httpGetSecret = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath("/private/big-secret.txt")
.build();
final Future<SimpleHttpResponse> future = client.execute(httpGetSecret, context, null);
final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
assertProtocolVersion(context);
}
@Test
void test_auth_failure_wrong_auth_credentials() throws Exception {
addCredentials(
new AuthScope(target),
new UsernamePasswordCredentials("testuser", "wrong password".toCharArray()));
final CloseableHttpAsyncClient client = client();
final HttpClientContext context = context();
final SimpleHttpRequest httpGetSecret = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath("/private/big-secret.txt")
.build();
final Future<SimpleHttpResponse> future = client.execute(httpGetSecret, context, null);
final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
assertProtocolVersion(context);
}
@Test
void test_auth_success() throws Exception {
addCredentials(
new AuthScope(target),
new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
final CloseableHttpAsyncClient client = client();
final HttpClientContext context = context();
final SimpleHttpRequest httpGetSecret = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath("/private/big-secret.txt")
.build();
final Future<SimpleHttpResponse> future = client.execute(httpGetSecret, context, null);
final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
assertProtocolVersion(context);
}
}

View File

@ -0,0 +1,78 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.compatibility.async;
import java.util.concurrent.Future;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.Credentials;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.core5.http.HeaderElements;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public abstract class HttpAsyncClientHttp1CompatibilityTest extends HttpAsyncClientCompatibilityTest {
private final HttpHost target;
public HttpAsyncClientHttp1CompatibilityTest(
final HttpHost target,
final HttpHost proxy,
final Credentials proxyCreds) throws Exception {
super(HttpVersionPolicy.FORCE_HTTP_1, target, proxy, proxyCreds);
this.target = target;
}
@Test
void test_auth_success_no_keep_alive() throws Exception {
addCredentials(
new AuthScope(target),
new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
final CloseableHttpAsyncClient client = client();
final HttpClientContext context = context();
final SimpleHttpRequest httpGetSecret = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath("/private/big-secret.txt")
.addHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE)
.build();
final Future<SimpleHttpResponse> future = client.execute(httpGetSecret, context, null);
final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
assertProtocolVersion(context);
}
}

View File

@ -0,0 +1,156 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.compatibility.sync;
import org.apache.hc.client5.http.cache.CacheResponseStatus;
import org.apache.hc.client5.http.cache.HttpCacheContext;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.cache.RequestCacheControl;
import org.apache.hc.client5.http.cache.ResponseCacheControl;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpOptions;
import org.apache.hc.client5.http.impl.cache.CacheConfig;
import org.apache.hc.client5.http.impl.cache.HeapResourceFactory;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.testing.extension.sync.CachingHttpClientResource;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
public abstract class CachingHttpClientCompatibilityTest {
private final HttpHost target;
@RegisterExtension
private final CachingHttpClientResource clientResource;
public CachingHttpClientCompatibilityTest(final HttpHost target) throws Exception {
this.target = target;
this.clientResource = new CachingHttpClientResource();
this.clientResource.configure(builder -> builder
.setCacheConfig(CacheConfig.custom()
.setMaxObjectSize(10240 * 16)
.build())
.setResourceFactory(HeapResourceFactory.INSTANCE));
}
CloseableHttpClient client() {
return clientResource.client();
}
@Test
void test_options_ping() throws Exception {
final CloseableHttpClient client = client();
final HttpCacheContext context = HttpCacheContext.create();
final HttpOptions options = new HttpOptions("*");
try (ClassicHttpResponse response = client.executeOpen(target, options, context)) {
Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
EntityUtils.consume(response.getEntity());
}
}
@Test
void test_get_from_cache() throws Exception {
final CloseableHttpClient client = client();
final String[] resources1 = {"/111", "/222"};
for (final String r : resources1) {
final HttpCacheContext context1 = HttpCacheContext.create();
final HttpGet httpGet1 = new HttpGet(r);
try (ClassicHttpResponse response = client.executeOpen(target, httpGet1, context1)) {
Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
Assertions.assertEquals(CacheResponseStatus.CACHE_MISS, context1.getCacheResponseStatus());
final ResponseCacheControl responseCacheControl = context1.getResponseCacheControl();
Assertions.assertNotNull(responseCacheControl);
Assertions.assertEquals(600, responseCacheControl.getMaxAge());
final HttpCacheEntry cacheEntry = context1.getCacheEntry();
Assertions.assertNotNull(cacheEntry);
EntityUtils.consume(response.getEntity());
}
final HttpCacheContext context2 = HttpCacheContext.create();
final HttpGet httpGet2 = new HttpGet(r);
try (ClassicHttpResponse response = client.executeOpen(target, httpGet2, context2)) {
Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
Assertions.assertEquals(CacheResponseStatus.CACHE_HIT, context2.getCacheResponseStatus());
final ResponseCacheControl responseCacheControl = context2.getResponseCacheControl();
Assertions.assertNotNull(responseCacheControl);
Assertions.assertEquals(600, responseCacheControl.getMaxAge());
final HttpCacheEntry cacheEntry = context2.getCacheEntry();
Assertions.assertNotNull(cacheEntry);
Assertions.assertSame(cacheEntry, context1.getCacheEntry());
EntityUtils.consume(response.getEntity());
}
Thread.sleep(2000);
final HttpGet httpGet3 = new HttpGet(r);
final HttpCacheContext context3 = HttpCacheContext.create();
context3.setRequestCacheControl(RequestCacheControl.builder()
.setMaxAge(0)
.build());
try (ClassicHttpResponse response = client.executeOpen(target, httpGet3, context3)) {
Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
Assertions.assertEquals(CacheResponseStatus.VALIDATED, context3.getCacheResponseStatus());
final HttpCacheEntry cacheEntry = context3.getCacheEntry();
Assertions.assertNotNull(cacheEntry);
Assertions.assertNotSame(cacheEntry, context1.getCacheEntry());
EntityUtils.consume(response.getEntity());
}
}
final String[] resources2 = {"/333"};
for (final String r : resources2) {
final HttpCacheContext context1 = HttpCacheContext.create();
final HttpGet httpGet1 = new HttpGet(r);
try (ClassicHttpResponse response = client.executeOpen(target, httpGet1, context1)) {
Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
Assertions.assertEquals(CacheResponseStatus.CACHE_MISS, context1.getCacheResponseStatus());
final ResponseCacheControl responseCacheControl = context1.getResponseCacheControl();
Assertions.assertNotNull(responseCacheControl);
Assertions.assertEquals(-1, responseCacheControl.getMaxAge());
final HttpCacheEntry cacheEntry = context1.getCacheEntry();
Assertions.assertNotNull(cacheEntry);
EntityUtils.consume(response.getEntity());
}
final HttpCacheContext context2 = HttpCacheContext.create();
final HttpGet httpGet2 = new HttpGet(r);
try (ClassicHttpResponse response = client.executeOpen(target, httpGet2, context2)) {
Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
Assertions.assertEquals(CacheResponseStatus.VALIDATED, context2.getCacheResponseStatus());
final ResponseCacheControl responseCacheControl = context2.getResponseCacheControl();
Assertions.assertNotNull(responseCacheControl);
Assertions.assertEquals(-1, responseCacheControl.getMaxAge());
final HttpCacheEntry cacheEntry = context2.getCacheEntry();
Assertions.assertNotNull(cacheEntry);
Assertions.assertNotSame(cacheEntry, context1.getCacheEntry());
EntityUtils.consume(response.getEntity());
}
}
}
}

View File

@ -0,0 +1,188 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.compatibility.sync;
import org.apache.hc.client5.http.ContextBuilder;
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.Credentials;
import org.apache.hc.client5.http.auth.CredentialsStore;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpOptions;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.testing.extension.sync.HttpClientResource;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
public abstract class HttpClientCompatibilityTest {
private final HttpHost target;
@RegisterExtension
private final HttpClientResource clientResource;
private final CredentialsStore credentialsProvider;
public HttpClientCompatibilityTest(final HttpHost target, final HttpHost proxy, final Credentials proxyCreds) throws Exception {
this.target = target;
this.clientResource = new HttpClientResource();
this.clientResource.configure(builder -> builder.setProxy(proxy));
this.credentialsProvider = new BasicCredentialsProvider();
if (proxy != null && proxyCreds != null) {
this.addCredentials(new AuthScope(proxy), proxyCreds);
}
}
CloseableHttpClient client() {
return clientResource.client();
}
HttpClientContext context() {
return ContextBuilder.create()
.useCredentialsProvider(credentialsProvider)
.build();
}
void addCredentials(final AuthScope authScope, final Credentials credentials) {
credentialsProvider.setCredentials(authScope, credentials);
}
@Test
void test_options_ping() throws Exception {
final CloseableHttpClient client = client();
final HttpClientContext context = context();
final HttpOptions options = new HttpOptions("*");
try (ClassicHttpResponse response = client.executeOpen(target, options, context)) {
Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
EntityUtils.consume(response.getEntity());
}
}
@Test
void test_get() throws Exception {
final CloseableHttpClient client = client();
final HttpClientContext context = context();
final String[] requestUris = new String[] { "/111", "/222", "/333" };
for (final String requestUri: requestUris) {
final ClassicHttpRequest request = ClassicRequestBuilder.get(requestUri)
.build();
try (ClassicHttpResponse response = client.executeOpen(target, request, context)) {
Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
EntityUtils.consume(response.getEntity());
}
}
}
@Test
void test_get_connection_close() throws Exception {
final CloseableHttpClient client = client();
final HttpClientContext context = context();
final String[] requestUris = new String[] { "/111", "/222", "/333" };
for (final String requestUri: requestUris) {
final ClassicHttpRequest request = ClassicRequestBuilder.get(requestUri)
.addHeader(HttpHeaders.CONNECTION, "close")
.build();
try (ClassicHttpResponse response = client.executeOpen(target, request, context)) {
Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
EntityUtils.consume(response.getEntity());
}
}
}
@Test
void test_wrong_target_auth_scope() throws Exception {
addCredentials(
new AuthScope("http", "otherhost", -1, "Restricted Files", null),
new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
final CloseableHttpClient client = client();
final HttpClientContext context = context();
final ClassicHttpRequest request = new HttpGet("/private/big-secret.txt");
try (ClassicHttpResponse response = client.executeOpen(target, request, context)) {
Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
EntityUtils.consume(response.getEntity());
}
}
@Test
void test_wrong_target_credentials() throws Exception {
addCredentials(
new AuthScope(target),
new UsernamePasswordCredentials("testuser", "wrong password".toCharArray()));
final CloseableHttpClient client = client();
final HttpClientContext context = context();
final ClassicHttpRequest request = new HttpGet("/private/big-secret.txt");
try (ClassicHttpResponse response = client.executeOpen(target, request, context)) {
Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
EntityUtils.consume(response.getEntity());
}
}
@Test
void test_correct_target_credentials() throws Exception {
addCredentials(
new AuthScope(target),
new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
final CloseableHttpClient client = client();
final HttpClientContext context = context();
final ClassicHttpRequest request = new HttpGet("/private/big-secret.txt");
try (ClassicHttpResponse response = client.executeOpen(target, request, context)) {
Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
EntityUtils.consume(response.getEntity());
}
}
@Test
void test_correct_target_credentials_no_keep_alive() throws Exception {
addCredentials(
new AuthScope(target),
new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
final CloseableHttpClient client = client();
final HttpClientContext context = context();
final ClassicHttpRequest request = ClassicRequestBuilder.get("/private/big-secret.txt")
.addHeader(HttpHeaders.CONNECTION, "close")
.build();
try (ClassicHttpResponse response = client.executeOpen(target, request, context)) {
Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
EntityUtils.consume(response.getEntity());
}
}
}

View File

@ -0,0 +1,89 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.extension.async;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.function.Consumer;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.cache.CachingHttpAsyncClientBuilder;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.ssl.SSLContexts;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
public class CachingHttpAsyncClientResource implements AfterEachCallback {
private final CachingHttpAsyncClientBuilder clientBuilder;
private CloseableHttpAsyncClient client;
public CachingHttpAsyncClientResource(final HttpVersionPolicy versionPolicy) throws IOException {
this.clientBuilder = CachingHttpAsyncClientBuilder.create();
try {
this.clientBuilder
.setConnectionManager(PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(new DefaultClientTlsStrategy(SSLContexts.custom()
.loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray())
.build()))
.setDefaultTlsConfig(TlsConfig.custom()
.setVersionPolicy(versionPolicy)
.build())
.build());
} catch (final CertificateException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException ex) {
throw new IllegalStateException(ex);
}
}
public void configure(final Consumer<CachingHttpAsyncClientBuilder> customizer) {
customizer.accept(clientBuilder);
}
@Override
public void afterEach(final ExtensionContext extensionContext) {
if (client != null) {
client.close(CloseMode.GRACEFUL);
}
}
public CloseableHttpAsyncClient client() {
if (client == null) {
client = clientBuilder.build();
client.start();
}
return client;
}
}

View File

@ -0,0 +1,89 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.extension.async;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.function.Consumer;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.ssl.SSLContexts;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
public class HttpAsyncClientResource implements AfterEachCallback {
private final HttpAsyncClientBuilder clientBuilder;
private CloseableHttpAsyncClient client;
public HttpAsyncClientResource(final HttpVersionPolicy versionPolicy) throws IOException {
try {
this.clientBuilder = HttpAsyncClients.custom()
.setConnectionManager(PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(new DefaultClientTlsStrategy(SSLContexts.custom()
.loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray())
.build()))
.setDefaultTlsConfig(TlsConfig.custom()
.setVersionPolicy(versionPolicy)
.build())
.build());
} catch (final CertificateException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException ex) {
throw new IllegalStateException(ex);
}
}
public void configure(final Consumer<HttpAsyncClientBuilder> customizer) {
customizer.accept(clientBuilder);
}
@Override
public void afterEach(final ExtensionContext extensionContext) {
if (client != null) {
client.close(CloseMode.GRACEFUL);
}
}
public CloseableHttpAsyncClient client() {
if (client == null) {
client = clientBuilder.build();
client.start();
}
return client;
}
}

View File

@ -0,0 +1,83 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.extension.sync;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.function.Consumer;
import org.apache.hc.client5.http.impl.cache.CachingHttpClientBuilder;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.ssl.SSLContexts;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
public class CachingHttpClientResource implements AfterEachCallback {
private final CachingHttpClientBuilder clientBuilder;
private CloseableHttpClient client;
public CachingHttpClientResource() throws IOException {
this.clientBuilder = CachingHttpClientBuilder.create();
try {
this.clientBuilder
.setConnectionManager(PoolingHttpClientConnectionManagerBuilder.create()
.setTlsSocketStrategy(new DefaultClientTlsStrategy(SSLContexts.custom()
.loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray())
.build()))
.build());
} catch (final CertificateException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException ex) {
throw new IllegalStateException(ex);
}
}
public void configure(final Consumer<CachingHttpClientBuilder> customizer) {
customizer.accept(clientBuilder);
}
@Override
public void afterEach(final ExtensionContext extensionContext) {
if (client != null) {
client.close(CloseMode.GRACEFUL);
}
}
public CloseableHttpClient client() {
if (client == null) {
client = clientBuilder.build();
}
return client;
}
}

View File

@ -0,0 +1,83 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.extension.sync;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.function.Consumer;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.ssl.SSLContexts;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
public class HttpClientResource implements AfterEachCallback {
private final HttpClientBuilder clientBuilder;
private CloseableHttpClient client;
public HttpClientResource() throws IOException {
try {
this.clientBuilder = HttpClients.custom()
.setConnectionManager(PoolingHttpClientConnectionManagerBuilder.create()
.setTlsSocketStrategy(new DefaultClientTlsStrategy(SSLContexts.custom()
.loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray())
.build()))
.build());
} catch (final CertificateException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException ex) {
throw new IllegalStateException(ex);
}
}
public void configure(final Consumer<HttpClientBuilder> customizer) {
customizer.accept(clientBuilder);
}
@Override
public void afterEach(final ExtensionContext extensionContext) {
if (client != null) {
client.close(CloseMode.GRACEFUL);
}
}
public CloseableHttpClient client() {
if (client == null) {
client = clientBuilder.build();
}
return client;
}
}

View File

@ -29,7 +29,6 @@ package org.apache.hc.client5.testing.extension.sync;
import java.util.function.Consumer;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.core5.http.URIScheme;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.util.Asserts;
@ -50,7 +49,6 @@ public class TestClientResources implements AfterEachCallback {
private final TestClientBuilder clientBuilder;
private TestServer server;
private PoolingHttpClientConnectionManager connManager;
private TestClient client;
public TestClientResources(final URIScheme scheme, final ClientProtocolLevel clientProtocolLevel, final Timeout timeout) {
@ -76,11 +74,6 @@ public class TestClientResources implements AfterEachCallback {
if (client != null) {
client.close(CloseMode.GRACEFUL);
}
if (connManager != null) {
connManager.close(CloseMode.IMMEDIATE);
}
if (server != null) {
server.shutdown(CloseMode.IMMEDIATE);
}

View File

@ -1,240 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.external;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeoutException;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.cache.CacheResponseStatus;
import org.apache.hc.client5.http.cache.HttpCacheContext;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.cache.CacheConfig;
import org.apache.hc.client5.http.impl.cache.CachingHttpAsyncClients;
import org.apache.hc.client5.http.impl.cache.HeapResourceFactory;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.HttpVersion;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.ssl.SSLContexts;
import org.apache.hc.core5.util.TextUtils;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
public class CachingHttpAsyncClientCompatibilityTest {
public static void main(final String... args) throws Exception {
final CachingHttpAsyncClientCompatibilityTest[] tests = new CachingHttpAsyncClientCompatibilityTest[] {
new CachingHttpAsyncClientCompatibilityTest(
HttpVersion.HTTP_1_1, new HttpHost("http", "localhost", 8080)),
new CachingHttpAsyncClientCompatibilityTest(
HttpVersion.HTTP_2_0, new HttpHost("http", "localhost", 8080))
};
for (final CachingHttpAsyncClientCompatibilityTest test: tests) {
try {
test.execute();
} finally {
test.shutdown();
}
}
}
private static final Timeout TIMEOUT = Timeout.ofSeconds(5);
private final HttpVersion protocolVersion;
private final HttpHost target;
private final PoolingAsyncClientConnectionManager connManager;
private final CloseableHttpAsyncClient client;
CachingHttpAsyncClientCompatibilityTest(final HttpVersion protocolVersion, final HttpHost target) throws Exception {
this.protocolVersion = protocolVersion;
this.target = target;
this.connManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(new DefaultClientTlsStrategy(SSLContexts.custom()
.loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray())
.build()))
.setDefaultTlsConfig(TlsConfig.custom()
.setVersionPolicy(this.protocolVersion == HttpVersion.HTTP_2 ?
HttpVersionPolicy.FORCE_HTTP_2 : HttpVersionPolicy.FORCE_HTTP_1)
.build())
.build();
this.client = CachingHttpAsyncClients.custom()
.setCacheConfig(CacheConfig.custom()
.setMaxObjectSize(20480)
.setHeuristicCachingEnabled(true)
.build())
.setResourceFactory(HeapResourceFactory.INSTANCE)
.setConnectionManager(this.connManager)
.build();
}
void shutdown() throws Exception {
client.close();
}
enum TestResult { OK, NOK }
private void logResult(final TestResult result,
final HttpRequest request,
final HttpResponse response,
final String message) {
final StringBuilder buf = new StringBuilder();
buf.append(result);
if (buf.length() == 2) {
buf.append(" ");
}
buf.append(": ");
if (response != null && response.getVersion() != null) {
buf.append(response.getVersion()).append(" ");
} else {
buf.append(protocolVersion).append(" ");
}
buf.append(target);
buf.append(": ");
buf.append(request.getMethod()).append(" ").append(request.getRequestUri());
if (message != null && !TextUtils.isBlank(message)) {
buf.append(" -> ").append(message);
}
System.out.println(buf);
}
void execute() throws InterruptedException {
client.start();
// Initial ping
{
final HttpCacheContext context = HttpCacheContext.create();
final SimpleHttpRequest options = SimpleRequestBuilder.options()
.setHttpHost(target)
.setPath("*")
.build();
final Future<SimpleHttpResponse> future = client.execute(options, context, null);
try {
final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
final int code = response.getCode();
if (code == HttpStatus.SC_OK) {
logResult(TestResult.OK, options, response, Objects.toString(response.getFirstHeader("server")));
} else {
logResult(TestResult.NOK, options, response, "(status " + code + ")");
}
} catch (final ExecutionException ex) {
final Throwable cause = ex.getCause();
logResult(TestResult.NOK, options, null, "(" + cause.getMessage() + ")");
} catch (final TimeoutException ex) {
logResult(TestResult.NOK, options, null, "(time out)");
}
}
// GET from cache
{
connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
final HttpCacheContext context = HttpCacheContext.create();
final String[] links = {"/", "/css/hc-maven.css", "/images/logos/httpcomponents.png"};
for (final String link: links) {
final SimpleHttpRequest httpGet1 = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath(link)
.build();
final Future<SimpleHttpResponse> linkFuture1 = client.execute(httpGet1, context, null);
try {
final SimpleHttpResponse response = linkFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
final int code = response.getCode();
final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus();
if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.CACHE_MISS) {
logResult(TestResult.OK, httpGet1, response, "200, " + cacheResponseStatus);
} else {
logResult(TestResult.NOK, httpGet1, response, "(status " + code + ", " + cacheResponseStatus + ")");
}
} catch (final ExecutionException ex) {
final Throwable cause = ex.getCause();
logResult(TestResult.NOK, httpGet1, null, "(" + cause.getMessage() + ")");
} catch (final TimeoutException ex) {
logResult(TestResult.NOK, httpGet1, null, "(time out)");
}
final SimpleHttpRequest httpGet2 = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath(link)
.build();
final Future<SimpleHttpResponse> linkFuture2 = client.execute(httpGet2, context, null);
try {
final SimpleHttpResponse response = linkFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
final int code = response.getCode();
final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus();
if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.CACHE_HIT) {
logResult(TestResult.OK, httpGet2, response, "200, " + cacheResponseStatus);
} else {
logResult(TestResult.NOK, httpGet2, response, "(status " + code + ", " + cacheResponseStatus + ")");
}
} catch (final ExecutionException ex) {
final Throwable cause = ex.getCause();
logResult(TestResult.NOK, httpGet2, null, "(" + cause.getMessage() + ")");
} catch (final TimeoutException ex) {
logResult(TestResult.NOK, httpGet2, null, "(time out)");
}
Thread.sleep(2000);
final SimpleHttpRequest httpGet3 = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath(link)
.setHeader(HttpHeaders.CACHE_CONTROL, "max-age=0")
.build();
final Future<SimpleHttpResponse> linkFuture3 = client.execute(httpGet3, context, null);
try {
final SimpleHttpResponse response = linkFuture3.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
final int code = response.getCode();
final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus();
if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.VALIDATED) {
logResult(TestResult.OK, httpGet3, response, "200, " + cacheResponseStatus);
} else {
logResult(TestResult.NOK, httpGet3, response, "(status " + code + ", " + cacheResponseStatus + ")");
}
} catch (final ExecutionException ex) {
final Throwable cause = ex.getCause();
logResult(TestResult.NOK, httpGet3, null, "(" + cause.getMessage() + ")");
} catch (final TimeoutException ex) {
logResult(TestResult.NOK, httpGet3, null, "(time out)");
}
}
}
}
}

View File

@ -1,185 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.external;
import java.util.Objects;
import javax.net.ssl.SSLContext;
import org.apache.hc.client5.http.cache.CacheResponseStatus;
import org.apache.hc.client5.http.cache.HttpCacheContext;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpOptions;
import org.apache.hc.client5.http.impl.cache.CacheConfig;
import org.apache.hc.client5.http.impl.cache.CachingHttpClients;
import org.apache.hc.client5.http.impl.cache.HeapResourceFactory;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.ssl.SSLContexts;
import org.apache.hc.core5.util.TextUtils;
import org.apache.hc.core5.util.TimeValue;
public class CachingHttpClientCompatibilityTest {
public static void main(final String... args) throws Exception {
final CachingHttpClientCompatibilityTest[] tests = new CachingHttpClientCompatibilityTest[] {
new CachingHttpClientCompatibilityTest(
new HttpHost("http", "localhost", 8080))
};
for (final CachingHttpClientCompatibilityTest test: tests) {
try {
test.execute();
} finally {
test.shutdown();
}
}
}
private final HttpHost target;
private final PoolingHttpClientConnectionManager connManager;
private final CloseableHttpClient client;
CachingHttpClientCompatibilityTest(final HttpHost target) throws Exception {
this.target = target;
final SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray()).build();
this.connManager = PoolingHttpClientConnectionManagerBuilder.create()
.setTlsSocketStrategy(new DefaultClientTlsStrategy(sslContext))
.build();
this.client = CachingHttpClients.custom()
.setCacheConfig(CacheConfig.custom()
.setMaxObjectSize(20480)
.setHeuristicCachingEnabled(true)
.build())
.setResourceFactory(HeapResourceFactory.INSTANCE)
.setConnectionManager(this.connManager)
.build();
}
void shutdown() throws Exception {
client.close();
}
enum TestResult { OK, NOK }
private void logResult(final TestResult result, final HttpRequest request, final String message) {
final StringBuilder buf = new StringBuilder();
buf.append(result);
if (buf.length() == 2) {
buf.append(" ");
}
buf.append(": ").append(target);
buf.append(": ");
buf.append(request.getMethod()).append(" ").append(request.getRequestUri());
if (message != null && !TextUtils.isBlank(message)) {
buf.append(" -> ").append(message);
}
System.out.println(buf);
}
void execute() throws InterruptedException {
// Initial ping
{
final HttpCacheContext context = HttpCacheContext.create();
final HttpOptions options = new HttpOptions("*");
try (final ClassicHttpResponse response = client.executeOpen(target, options, context)) {
final int code = response.getCode();
EntityUtils.consume(response.getEntity());
if (code == HttpStatus.SC_OK) {
logResult(TestResult.OK, options, Objects.toString(response.getFirstHeader("server")));
} else {
logResult(TestResult.NOK, options, "(status " + code + ")");
}
} catch (final Exception ex) {
logResult(TestResult.NOK, options, "(" + ex.getMessage() + ")");
}
}
// GET from cache
{
connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
final String[] links = {"/", "/css/hc-maven.css", "/images/logos/httpcomponents.png"};
final HttpCacheContext context = HttpCacheContext.create();
for (final String link: links) {
final HttpGet httpGet1 = new HttpGet(link);
try (ClassicHttpResponse response = client.executeOpen(target, httpGet1, context)) {
final int code = response.getCode();
final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus();
EntityUtils.consume(response.getEntity());
if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.CACHE_MISS) {
logResult(TestResult.OK, httpGet1, "200, " + cacheResponseStatus);
} else {
logResult(TestResult.NOK, httpGet1, "(status " + code + ", " + cacheResponseStatus + ")");
}
} catch (final Exception ex) {
logResult(TestResult.NOK, httpGet1, "(" + ex.getMessage() + ")");
}
final HttpGet httpGet2 = new HttpGet(link);
try (ClassicHttpResponse response = client.executeOpen(target, httpGet2, context)) {
final int code = response.getCode();
final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus();
EntityUtils.consume(response.getEntity());
if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.CACHE_HIT) {
logResult(TestResult.OK, httpGet2, "200, " + cacheResponseStatus);
} else {
logResult(TestResult.NOK, httpGet2, "(status " + code + ", " + cacheResponseStatus + ")");
}
} catch (final Exception ex) {
logResult(TestResult.NOK, httpGet2, "(" + ex.getMessage() + ")");
}
Thread.sleep(2000);
final HttpGet httpGet3 = new HttpGet(link);
httpGet3.setHeader(HttpHeaders.CACHE_CONTROL, "max-age=0");
try (ClassicHttpResponse response = client.executeOpen(target, httpGet3, context)) {
final int code = response.getCode();
final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus();
EntityUtils.consume(response.getEntity());
if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.VALIDATED) {
logResult(TestResult.OK, httpGet3, "200, " + cacheResponseStatus);
} else {
logResult(TestResult.NOK, httpGet3, "(status " + code + ", " + cacheResponseStatus + ")");
}
} catch (final Exception ex) {
logResult(TestResult.NOK, httpGet3, "(" + ex.getMessage() + ")");
}
}
}
}
}

View File

@ -1,364 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.external;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeoutException;
import javax.net.ssl.SSLContext;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.Credentials;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.core5.http.HeaderElements;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.ssl.SSLContexts;
import org.apache.hc.core5.util.TextUtils;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
public class HttpAsyncClientCompatibilityTest {
public static void main(final String... args) throws Exception {
final HttpAsyncClientCompatibilityTest[] tests = new HttpAsyncClientCompatibilityTest[] {
new HttpAsyncClientCompatibilityTest(
HttpVersionPolicy.FORCE_HTTP_1,
new HttpHost("http", "localhost", 8080), null, null),
new HttpAsyncClientCompatibilityTest(
HttpVersionPolicy.FORCE_HTTP_1,
new HttpHost("http", "test-httpd", 8080), new HttpHost("localhost", 8888), null),
new HttpAsyncClientCompatibilityTest(
HttpVersionPolicy.FORCE_HTTP_1,
new HttpHost("http", "test-httpd", 8080), new HttpHost("localhost", 8889),
new UsernamePasswordCredentials("squid", "nopassword".toCharArray())),
new HttpAsyncClientCompatibilityTest(
HttpVersionPolicy.FORCE_HTTP_1,
new HttpHost("https", "localhost", 8443), null, null),
new HttpAsyncClientCompatibilityTest(
HttpVersionPolicy.FORCE_HTTP_1,
new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8888), null),
new HttpAsyncClientCompatibilityTest(
HttpVersionPolicy.FORCE_HTTP_1,
new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8889),
new UsernamePasswordCredentials("squid", "nopassword".toCharArray())),
new HttpAsyncClientCompatibilityTest(
HttpVersionPolicy.FORCE_HTTP_2,
new HttpHost("http", "localhost", 8080), null, null),
new HttpAsyncClientCompatibilityTest(
HttpVersionPolicy.FORCE_HTTP_2,
new HttpHost("https", "localhost", 8443), null, null),
new HttpAsyncClientCompatibilityTest(
HttpVersionPolicy.NEGOTIATE,
new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8888), null),
new HttpAsyncClientCompatibilityTest(
HttpVersionPolicy.NEGOTIATE,
new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8889),
new UsernamePasswordCredentials("squid", "nopassword".toCharArray())),
new HttpAsyncClientCompatibilityTest(
HttpVersionPolicy.FORCE_HTTP_2,
new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8888), null),
new HttpAsyncClientCompatibilityTest(
HttpVersionPolicy.FORCE_HTTP_2,
new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8889),
new UsernamePasswordCredentials("squid", "nopassword".toCharArray()))
};
for (final HttpAsyncClientCompatibilityTest test: tests) {
try {
test.execute();
} finally {
test.shutdown();
}
}
}
private static final Timeout TIMEOUT = Timeout.ofSeconds(5);
private final HttpVersionPolicy versionPolicy;
private final HttpHost target;
private final HttpHost proxy;
private final BasicCredentialsProvider credentialsProvider;
private final PoolingAsyncClientConnectionManager connManager;
private final CloseableHttpAsyncClient client;
HttpAsyncClientCompatibilityTest(
final HttpVersionPolicy versionPolicy,
final HttpHost target,
final HttpHost proxy,
final Credentials proxyCreds) throws Exception {
this.versionPolicy = versionPolicy;
this.target = target;
this.proxy = proxy;
this.credentialsProvider = new BasicCredentialsProvider();
final RequestConfig requestConfig = RequestConfig.DEFAULT;
if (proxy != null && proxyCreds != null) {
this.credentialsProvider.setCredentials(new AuthScope(proxy), proxyCreds);
}
final SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray()).build();
this.connManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(new DefaultClientTlsStrategy(sslContext))
.setDefaultTlsConfig(TlsConfig.custom()
.setVersionPolicy(versionPolicy)
.build())
.build();
this.client = HttpAsyncClients.custom()
.setConnectionManager(this.connManager)
.setProxy(this.proxy)
.setDefaultRequestConfig(requestConfig)
.build();
}
void shutdown() throws Exception {
client.close();
}
enum TestResult { OK, NOK }
private void logResult(final TestResult result,
final HttpRequest request,
final HttpResponse response,
final String message) {
final StringBuilder buf = new StringBuilder();
buf.append(result);
if (buf.length() == 2) {
buf.append(" ");
}
buf.append(": ");
if (response != null) {
buf.append(response.getVersion()).append(" ");
} else {
buf.append(versionPolicy).append(" ");
}
buf.append(target);
if (proxy != null) {
buf.append(" via ").append(proxy);
}
buf.append(": ");
buf.append(request.getMethod()).append(" ").append(request.getRequestUri());
if (message != null && !TextUtils.isBlank(message)) {
buf.append(" -> ").append(message);
}
System.out.println(buf);
}
void execute() throws Exception {
client.start();
// Initial ping
{
final HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credentialsProvider);
final SimpleHttpRequest options = SimpleRequestBuilder.options()
.setHttpHost(target)
.setPath("*")
.build();
final Future<SimpleHttpResponse> future = client.execute(options, context, null);
try {
final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
final int code = response.getCode();
if (code == HttpStatus.SC_OK) {
logResult(TestResult.OK, options, response, Objects.toString(response.getFirstHeader("server")));
} else {
logResult(TestResult.NOK, options, response, "(status " + code + ")");
}
} catch (final ExecutionException ex) {
final Throwable cause = ex.getCause();
logResult(TestResult.NOK, options, null, "(" + cause.getMessage() + ")");
} catch (final TimeoutException ex) {
logResult(TestResult.NOK, options, null, "(time out)");
}
}
// Basic GET requests
{
connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
final HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credentialsProvider);
final String[] requestUris = new String[] {"/", "/news.html", "/status.html"};
for (final String requestUri: requestUris) {
final SimpleHttpRequest httpGet = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath(requestUri)
.build();
final Future<SimpleHttpResponse> future = client.execute(httpGet, context, null);
try {
final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
final int code = response.getCode();
if (code == HttpStatus.SC_OK) {
logResult(TestResult.OK, httpGet, response, "200");
} else {
logResult(TestResult.NOK, httpGet, response, "(status " + code + ")");
}
} catch (final ExecutionException ex) {
final Throwable cause = ex.getCause();
logResult(TestResult.NOK, httpGet, null, "(" + cause.getMessage() + ")");
} catch (final TimeoutException ex) {
logResult(TestResult.NOK, httpGet, null, "(time out)");
}
}
}
// Wrong target auth scope
{
connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
credentialsProvider.setCredentials(
new AuthScope("http", "otherhost", -1, "Restricted Files", null),
new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
final HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credentialsProvider);
final SimpleHttpRequest httpGetSecret = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath("/private/big-secret.txt")
.build();
final Future<SimpleHttpResponse> future = client.execute(httpGetSecret, context, null);
try {
final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
final int code = response.getCode();
if (code == HttpStatus.SC_UNAUTHORIZED) {
logResult(TestResult.OK, httpGetSecret, response, "401 (wrong target auth scope)");
} else {
logResult(TestResult.NOK, httpGetSecret, response, "(status " + code + ")");
}
} catch (final ExecutionException ex) {
final Throwable cause = ex.getCause();
logResult(TestResult.NOK, httpGetSecret, null, "(" + cause.getMessage() + ")");
} catch (final TimeoutException ex) {
logResult(TestResult.NOK, httpGetSecret, null, "(time out)");
}
}
// Wrong target credentials
{
connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
credentialsProvider.setCredentials(
new AuthScope(target),
new UsernamePasswordCredentials("testuser", "wrong password".toCharArray()));
final HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credentialsProvider);
final SimpleHttpRequest httpGetSecret = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath("/private/big-secret.txt")
.build();
final Future<SimpleHttpResponse> future = client.execute(httpGetSecret, context, null);
try {
final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
final int code = response.getCode();
if (code == HttpStatus.SC_UNAUTHORIZED) {
logResult(TestResult.OK, httpGetSecret, response, "401 (wrong target creds)");
} else {
logResult(TestResult.NOK, httpGetSecret, response, "(status " + code + ")");
}
} catch (final ExecutionException ex) {
final Throwable cause = ex.getCause();
logResult(TestResult.NOK, httpGetSecret, null, "(" + cause.getMessage() + ")");
} catch (final TimeoutException ex) {
logResult(TestResult.NOK, httpGetSecret, null, "(time out)");
}
}
// Correct target credentials
{
connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
credentialsProvider.setCredentials(
new AuthScope(target),
new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
final HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credentialsProvider);
final SimpleHttpRequest httpGetSecret = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath("/private/big-secret.txt")
.build();
final Future<SimpleHttpResponse> future = client.execute(httpGetSecret, context, null);
try {
final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
final int code = response.getCode();
if (code == HttpStatus.SC_OK) {
logResult(TestResult.OK, httpGetSecret, response, "200 (correct target creds)");
} else {
logResult(TestResult.NOK, httpGetSecret, response, "(status " + code + ")");
}
} catch (final ExecutionException ex) {
final Throwable cause = ex.getCause();
logResult(TestResult.NOK, httpGetSecret, null, "(" + cause.getMessage() + ")");
} catch (final TimeoutException ex) {
logResult(TestResult.NOK, httpGetSecret, null, "(time out)");
}
}
// Correct target credentials (no keep-alive)
if (versionPolicy == HttpVersionPolicy.FORCE_HTTP_1)
{
connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
credentialsProvider.setCredentials(
new AuthScope(target),
new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
final HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credentialsProvider);
final SimpleHttpRequest httpGetSecret = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath("/private/big-secret.txt")
.build();
httpGetSecret.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE);
final Future<SimpleHttpResponse> future = client.execute(httpGetSecret, context, null);
try {
final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
final int code = response.getCode();
if (code == HttpStatus.SC_OK) {
logResult(TestResult.OK, httpGetSecret, response, "200 (correct target creds / no keep-alive)");
} else {
logResult(TestResult.NOK, httpGetSecret, response, "(status " + code + ")");
}
} catch (final ExecutionException ex) {
final Throwable cause = ex.getCause();
logResult(TestResult.NOK, httpGetSecret, null, "(" + cause.getMessage() + ")");
} catch (final TimeoutException ex) {
logResult(TestResult.NOK, httpGetSecret, null, "(time out)");
}
}
}
}

View File

@ -1,269 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.external;
import java.util.Objects;
import javax.net.ssl.SSLContext;
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.Credentials;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpOptions;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HeaderElements;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.ssl.SSLContexts;
import org.apache.hc.core5.util.TextUtils;
import org.apache.hc.core5.util.TimeValue;
public class HttpClientCompatibilityTest {
public static void main(final String... args) throws Exception {
final HttpClientCompatibilityTest[] tests = new HttpClientCompatibilityTest[] {
new HttpClientCompatibilityTest(
new HttpHost("http", "localhost", 8080), null, null),
new HttpClientCompatibilityTest(
new HttpHost("http", "test-httpd", 8080), new HttpHost("localhost", 8888), null),
new HttpClientCompatibilityTest(
new HttpHost("http", "test-httpd", 8080), new HttpHost("localhost", 8889),
new UsernamePasswordCredentials("squid", "nopassword".toCharArray())),
new HttpClientCompatibilityTest(
new HttpHost("https", "localhost", 8443), null, null),
new HttpClientCompatibilityTest(
new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8888), null),
new HttpClientCompatibilityTest(
new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8889),
new UsernamePasswordCredentials("squid", "nopassword".toCharArray()))
};
for (final HttpClientCompatibilityTest test: tests) {
try {
test.execute();
} finally {
test.shutdown();
}
}
}
private final HttpHost target;
private final HttpHost proxy;
private final BasicCredentialsProvider credentialsProvider;
private final PoolingHttpClientConnectionManager connManager;
private final CloseableHttpClient client;
HttpClientCompatibilityTest(
final HttpHost target,
final HttpHost proxy,
final Credentials proxyCreds) throws Exception {
this.target = target;
this.proxy = proxy;
this.credentialsProvider = new BasicCredentialsProvider();
final RequestConfig requestConfig = RequestConfig.DEFAULT;
if (proxy != null && proxyCreds != null) {
this.credentialsProvider.setCredentials(new AuthScope(proxy), proxyCreds);
}
final SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray()).build();
this.connManager = PoolingHttpClientConnectionManagerBuilder.create()
.setTlsSocketStrategy(new DefaultClientTlsStrategy(sslContext))
.build();
this.client = HttpClients.custom()
.setConnectionManager(this.connManager)
.setProxy(this.proxy)
.setDefaultRequestConfig(requestConfig)
.build();
}
void shutdown() throws Exception {
client.close();
}
enum TestResult { OK, NOK }
private void logResult(final TestResult result, final HttpRequest request, final String message) {
final StringBuilder buf = new StringBuilder();
buf.append(result);
if (buf.length() == 2) {
buf.append(" ");
}
buf.append(": ").append(target);
if (proxy != null) {
buf.append(" via ").append(proxy);
}
buf.append(": ");
buf.append(request.getMethod()).append(" ").append(request.getRequestUri());
if (message != null && !TextUtils.isBlank(message)) {
buf.append(" -> ").append(message);
}
System.out.println(buf);
}
void execute() {
// Initial ping
{
final HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credentialsProvider);
final HttpOptions options = new HttpOptions("*");
try (ClassicHttpResponse response = client.executeOpen(target, options, context)) {
final int code = response.getCode();
EntityUtils.consume(response.getEntity());
if (code == HttpStatus.SC_OK) {
logResult(TestResult.OK, options, Objects.toString(response.getFirstHeader("server")));
} else {
logResult(TestResult.NOK, options, "(status " + code + ")");
}
} catch (final Exception ex) {
logResult(TestResult.NOK, options, "(" + ex.getMessage() + ")");
}
}
// Basic GET requests
{
connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
final HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credentialsProvider);
final String[] requestUris = new String[] {"/", "/news.html", "/status.html"};
for (final String requestUri: requestUris) {
final HttpGet httpGet = new HttpGet(requestUri);
try (ClassicHttpResponse response = client.executeOpen(target, httpGet, context)) {
final int code = response.getCode();
EntityUtils.consume(response.getEntity());
if (code == HttpStatus.SC_OK) {
logResult(TestResult.OK, httpGet, "200");
} else {
logResult(TestResult.NOK, httpGet, "(status " + code + ")");
}
} catch (final Exception ex) {
logResult(TestResult.NOK, httpGet, "(" + ex.getMessage() + ")");
}
}
}
// Wrong target auth scope
{
connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
credentialsProvider.setCredentials(
new AuthScope("http", "otherhost", -1, "Restricted Files", null),
new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
final HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credentialsProvider);
final HttpGet httpGetSecret = new HttpGet("/private/big-secret.txt");
try (ClassicHttpResponse response = client.executeOpen(target, httpGetSecret, context)) {
final int code = response.getCode();
EntityUtils.consume(response.getEntity());
if (code == HttpStatus.SC_UNAUTHORIZED) {
logResult(TestResult.OK, httpGetSecret, "401 (wrong target auth scope)");
} else {
logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")");
}
} catch (final Exception ex) {
logResult(TestResult.NOK, httpGetSecret, "(" + ex.getMessage() + ")");
}
}
// Wrong target credentials
{
connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
credentialsProvider.setCredentials(
new AuthScope(target),
new UsernamePasswordCredentials("testuser", "wrong password".toCharArray()));
final HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credentialsProvider);
final HttpGet httpGetSecret = new HttpGet("/private/big-secret.txt");
try (ClassicHttpResponse response = client.executeOpen(target, httpGetSecret, context)) {
final int code = response.getCode();
EntityUtils.consume(response.getEntity());
if (code == HttpStatus.SC_UNAUTHORIZED) {
logResult(TestResult.OK, httpGetSecret, "401 (wrong target creds)");
} else {
logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")");
}
} catch (final Exception ex) {
logResult(TestResult.NOK, httpGetSecret, "(" + ex.getMessage() + ")");
}
}
// Correct target credentials
{
connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
credentialsProvider.setCredentials(
new AuthScope(target),
new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
final HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credentialsProvider);
final HttpGet httpGetSecret = new HttpGet("/private/big-secret.txt");
try (ClassicHttpResponse response = client.executeOpen(target, httpGetSecret, context)) {
final int code = response.getCode();
EntityUtils.consume(response.getEntity());
if (code == HttpStatus.SC_OK) {
logResult(TestResult.OK, httpGetSecret, "200 (correct target creds)");
} else {
logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")");
}
} catch (final Exception ex) {
logResult(TestResult.NOK, httpGetSecret, "(" + ex.getMessage() + ")");
}
}
// Correct target credentials (no keep-alive)
{
connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
credentialsProvider.setCredentials(
new AuthScope(target),
new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
final HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credentialsProvider);
final HttpGet httpGetSecret = new HttpGet("/private/big-secret.txt");
httpGetSecret.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE);
try (ClassicHttpResponse response = client.executeOpen(target, httpGetSecret, context)) {
final int code = response.getCode();
EntityUtils.consume(response.getEntity());
if (code == HttpStatus.SC_OK) {
logResult(TestResult.OK, httpGetSecret, "200 (correct target creds / no keep-alive)");
} else {
logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")");
}
} catch (final Exception ex) {
logResult(TestResult.NOK, httpGetSecret, "(" + ex.getMessage() + ")");
}
}
}
}

View File

@ -1,26 +1,3 @@
Building Docker containers for compatibility tests
========================================================
= Apache HTTPD 2.4 image
Remark: omit sudo command if executing as root
---
sudo docker build -t httpclient-tests-httpd apache-httpd
---
= Squid 3.3 image
Remark: omit sudo command if executing as root
---
sudo docker build -t httpclient-tests-squid squid
---
= Start containers
---
sudo docker-compose up
---
= SSL key / cert material (optional)
# Issue a certificate request

View File

@ -160,6 +160,11 @@ DocumentRoot "/var/httpd/www"
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
<Files ~ "(111|222)">
Header set Cache-Control "max-age=600"
</Files>
</Directory>

View File

@ -311,17 +311,11 @@ DocumentRoot "/var/httpd/www"
#
Require all granted
</Directory>
<Files ~ "(111|222)">
Header set Cache-Control "max-age=600"
</Files>
<IfModule headers_module>
<Location /index.html>
Header add Link "</css/site.css>;rel=preload"
Header add Link "</css/maven-theme.css>;rel=preload"
Header add Link "</css/maven-base.css>;rel=preload"
Header add Link "</css/hc-maven.css>;rel=preload"
Header add Link "</images/logos/httpcomponents.png>;rel=preload"
</Location>
</IfModule>
</Directory>
#
# DirectoryIndex: sets the file that Apache will serve if a directory

12
pom.xml
View File

@ -74,9 +74,9 @@
<mockito.version>4.11.0</mockito.version>
<hc.stylecheck.version>1</hc.stylecheck.version>
<rxjava.version>2.2.21</rxjava.version>
<testcontainers.version>1.20.2</testcontainers.version>
<api.comparison.version>5.3</api.comparison.version>
<hc.animal-sniffer.signature.ignores>javax.net.ssl.SSLEngine,javax.net.ssl.SSLParameters,java.nio.ByteBuffer,java.nio.CharBuffer</hc.animal-sniffer.signature.ignores>
<japicmp.version>0.15.4</japicmp.version>
</properties>
<dependencyManagement>
@ -182,6 +182,16 @@
<version>${hamcrest.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>${testcontainers.version}</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${testcontainers.version}</version>
</dependency>
</dependencies>
</dependencyManagement>