From 666ca7350b270a89bfd9a92673f19bce5b5f64fe Mon Sep 17 00:00:00 2001 From: Oleg Kalnichevski Date: Mon, 14 Oct 2024 11:19:12 +0200 Subject: [PATCH] Migrated Docker based integration tests to TestContainers --- .github/workflows/maven.yml | 2 +- httpclient5-testing/docker/.dockerignore | 16 - .../docker/apache-httpd/Dockerfile | 45 --- httpclient5-testing/docker/docker-compose.yml | 32 -- httpclient5-testing/docker/squid/Dockerfile | 29 -- httpclient5-testing/pom.xml | 35 ++ .../ApacheHTTPDSquidCompatibilityIT.java | 326 ++++++++++++++++ .../compatibility/ContainerImages.java | 119 ++++++ ...chingHttpAsyncClientCompatibilityTest.java | 179 +++++++++ .../HttpAsyncClientCompatibilityTest.java | 176 +++++++++ ...HttpAsyncClientHttp1CompatibilityTest.java | 78 ++++ .../CachingHttpClientCompatibilityTest.java | 156 ++++++++ .../sync/HttpClientCompatibilityTest.java | 188 +++++++++ .../async/CachingHttpAsyncClientResource.java | 89 +++++ .../async/HttpAsyncClientResource.java | 89 +++++ .../sync/CachingHttpClientResource.java | 83 ++++ .../extension/sync/HttpClientResource.java | 83 ++++ .../extension/sync/TestClientResources.java | 7 - ...chingHttpAsyncClientCompatibilityTest.java | 240 ------------ .../CachingHttpClientCompatibilityTest.java | 185 --------- .../HttpAsyncClientCompatibilityTest.java | 364 ------------------ .../external/HttpClientCompatibilityTest.java | 269 ------------- .../test/resources}/docker/BUILDING.txt | 23 -- .../resources/docker/httpd}/httpd-ssl.conf | 5 + .../test/resources/docker/httpd}/httpd.conf | 14 +- .../test/resources/docker}/index.txt | 0 .../test/resources/docker}/openssl.cnf | 0 .../test/resources/docker}/serial | 0 .../test/resources/docker}/server-cert.pem | 0 .../test/resources/docker}/server-key.pem | 0 .../test/resources}/docker/squid/squid.conf | 0 pom.xml | 12 +- 32 files changed, 1622 insertions(+), 1222 deletions(-) delete mode 100644 httpclient5-testing/docker/.dockerignore delete mode 100644 httpclient5-testing/docker/apache-httpd/Dockerfile delete mode 100644 httpclient5-testing/docker/docker-compose.yml delete mode 100644 httpclient5-testing/docker/squid/Dockerfile create mode 100644 httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/ApacheHTTPDSquidCompatibilityIT.java create mode 100644 httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/ContainerImages.java create mode 100644 httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/async/CachingHttpAsyncClientCompatibilityTest.java create mode 100644 httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/async/HttpAsyncClientCompatibilityTest.java create mode 100644 httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/async/HttpAsyncClientHttp1CompatibilityTest.java create mode 100644 httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/sync/CachingHttpClientCompatibilityTest.java create mode 100644 httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/sync/HttpClientCompatibilityTest.java create mode 100644 httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/async/CachingHttpAsyncClientResource.java create mode 100644 httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/async/HttpAsyncClientResource.java create mode 100644 httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/sync/CachingHttpClientResource.java create mode 100644 httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/sync/HttpClientResource.java delete mode 100644 httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/CachingHttpAsyncClientCompatibilityTest.java delete mode 100644 httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/CachingHttpClientCompatibilityTest.java delete mode 100644 httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/HttpAsyncClientCompatibilityTest.java delete mode 100644 httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/HttpClientCompatibilityTest.java rename httpclient5-testing/{ => src/test/resources}/docker/BUILDING.txt (56%) rename httpclient5-testing/{docker/apache-httpd => src/test/resources/docker/httpd}/httpd-ssl.conf (99%) rename httpclient5-testing/{docker/apache-httpd => src/test/resources/docker/httpd}/httpd.conf (98%) rename httpclient5-testing/{docker/apache-httpd => src/test/resources/docker}/index.txt (100%) rename httpclient5-testing/{docker/apache-httpd => src/test/resources/docker}/openssl.cnf (100%) rename httpclient5-testing/{docker/apache-httpd => src/test/resources/docker}/serial (100%) rename httpclient5-testing/{docker/apache-httpd => src/test/resources/docker}/server-cert.pem (100%) rename httpclient5-testing/{docker/apache-httpd => src/test/resources/docker}/server-key.pem (100%) rename httpclient5-testing/{ => src/test/resources}/docker/squid/squid.conf (100%) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 187832dab4..6a88469d58 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -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 diff --git a/httpclient5-testing/docker/.dockerignore b/httpclient5-testing/docker/.dockerignore deleted file mode 100644 index 2b0b58d567..0000000000 --- a/httpclient5-testing/docker/.dockerignore +++ /dev/null @@ -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 diff --git a/httpclient5-testing/docker/apache-httpd/Dockerfile b/httpclient5-testing/docker/apache-httpd/Dockerfile deleted file mode 100644 index 6400aff4fb..0000000000 --- a/httpclient5-testing/docker/apache-httpd/Dockerfile +++ /dev/null @@ -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 diff --git a/httpclient5-testing/docker/docker-compose.yml b/httpclient5-testing/docker/docker-compose.yml deleted file mode 100644 index d04c5e8b13..0000000000 --- a/httpclient5-testing/docker/docker-compose.yml +++ /dev/null @@ -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" \ No newline at end of file diff --git a/httpclient5-testing/docker/squid/Dockerfile b/httpclient5-testing/docker/squid/Dockerfile deleted file mode 100644 index 60f4253e6e..0000000000 --- a/httpclient5-testing/docker/squid/Dockerfile +++ /dev/null @@ -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 diff --git a/httpclient5-testing/pom.xml b/httpclient5-testing/pom.xml index 863f8bad40..63377a25af 100644 --- a/httpclient5-testing/pom.xml +++ b/httpclient5-testing/pom.xml @@ -97,8 +97,43 @@ rxjava test + + org.testcontainers + testcontainers + test + + + org.testcontainers + junit-jupiter + test + + + + docker + + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${hc.surefire.version} + + + + integration-test + verify + + + + + + + + + + diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/ApacheHTTPDSquidCompatibilityIT.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/ApacheHTTPDSquidCompatibilityIT.java new file mode 100644 index 0000000000..66ef1cda97 --- /dev/null +++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/ApacheHTTPDSquidCompatibilityIT.java @@ -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 + * . + * + */ +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()); + } + + } + +} diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/ContainerImages.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/ContainerImages.java new file mode 100644 index 0000000000..1c313249ea --- /dev/null +++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/ContainerImages.java @@ -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 + * . + * + */ +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); + + } + +} diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/async/CachingHttpAsyncClientCompatibilityTest.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/async/CachingHttpAsyncClientCompatibilityTest.java new file mode 100644 index 0000000000..647f32d4b4 --- /dev/null +++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/async/CachingHttpAsyncClientCompatibilityTest.java @@ -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 + * . + * + */ +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 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 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 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 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 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 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()); + } + } + +} diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/async/HttpAsyncClientCompatibilityTest.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/async/HttpAsyncClientCompatibilityTest.java new file mode 100644 index 0000000000..acfa492fde --- /dev/null +++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/async/HttpAsyncClientCompatibilityTest.java @@ -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 + * . + * + */ +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 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 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 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 future = client.execute(httpGetSecret, context, null); + final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); + Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); + assertProtocolVersion(context); + } + +} diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/async/HttpAsyncClientHttp1CompatibilityTest.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/async/HttpAsyncClientHttp1CompatibilityTest.java new file mode 100644 index 0000000000..901e655c4b --- /dev/null +++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/async/HttpAsyncClientHttp1CompatibilityTest.java @@ -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 + * . + * + */ +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 future = client.execute(httpGetSecret, context, null); + final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); + Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); + assertProtocolVersion(context); + } + +} diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/sync/CachingHttpClientCompatibilityTest.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/sync/CachingHttpClientCompatibilityTest.java new file mode 100644 index 0000000000..ebcb7b40b8 --- /dev/null +++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/sync/CachingHttpClientCompatibilityTest.java @@ -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 + * . + * + */ +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()); + } + } + } + +} diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/sync/HttpClientCompatibilityTest.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/sync/HttpClientCompatibilityTest.java new file mode 100644 index 0000000000..6db3469524 --- /dev/null +++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/compatibility/sync/HttpClientCompatibilityTest.java @@ -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 + * . + * + */ +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()); + } + } + +} diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/async/CachingHttpAsyncClientResource.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/async/CachingHttpAsyncClientResource.java new file mode 100644 index 0000000000..e997c87336 --- /dev/null +++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/async/CachingHttpAsyncClientResource.java @@ -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 + * . + * + */ + +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 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; + } + +} diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/async/HttpAsyncClientResource.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/async/HttpAsyncClientResource.java new file mode 100644 index 0000000000..3fff3d597e --- /dev/null +++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/async/HttpAsyncClientResource.java @@ -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 + * . + * + */ + +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 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; + } + +} diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/sync/CachingHttpClientResource.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/sync/CachingHttpClientResource.java new file mode 100644 index 0000000000..57e58e9626 --- /dev/null +++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/sync/CachingHttpClientResource.java @@ -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 + * . + * + */ + +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 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; + } + +} diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/sync/HttpClientResource.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/sync/HttpClientResource.java new file mode 100644 index 0000000000..40577fd378 --- /dev/null +++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/sync/HttpClientResource.java @@ -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 + * . + * + */ + +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 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; + } + +} diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/sync/TestClientResources.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/sync/TestClientResources.java index c38c7fba1b..2dcb6356ec 100644 --- a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/sync/TestClientResources.java +++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/sync/TestClientResources.java @@ -29,7 +29,6 @@ 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 void afterEach(final ExtensionContext extensionContext) { if (client != null) { client.close(CloseMode.GRACEFUL); } - - if (connManager != null) { - connManager.close(CloseMode.IMMEDIATE); - } - if (server != null) { server.shutdown(CloseMode.IMMEDIATE); } diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/CachingHttpAsyncClientCompatibilityTest.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/CachingHttpAsyncClientCompatibilityTest.java deleted file mode 100644 index 683286dfd6..0000000000 --- a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/CachingHttpAsyncClientCompatibilityTest.java +++ /dev/null @@ -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 - * . - * - */ -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 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 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 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 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)"); - } - } - } - } - -} diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/CachingHttpClientCompatibilityTest.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/CachingHttpClientCompatibilityTest.java deleted file mode 100644 index c24a4c1fc5..0000000000 --- a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/CachingHttpClientCompatibilityTest.java +++ /dev/null @@ -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 - * . - * - */ -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() + ")"); - } - } - } - } - -} diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/HttpAsyncClientCompatibilityTest.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/HttpAsyncClientCompatibilityTest.java deleted file mode 100644 index acc3d7fc5b..0000000000 --- a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/HttpAsyncClientCompatibilityTest.java +++ /dev/null @@ -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 - * . - * - */ -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 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 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 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 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 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 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)"); - } - } - } - -} diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/HttpClientCompatibilityTest.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/HttpClientCompatibilityTest.java deleted file mode 100644 index 0b73e9997c..0000000000 --- a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/HttpClientCompatibilityTest.java +++ /dev/null @@ -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 - * . - * - */ -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() + ")"); - } - } - } - -} diff --git a/httpclient5-testing/docker/BUILDING.txt b/httpclient5-testing/src/test/resources/docker/BUILDING.txt similarity index 56% rename from httpclient5-testing/docker/BUILDING.txt rename to httpclient5-testing/src/test/resources/docker/BUILDING.txt index 419a61d2e8..fdc7d90566 100644 --- a/httpclient5-testing/docker/BUILDING.txt +++ b/httpclient5-testing/src/test/resources/docker/BUILDING.txt @@ -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 diff --git a/httpclient5-testing/docker/apache-httpd/httpd-ssl.conf b/httpclient5-testing/src/test/resources/docker/httpd/httpd-ssl.conf similarity index 99% rename from httpclient5-testing/docker/apache-httpd/httpd-ssl.conf rename to httpclient5-testing/src/test/resources/docker/httpd/httpd-ssl.conf index 53477407ca..ea175bb2f5 100644 --- a/httpclient5-testing/docker/apache-httpd/httpd-ssl.conf +++ b/httpclient5-testing/src/test/resources/docker/httpd/httpd-ssl.conf @@ -160,6 +160,11 @@ DocumentRoot "/var/httpd/www" Options Indexes FollowSymLinks AllowOverride None Require all granted + + + Header set Cache-Control "max-age=600" + + diff --git a/httpclient5-testing/docker/apache-httpd/httpd.conf b/httpclient5-testing/src/test/resources/docker/httpd/httpd.conf similarity index 98% rename from httpclient5-testing/docker/apache-httpd/httpd.conf rename to httpclient5-testing/src/test/resources/docker/httpd/httpd.conf index 93d05d4268..f9931ea71b 100644 --- a/httpclient5-testing/docker/apache-httpd/httpd.conf +++ b/httpclient5-testing/src/test/resources/docker/httpd/httpd.conf @@ -311,17 +311,11 @@ DocumentRoot "/var/httpd/www" # Require all granted - + + Header set Cache-Control "max-age=600" + - - - Header add Link ";rel=preload" - Header add Link ";rel=preload" - Header add Link ";rel=preload" - Header add Link ";rel=preload" - Header add Link ";rel=preload" - - + # # DirectoryIndex: sets the file that Apache will serve if a directory diff --git a/httpclient5-testing/docker/apache-httpd/index.txt b/httpclient5-testing/src/test/resources/docker/index.txt similarity index 100% rename from httpclient5-testing/docker/apache-httpd/index.txt rename to httpclient5-testing/src/test/resources/docker/index.txt diff --git a/httpclient5-testing/docker/apache-httpd/openssl.cnf b/httpclient5-testing/src/test/resources/docker/openssl.cnf similarity index 100% rename from httpclient5-testing/docker/apache-httpd/openssl.cnf rename to httpclient5-testing/src/test/resources/docker/openssl.cnf diff --git a/httpclient5-testing/docker/apache-httpd/serial b/httpclient5-testing/src/test/resources/docker/serial similarity index 100% rename from httpclient5-testing/docker/apache-httpd/serial rename to httpclient5-testing/src/test/resources/docker/serial diff --git a/httpclient5-testing/docker/apache-httpd/server-cert.pem b/httpclient5-testing/src/test/resources/docker/server-cert.pem similarity index 100% rename from httpclient5-testing/docker/apache-httpd/server-cert.pem rename to httpclient5-testing/src/test/resources/docker/server-cert.pem diff --git a/httpclient5-testing/docker/apache-httpd/server-key.pem b/httpclient5-testing/src/test/resources/docker/server-key.pem similarity index 100% rename from httpclient5-testing/docker/apache-httpd/server-key.pem rename to httpclient5-testing/src/test/resources/docker/server-key.pem diff --git a/httpclient5-testing/docker/squid/squid.conf b/httpclient5-testing/src/test/resources/docker/squid/squid.conf similarity index 100% rename from httpclient5-testing/docker/squid/squid.conf rename to httpclient5-testing/src/test/resources/docker/squid/squid.conf diff --git a/pom.xml b/pom.xml index d70eca2881..5b0bff4b95 100644 --- a/pom.xml +++ b/pom.xml @@ -74,9 +74,9 @@ 4.11.0 1 2.2.21 + 1.20.2 5.3 javax.net.ssl.SSLEngine,javax.net.ssl.SSLParameters,java.nio.ByteBuffer,java.nio.CharBuffer - 0.15.4 @@ -182,6 +182,16 @@ ${hamcrest.version} test + + org.testcontainers + testcontainers + ${testcontainers.version} + + + org.testcontainers + junit-jupiter + ${testcontainers.version} +