You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2017/05/01 12:39:18 UTC
svn commit: r1793320 [1/5] - in /httpcomponents/httpclient/trunk:
httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/
httpclient5-testing/src/main/java/org/apache/hc/client5/testing/classic/
httpclient5-testing/src/test/java/org/apac...
Author: olegk
Date: Mon May 1 12:39:16 2017
New Revision: 1793320
URL: http://svn.apache.org/viewvc?rev=1793320&view=rev
Log:
Initial implementation of the async request execution chain
Added:
httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/
httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AbstractSimpleServerExchangeHandler.java (with props)
httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AsyncEchoHandler.java (with props)
httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AsyncRandomHandler.java (with props)
httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/SSLTestContexts.java (contents, props changed)
- copied, changed from r1793319, httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/SSLTestContexts.java
httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/
httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/IntegrationTestBase.java (with props)
httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/LocalAsyncServerTestBase.java (with props)
httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestAsyncRedirects.java (with props)
httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestAsyncStatefulConnManagement.java (with props)
httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestHttpAsync.java (with props)
httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestHttpAsyncMinimal.java (with props)
httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/AsyncClientCustomSSL.java (with props)
httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/AsyncClientInterceptors.java (with props)
httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/AsyncClientMessageTrailers.java
- copied, changed from r1793319, httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/AsyncClientConnectionEviction.java
httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/AsyncQuickStart.java (with props)
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/async/AsyncExecCallback.java
- copied, changed from r1793319, httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleRequestProducer.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/async/AsyncExecChain.java (with props)
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/async/AsyncExecChainHandler.java
- copied, changed from r1793319, httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleRequestProducer.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/async/AsyncExecRuntime.java (with props)
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncExecChainElement.java (with props)
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncExecRuntimeImpl.java (with props)
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncMainClientExec.java (with props)
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncProtocolExec.java (with props)
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncRedirectExec.java (with props)
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncRetryExec.java (with props)
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/BasicAsyncEntityProducer.java (with props)
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/LoggingAsyncClientExchangeHandler.java (with props)
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/NoopHttpProcessor.java
- copied, changed from r1793319, httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleResponseConsumer.java
Removed:
httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/SSLTestContexts.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/AbstractAsyncResponseConsumer.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/logging/LoggingIOEventHandler.java
Modified:
httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/classic/EchoHandler.java
httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/classic/RandomHandler.java
httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/HttpAsyncClientCompatibilityTest.java
httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/LocalServerTestBase.java
httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestRedirects.java
httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestSSLSocketFactory.java
httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/AsyncClientConnectionEviction.java
httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/AsyncClientHttp1Pipelining.java
httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/AsyncClientHttp2Multiplexing.java
httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/AsyncClientHttpExchange.java
httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/ClientCustomSSL.java
httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/ClientInterceptors.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/async/HttpAsyncClient.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/DefaultAsyncRequestProducer.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleHttpRequest.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleHttpResponse.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleRequestProducer.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleResponseConsumer.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/ExecSupport.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AbstractHttpAsyncClientBase.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/CloseableHttpAsyncClient.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientEventHandlerFactory.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClients.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncClient.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/MinimalHttpAsyncClient.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/AsyncClientConnectionOperator.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/ManagedAsyncClientConnection.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/ExecRuntimeImpl.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/nio/AsyncClientConnectionManager.java
httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/H2TlsStrategy.java
Added: httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AbstractSimpleServerExchangeHandler.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AbstractSimpleServerExchangeHandler.java?rev=1793320&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AbstractSimpleServerExchangeHandler.java (added)
+++ httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AbstractSimpleServerExchangeHandler.java Mon May 1 12:39:16 2017
@@ -0,0 +1,79 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.client5.testing.async;
+
+import java.io.IOException;
+
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.nio.AsyncRequestConsumer;
+import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer;
+import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer;
+import org.apache.hc.core5.http.nio.support.AbstractAsyncRequesterConsumer;
+import org.apache.hc.core5.http.nio.support.AbstractServerExchangeHandler;
+import org.apache.hc.core5.http.nio.support.BasicAsyncResponseProducer;
+import org.apache.hc.core5.http.nio.support.ResponseTrigger;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.http.protocol.HttpCoreContext;
+
+public abstract class AbstractSimpleServerExchangeHandler extends AbstractServerExchangeHandler<SimpleHttpRequest> {
+
+ protected abstract SimpleHttpResponse handle(SimpleHttpRequest request, HttpCoreContext context) throws HttpException;
+
+ @Override
+ protected final AsyncRequestConsumer<SimpleHttpRequest> supplyConsumer(
+ final HttpRequest request,
+ final HttpContext context) throws HttpException {
+ return new AbstractAsyncRequesterConsumer<SimpleHttpRequest, String>(new StringAsyncEntityConsumer()) {
+
+ @Override
+ protected SimpleHttpRequest buildResult(
+ final HttpRequest request,
+ final String entity,
+ final ContentType contentType) {
+ return new SimpleHttpRequest(request, entity, contentType);
+ }
+
+ };
+ }
+
+ @Override
+ protected final void handle(
+ final SimpleHttpRequest request,
+ final ResponseTrigger responseTrigger,
+ final HttpContext context) throws HttpException, IOException {
+ final SimpleHttpResponse response = handle(request, HttpCoreContext.adapt(context));
+ responseTrigger.submitResponse(new BasicAsyncResponseProducer(
+ response,
+ response.getBody() != null ? new StringAsyncEntityProducer(response.getBody(), response.getContentType()) : null));
+
+ }
+
+}
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AbstractSimpleServerExchangeHandler.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AbstractSimpleServerExchangeHandler.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AbstractSimpleServerExchangeHandler.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AsyncEchoHandler.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AsyncEchoHandler.java?rev=1793320&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AsyncEchoHandler.java (added)
+++ httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AsyncEchoHandler.java Mon May 1 12:39:16 2017
@@ -0,0 +1,154 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.client5.testing.async;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.hc.core5.concurrent.FutureCallback;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.EntityDetails;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.MethodNotSupportedException;
+import org.apache.hc.core5.http.message.BasicHttpResponse;
+import org.apache.hc.core5.http.nio.AsyncEntityProducer;
+import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
+import org.apache.hc.core5.http.nio.CapacityChannel;
+import org.apache.hc.core5.http.nio.DataStreamChannel;
+import org.apache.hc.core5.http.nio.ResponseChannel;
+import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityConsumer;
+import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer;
+import org.apache.hc.core5.util.Asserts;
+
+/**
+ * A handler that echos the incoming request entity.
+ */
+public class AsyncEchoHandler implements AsyncServerExchangeHandler {
+
+ private final BasicAsyncEntityConsumer entityConsumer;
+ private final AtomicReference<AsyncEntityProducer> entityProducerRef;
+
+ public AsyncEchoHandler() {
+ this.entityConsumer = new BasicAsyncEntityConsumer();
+ this.entityProducerRef = new AtomicReference<>(null);
+ }
+
+ @Override
+ public void releaseResources() {
+ entityConsumer.releaseResources();
+ final AsyncEntityProducer producer = entityProducerRef.getAndSet(null);
+ if (producer != null) {
+ producer.releaseResources();
+ }
+ }
+
+ @Override
+ public void handleRequest(
+ final HttpRequest request,
+ final EntityDetails entityDetails,
+ final ResponseChannel responseChannel) throws HttpException, IOException {
+ final String method = request.getMethod();
+ if (!"GET".equalsIgnoreCase(method) &&
+ !"HEAD".equalsIgnoreCase(method) &&
+ !"POST".equalsIgnoreCase(method) &&
+ !"PUT".equalsIgnoreCase(method)) {
+ throw new MethodNotSupportedException(method + " not supported by " + getClass().getName());
+ }
+ if (entityDetails != null) {
+
+ final ContentType contentType = ContentType.parseLenient(entityDetails.getContentType());
+ entityConsumer.streamStart(entityDetails, new FutureCallback<byte[]>() {
+
+ @Override
+ public void completed(final byte[] content) {
+ final BasicAsyncEntityProducer entityProducer = new BasicAsyncEntityProducer(content, contentType);
+ entityProducerRef.set(entityProducer);
+ try {
+ responseChannel.sendResponse(new BasicHttpResponse(HttpStatus.SC_OK), entityProducer);
+ } catch (final IOException | HttpException ex) {
+ failed(ex);
+ }
+ }
+
+ @Override
+ public void failed(final Exception ex) {
+ releaseResources();
+ }
+
+ @Override
+ public void cancelled() {
+ releaseResources();
+ }
+
+ });
+ } else {
+ responseChannel.sendResponse(new BasicHttpResponse(HttpStatus.SC_OK), null);
+ entityConsumer.releaseResources();
+ }
+ }
+
+ @Override
+ public void updateCapacity(final CapacityChannel capacityChannel) throws IOException {
+ entityConsumer.updateCapacity(capacityChannel);
+ }
+
+ @Override
+ public int consume(final ByteBuffer src) throws IOException {
+ return entityConsumer.consume(src);
+ }
+
+ @Override
+ public void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
+ entityConsumer.streamEnd(trailers);
+ }
+
+ @Override
+ public int available() {
+ final AsyncEntityProducer producer = entityProducerRef.get();
+ Asserts.notNull(producer, "Entity producer");
+ return producer.available();
+ }
+
+ @Override
+ public void produce(final DataStreamChannel channel) throws IOException {
+ final AsyncEntityProducer producer = entityProducerRef.get();
+ Asserts.notNull(producer, "Entity producer");
+ producer.produce(channel);
+ }
+
+ @Override
+ public void failed(final Exception cause) {
+ releaseResources();
+ }
+
+}
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AsyncEchoHandler.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AsyncEchoHandler.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AsyncEchoHandler.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AsyncRandomHandler.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AsyncRandomHandler.java?rev=1793320&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AsyncRandomHandler.java (added)
+++ httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AsyncRandomHandler.java Mon May 1 12:39:16 2017
@@ -0,0 +1,210 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.client5.testing.async;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.EntityDetails;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpException;
+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.MethodNotSupportedException;
+import org.apache.hc.core5.http.ProtocolException;
+import org.apache.hc.core5.http.message.BasicHttpResponse;
+import org.apache.hc.core5.http.nio.AsyncEntityProducer;
+import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
+import org.apache.hc.core5.http.nio.CapacityChannel;
+import org.apache.hc.core5.http.nio.DataStreamChannel;
+import org.apache.hc.core5.http.nio.ResponseChannel;
+import org.apache.hc.core5.http.nio.StreamChannel;
+import org.apache.hc.core5.http.nio.entity.AbstractBinAsyncEntityProducer;
+import org.apache.hc.core5.util.Asserts;
+
+/**
+ * A handler that generates random data.
+ */
+public class AsyncRandomHandler implements AsyncServerExchangeHandler {
+
+ private final AtomicReference<AsyncEntityProducer> entityProducerRef;
+
+ public AsyncRandomHandler() {
+ this.entityProducerRef = new AtomicReference<>(null);
+ }
+
+ @Override
+ public void releaseResources() {
+ final AsyncEntityProducer producer = entityProducerRef.getAndSet(null);
+ if (producer != null) {
+ producer.releaseResources();
+ }
+ }
+
+ @Override
+ public void handleRequest(
+ final HttpRequest request,
+ final EntityDetails entityDetails,
+ final ResponseChannel responseChannel) throws HttpException, IOException {
+ final String method = request.getMethod();
+ if (!"GET".equalsIgnoreCase(method) &&
+ !"HEAD".equalsIgnoreCase(method) &&
+ !"POST".equalsIgnoreCase(method) &&
+ !"PUT".equalsIgnoreCase(method)) {
+ throw new MethodNotSupportedException(method + " not supported by " + getClass().getName());
+ }
+ final URI uri;
+ try {
+ uri = request.getUri();
+ } catch (URISyntaxException ex) {
+ throw new ProtocolException(ex.getMessage(), ex);
+ }
+ final String path = uri.getPath();
+ final int slash = path.lastIndexOf('/');
+ if (slash != -1) {
+ final String payload = path.substring(slash + 1, path.length());
+ final long n;
+ if (!payload.isEmpty()) {
+ try {
+ n = Long.parseLong(payload);
+ } catch (final NumberFormatException ex) {
+ throw new ProtocolException("Invalid request path: " + path);
+ }
+ } else {
+ // random length, but make sure at least something is sent
+ n = 1 + (int)(Math.random() * 79.0);
+ }
+ final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK);
+ final AsyncEntityProducer entityProducer = new RandomBinAsyncEntityProducer(n);
+ entityProducerRef.set(entityProducer);
+ responseChannel.sendResponse(response, entityProducer);
+ } else {
+ throw new ProtocolException("Invalid request path: " + path);
+ }
+ }
+
+ @Override
+ public void updateCapacity(final CapacityChannel capacityChannel) throws IOException {
+ capacityChannel.update(Integer.MAX_VALUE);
+ }
+
+ @Override
+ public int consume(final ByteBuffer src) throws IOException {
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ public void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
+ }
+
+ @Override
+ public int available() {
+ final AsyncEntityProducer producer = entityProducerRef.get();
+ Asserts.notNull(producer, "Entity producer");
+ return producer.available();
+ }
+
+ @Override
+ public void produce(final DataStreamChannel channel) throws IOException {
+ final AsyncEntityProducer producer = entityProducerRef.get();
+ Asserts.notNull(producer, "Entity producer");
+ producer.produce(channel);
+ }
+
+ @Override
+ public void failed(final Exception cause) {
+ releaseResources();
+ }
+
+ /**
+ * An entity that generates random data.
+ */
+ public static class RandomBinAsyncEntityProducer extends AbstractBinAsyncEntityProducer {
+
+ /** The range from which to generate random data. */
+ private final static byte[] RANGE = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+ .getBytes(StandardCharsets.US_ASCII);
+
+ /** The length of the random data to generate. */
+ private final long length;
+ private long remaining;
+ private final ByteBuffer buffer;
+
+ public RandomBinAsyncEntityProducer(final long len) {
+ super(2048, 512, ContentType.DEFAULT_TEXT);
+ length = len;
+ remaining = len;
+ buffer = ByteBuffer.allocate(1024);
+ }
+
+ @Override
+ public void releaseResources() {
+ }
+
+ @Override
+ public long getContentLength() {
+ return length;
+ }
+
+ @Override
+ public int available() {
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ protected void produceData(final StreamChannel<ByteBuffer> channel) throws IOException {
+ final int chunk = Math.min((int) (remaining < Integer.MAX_VALUE ? remaining : Integer.MAX_VALUE), buffer.remaining());
+ for (int i = 0; i < chunk; i++) {
+ final byte b = RANGE[(int) (Math.random() * RANGE.length)];
+ buffer.put(b);
+ }
+ buffer.flip();
+ final int bytesWritten = channel.write(buffer);
+ if (bytesWritten > 0) {
+ remaining -= bytesWritten;
+ }
+ buffer.compact();
+ if (remaining <= 0) {
+ channel.endStream();
+ }
+ }
+
+ @Override
+ public void failed(final Exception cause) {
+ }
+
+ }
+
+}
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AsyncRandomHandler.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AsyncRandomHandler.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AsyncRandomHandler.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/classic/EchoHandler.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/classic/EchoHandler.java?rev=1793320&r1=1793319&r2=1793320&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/classic/EchoHandler.java (original)
+++ httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/classic/EchoHandler.java Mon May 1 12:39:16 2017
@@ -65,11 +65,10 @@ public class EchoHandler implements Http
final String method = request.getMethod().toUpperCase(Locale.ROOT);
if (!"GET".equals(method) &&
- !"POST".equals(method) &&
- !"PUT".equals(method)
- ) {
- throw new MethodNotSupportedException
- (method + " not supported by " + getClass().getName());
+ !"HEAD".equals(method) &&
+ !"POST".equals(method) &&
+ !"PUT".equals(method)) {
+ throw new MethodNotSupportedException(method + " not supported by " + getClass().getName());
}
HttpEntity entity = request.getEntity();
Modified: httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/classic/RandomHandler.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/classic/RandomHandler.java?rev=1793320&r1=1793319&r2=1793320&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/classic/RandomHandler.java (original)
+++ httpcomponents/httpclient/trunk/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/classic/RandomHandler.java Mon May 1 12:39:16 2017
@@ -30,14 +30,16 @@ package org.apache.hc.client5.testing.cl
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
-import java.util.Locale;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.MethodNotSupportedException;
+import org.apache.hc.core5.http.ProtocolException;
import org.apache.hc.core5.http.io.HttpRequestHandler;
import org.apache.hc.core5.http.io.entity.AbstractHttpEntity;
import org.apache.hc.core5.http.protocol.HttpContext;
@@ -68,51 +70,40 @@ public class RandomHandler implements Ht
final HttpContext context)
throws HttpException, IOException {
- final String method = request.getMethod().toUpperCase(Locale.ROOT);
- if (!"GET".equals(method) && !"HEAD".equals(method)) {
- throw new MethodNotSupportedException
- (method + " not supported by " + getClass().getName());
+ final String method = request.getMethod();
+ if (!"GET".equalsIgnoreCase(method) &&
+ !"HEAD".equalsIgnoreCase(method) &&
+ !"POST".equalsIgnoreCase(method) &&
+ !"PUT".equalsIgnoreCase(method)) {
+ throw new MethodNotSupportedException(method + " not supported by " + getClass().getName());
}
-
- final String uri = request.getRequestUri();
- final int slash = uri.lastIndexOf('/');
- int length = -1;
- if (slash < uri.length()-1) {
- try {
- // no more than Integer, 2 GB ought to be enough for anybody
- length = Integer.parseInt(uri.substring(slash+1));
-
- if (length < 0) {
- response.setCode(HttpStatus.SC_BAD_REQUEST);
- response.setReasonPhrase("LENGTH " + length);
- }
- } catch (final NumberFormatException nfx) {
- response.setCode(HttpStatus.SC_BAD_REQUEST);
- response.setReasonPhrase(nfx.toString());
- }
- } else {
- // random length, but make sure at least something is sent
- length = 1 + (int)(Math.random() * 79.0);
+ final URI uri;
+ try {
+ uri = request.getUri();
+ } catch (URISyntaxException ex) {
+ throw new ProtocolException(ex.getMessage(), ex);
}
-
- if (length >= 0) {
-
- response.setCode(HttpStatus.SC_OK);
-
- if (!"HEAD".equals(method)) {
- final RandomEntity entity = new RandomEntity(length);
- entity.setContentType("text/plain; charset=US-ASCII");
- response.setEntity(entity);
+ final String path = uri.getPath();
+ final int slash = path.lastIndexOf('/');
+ if (slash != -1) {
+ final String payload = path.substring(slash + 1, path.length());
+ final long n;
+ if (!payload.isEmpty()) {
+ try {
+ n = Long.parseLong(payload);
+ } catch (final NumberFormatException ex) {
+ throw new ProtocolException("Invalid request path: " + path);
+ }
} else {
- response.setHeader("Content-Type",
- "text/plain; charset=US-ASCII");
- response.setHeader("Content-Length",
- String.valueOf(length));
+ // random length, but make sure at least something is sent
+ n = 1 + (int)(Math.random() * 79.0);
}
+ response.setCode(HttpStatus.SC_OK);
+ response.setEntity(new RandomEntity(n));
+ } else {
+ throw new ProtocolException("Invalid request path: " + path);
}
-
- } // handle
-
+ }
/**
* An entity that generates random data.
Copied: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/SSLTestContexts.java (from r1793319, httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/SSLTestContexts.java)
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/SSLTestContexts.java?p2=httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/SSLTestContexts.java&p1=httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/SSLTestContexts.java&r1=1793319&r2=1793320&rev=1793320&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/SSLTestContexts.java (original)
+++ httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/SSLTestContexts.java Mon May 1 12:39:16 2017
@@ -25,7 +25,7 @@
*
*/
-package org.apache.hc.client5.testing.sync;
+package org.apache.hc.client5.testing;
import javax.net.ssl.SSLContext;
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/SSLTestContexts.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/SSLTestContexts.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/SSLTestContexts.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/IntegrationTestBase.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/IntegrationTestBase.java?rev=1793320&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/IntegrationTestBase.java (added)
+++ httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/IntegrationTestBase.java Mon May 1 12:39:16 2017
@@ -0,0 +1,82 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.client5.testing.async;
+
+import java.net.InetSocketAddress;
+
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.io.ShutdownType;
+import org.apache.hc.core5.reactor.ListenerEndpoint;
+import org.junit.Rule;
+import org.junit.rules.ExternalResource;
+
+public abstract class IntegrationTestBase extends LocalAsyncServerTestBase {
+
+ public IntegrationTestBase(final URIScheme scheme) {
+ super(scheme);
+ }
+
+ public IntegrationTestBase() {
+ super(URIScheme.HTTP);
+ }
+
+ protected HttpAsyncClientBuilder clientBuilder;
+ protected CloseableHttpAsyncClient httpclient;
+
+ @Rule
+ public ExternalResource clientResource = new ExternalResource() {
+
+ @Override
+ protected void before() throws Throwable {
+ clientBuilder = HttpAsyncClientBuilder.create().setConnectionManager(connManager);
+ }
+
+ @Override
+ protected void after() {
+ if (httpclient != null) {
+ httpclient.shutdown(ShutdownType.GRACEFUL);
+ httpclient = null;
+ }
+ }
+
+ };
+
+ public HttpHost start() throws Exception {
+ server.start();
+ final ListenerEndpoint listener = server.listen(new InetSocketAddress(0));
+ httpclient = clientBuilder.build();
+ httpclient.start();
+ listener.waitFor();
+ final InetSocketAddress address = (InetSocketAddress) listener.getAddress();
+ return new HttpHost("localhost", address.getPort(), scheme.name());
+ }
+
+}
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/IntegrationTestBase.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/IntegrationTestBase.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/IntegrationTestBase.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/LocalAsyncServerTestBase.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/LocalAsyncServerTestBase.java?rev=1793320&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/LocalAsyncServerTestBase.java (added)
+++ httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/LocalAsyncServerTestBase.java Mon May 1 12:39:16 2017
@@ -0,0 +1,114 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.client5.testing.async;
+
+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.H2TlsStrategy;
+import org.apache.hc.client5.testing.SSLTestContexts;
+import org.apache.hc.core5.function.Supplier;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
+import org.apache.hc.core5.reactor.IOReactorConfig;
+import org.apache.hc.core5.testing.nio.Http2TestServer;
+import org.apache.hc.core5.util.TimeValue;
+import org.junit.Rule;
+import org.junit.rules.ExternalResource;
+
+public abstract class LocalAsyncServerTestBase {
+
+ protected final URIScheme scheme;
+
+ public LocalAsyncServerTestBase(final URIScheme scheme) {
+ this.scheme = scheme;
+ }
+
+ public LocalAsyncServerTestBase() {
+ this(URIScheme.HTTP);
+ }
+
+ protected Http2TestServer server;
+ protected PoolingAsyncClientConnectionManager connManager;
+
+ @Rule
+ public ExternalResource serverResource = new ExternalResource() {
+
+ @Override
+ protected void before() throws Throwable {
+ server = new Http2TestServer(
+ IOReactorConfig.DEFAULT,
+ scheme == URIScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null);
+ server.register("/echo/*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new AsyncEchoHandler();
+ }
+
+ });
+ server.register("/random/*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new AsyncRandomHandler();
+ }
+
+ });
+ }
+
+ @Override
+ protected void after() {
+ if (server != null) {
+ server.shutdown(TimeValue.ofSeconds(5));
+ server = null;
+ }
+ }
+
+ };
+
+ @Rule
+ public ExternalResource connManagerResource = new ExternalResource() {
+
+ @Override
+ protected void before() throws Throwable {
+ connManager = PoolingAsyncClientConnectionManagerBuilder.create()
+ .setTlsStrategy(new H2TlsStrategy(SSLTestContexts.createClientSSLContext()))
+ .build();
+ }
+
+ @Override
+ protected void after() {
+ if (connManager != null) {
+ connManager.close();
+ connManager = null;
+ }
+ }
+
+ };
+
+}
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/LocalAsyncServerTestBase.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/LocalAsyncServerTestBase.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/LocalAsyncServerTestBase.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestAsyncRedirects.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestAsyncRedirects.java?rev=1793320&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestAsyncRedirects.java (added)
+++ httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestAsyncRedirects.java Mon May 1 12:39:16 2017
@@ -0,0 +1,904 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.client5.testing.async;
+
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutionException;
+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.SimpleRequestProducer;
+import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.cookie.BasicCookieStore;
+import org.apache.hc.client5.http.cookie.CookieStore;
+import org.apache.hc.client5.http.impl.cookie.BasicClientCookie;
+import org.apache.hc.client5.http.protocol.CircularRedirectException;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.client5.http.protocol.RedirectException;
+import org.apache.hc.client5.testing.SSLTestContexts;
+import org.apache.hc.core5.function.Supplier;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpException;
+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.ProtocolException;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
+import org.apache.hc.core5.http.protocol.HttpCoreContext;
+import org.apache.hc.core5.net.URIBuilder;
+import org.apache.hc.core5.reactor.IOReactorConfig;
+import org.apache.hc.core5.reactor.ListenerEndpoint;
+import org.apache.hc.core5.testing.nio.Http2TestServer;
+import org.apache.hc.core5.util.TimeValue;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * Redirection test cases.
+ */
+@RunWith(Parameterized.class)
+public class TestAsyncRedirects extends IntegrationTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<Object[]> protocols() {
+ return Arrays.asList(new Object[][]{
+ {URIScheme.HTTP},
+ {URIScheme.HTTPS},
+ });
+ }
+
+ public TestAsyncRedirects(final URIScheme scheme) {
+ super(scheme);
+ }
+
+ static class BasicRedirectService extends AbstractSimpleServerExchangeHandler {
+
+ private final int statuscode;
+ private final boolean keepAlive;
+
+ public BasicRedirectService(final int statuscode, final boolean keepAlive) {
+ super();
+ this.statuscode = statuscode;
+ this.keepAlive = keepAlive;
+ }
+
+ public BasicRedirectService(final int statuscode) {
+ this(statuscode, true);
+ }
+
+ @Override
+ protected SimpleHttpResponse handle(
+ final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
+ try {
+ final URI requestURI = request.getUri();
+ final String path = requestURI.getPath();
+ if (path.equals("/oldlocation/")) {
+ final SimpleHttpResponse response = new SimpleHttpResponse(statuscode);
+ response.addHeader(new BasicHeader("Location",
+ new URIBuilder(requestURI).setPath("/newlocation/").build()));
+ if (!keepAlive) {
+ response.addHeader(new BasicHeader("Connection", "close"));
+ }
+ return response;
+ } else if (path.equals("/newlocation/")) {
+ return new SimpleHttpResponse(HttpStatus.SC_OK, "Successful redirect", ContentType.TEXT_PLAIN);
+ } else {
+ return new SimpleHttpResponse(HttpStatus.SC_NOT_FOUND, null, null);
+ }
+ } catch (final URISyntaxException ex) {
+ throw new ProtocolException(ex.getMessage(), ex);
+ }
+ }
+
+ }
+
+ static class CircularRedirectService extends AbstractSimpleServerExchangeHandler {
+
+ public CircularRedirectService() {
+ super();
+ }
+
+ @Override
+ protected SimpleHttpResponse handle(
+ final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
+ try {
+ final URI requestURI = request.getUri();
+ final String path = requestURI.getPath();
+ if (path.startsWith("/circular-oldlocation")) {
+ final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY);
+ response.addHeader(new BasicHeader("Location", "/circular-location2"));
+ return response;
+ } else if (path.startsWith("/circular-location2")) {
+ final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY);
+ response.addHeader(new BasicHeader("Location", "/circular-oldlocation"));
+ return response;
+ } else {
+ return new SimpleHttpResponse(HttpStatus.SC_NOT_FOUND, null, null);
+ }
+ } catch (final URISyntaxException ex) {
+ throw new ProtocolException(ex.getMessage(), ex);
+ }
+ }
+
+ }
+
+ static class RelativeRedirectService extends AbstractSimpleServerExchangeHandler {
+
+ @Override
+ protected SimpleHttpResponse handle(
+ final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
+ try {
+ final URI requestURI = request.getUri();
+ final String path = requestURI.getPath();
+ if (path.equals("/oldlocation/")) {
+ final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY);
+ response.addHeader(new BasicHeader("Location", "/relativelocation/"));
+ return response;
+ } else if (path.equals("/relativelocation/")) {
+ return new SimpleHttpResponse(HttpStatus.SC_OK, "Successful redirect", ContentType.TEXT_PLAIN);
+ } else {
+ return new SimpleHttpResponse(HttpStatus.SC_NOT_FOUND);
+ }
+ } catch (final URISyntaxException ex) {
+ throw new ProtocolException(ex.getMessage(), ex);
+ }
+ }
+ }
+
+ static class RelativeRedirectService2 extends AbstractSimpleServerExchangeHandler {
+
+ @Override
+ protected SimpleHttpResponse handle(
+ final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
+ try {
+ final URI requestURI = request.getUri();
+ final String path = requestURI.getPath();
+ if (path.equals("/test/oldlocation")) {
+ final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY);
+ response.addHeader(new BasicHeader("Location", "relativelocation"));
+ return response;
+ } else if (path.equals("/test/relativelocation")) {
+ return new SimpleHttpResponse(HttpStatus.SC_OK, "Successful redirect", ContentType.TEXT_PLAIN);
+ } else {
+ return new SimpleHttpResponse(HttpStatus.SC_NOT_FOUND);
+ }
+ } catch (final URISyntaxException ex) {
+ throw new ProtocolException(ex.getMessage(), ex);
+ }
+ }
+
+ }
+
+ @Test
+ public void testBasicRedirect300() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new BasicRedirectService(HttpStatus.SC_MULTIPLE_CHOICES, false);
+ }
+
+ });
+ final HttpHost target = start();
+
+ final HttpClientContext context = HttpClientContext.create();
+ final Future<SimpleHttpResponse> future = httpclient.execute(
+ SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+ final HttpResponse response = future.get();
+ Assert.assertNotNull(response);
+
+ final HttpRequest request = context.getRequest();
+
+ Assert.assertEquals(HttpStatus.SC_MULTIPLE_CHOICES, response.getCode());
+ Assert.assertEquals("/oldlocation/", request.getRequestUri());
+ }
+
+ @Test
+ public void testBasicRedirect301KeepAlive() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new BasicRedirectService(HttpStatus.SC_MOVED_PERMANENTLY, true);
+ }
+
+ });
+
+ final HttpHost target = start();
+ final HttpClientContext context = HttpClientContext.create();
+ final Future<SimpleHttpResponse> future = httpclient.execute(
+ SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+ final HttpResponse response = future.get();
+ Assert.assertNotNull(response);
+
+ final HttpRequest request = context.getRequest();
+
+ Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+ Assert.assertEquals("/newlocation/", request.getRequestUri());
+ Assert.assertEquals(target, new HttpHost(request.getAuthority(), request.getScheme()));
+ }
+
+ @Test
+ public void testBasicRedirect301NoKeepAlive() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new BasicRedirectService(HttpStatus.SC_MOVED_PERMANENTLY, false);
+ }
+
+ });
+
+ final HttpHost target = start();
+ final HttpClientContext context = HttpClientContext.create();
+ final Future<SimpleHttpResponse> future = httpclient.execute(
+ SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+ final HttpResponse response = future.get();
+ Assert.assertNotNull(response);
+
+ final HttpRequest request = context.getRequest();
+
+ Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+ Assert.assertEquals("/newlocation/", request.getRequestUri());
+ Assert.assertEquals(target, new HttpHost(request.getAuthority(), request.getScheme()));
+ }
+
+ @Test
+ public void testBasicRedirect302() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new BasicRedirectService(HttpStatus.SC_MOVED_TEMPORARILY);
+ }
+
+ });
+ final HttpHost target = start();
+ final HttpClientContext context = HttpClientContext.create();
+ final Future<SimpleHttpResponse> future = httpclient.execute(
+ SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+ final HttpResponse response = future.get();
+ Assert.assertNotNull(response);
+
+ final HttpRequest request = context.getRequest();
+
+ Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+ Assert.assertEquals("/newlocation/", request.getRequestUri());
+ Assert.assertEquals(target, new HttpHost(request.getAuthority(), request.getScheme()));
+ }
+
+ @Test
+ public void testBasicRedirect302NoLocation() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new AbstractSimpleServerExchangeHandler() {
+
+ @Override
+ protected SimpleHttpResponse handle(
+ final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
+ return new SimpleHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY);
+ }
+
+ };
+ }
+
+ });
+ final HttpHost target = start();
+ final HttpClientContext context = HttpClientContext.create();
+ final Future<SimpleHttpResponse> future = httpclient.execute(
+ SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+ final HttpResponse response = future.get();
+ Assert.assertNotNull(response);
+
+ final HttpRequest request = context.getRequest();
+ Assert.assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getCode());
+ Assert.assertEquals("/oldlocation/", request.getRequestUri());
+ Assert.assertEquals(target, new HttpHost(request.getAuthority(), request.getScheme()));
+ }
+
+ @Test
+ public void testBasicRedirect303() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new BasicRedirectService(HttpStatus.SC_SEE_OTHER);
+ }
+
+ });
+ final HttpHost target = start();
+ final HttpClientContext context = HttpClientContext.create();
+ final Future<SimpleHttpResponse> future = httpclient.execute(
+ SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+ final HttpResponse response = future.get();
+ Assert.assertNotNull(response);
+
+ final HttpRequest request = context.getRequest();
+
+ Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+ Assert.assertEquals("/newlocation/", request.getRequestUri());
+ Assert.assertEquals(target, new HttpHost(request.getAuthority(), request.getScheme()));
+ }
+
+ @Test
+ public void testBasicRedirect304() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new BasicRedirectService(HttpStatus.SC_NOT_MODIFIED);
+ }
+
+ });
+ final HttpHost target = start();
+ final HttpClientContext context = HttpClientContext.create();
+ final Future<SimpleHttpResponse> future = httpclient.execute(
+ SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+ final HttpResponse response = future.get();
+ Assert.assertNotNull(response);
+
+ final HttpRequest request = context.getRequest();
+
+ Assert.assertEquals(HttpStatus.SC_NOT_MODIFIED, response.getCode());
+ Assert.assertEquals("/oldlocation/", request.getRequestUri());
+ }
+
+ @Test
+ public void testBasicRedirect305() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new BasicRedirectService(HttpStatus.SC_USE_PROXY);
+ }
+
+ });
+ final HttpHost target = start();
+ final HttpClientContext context = HttpClientContext.create();
+ final Future<SimpleHttpResponse> future = httpclient.execute(
+ SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+ final HttpResponse response = future.get();
+ Assert.assertNotNull(response);
+
+ final HttpRequest request = context.getRequest();
+
+ Assert.assertEquals(HttpStatus.SC_USE_PROXY, response.getCode());
+ Assert.assertEquals("/oldlocation/", request.getRequestUri());
+ }
+
+ @Test
+ public void testBasicRedirect307() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new BasicRedirectService(HttpStatus.SC_TEMPORARY_REDIRECT);
+ }
+
+ });
+ final HttpHost target = start();
+ final HttpClientContext context = HttpClientContext.create();
+ final Future<SimpleHttpResponse> future = httpclient.execute(
+ SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+ final HttpResponse response = future.get();
+ Assert.assertNotNull(response);
+
+ final HttpRequest request = context.getRequest();
+
+ Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+ Assert.assertEquals("/newlocation/", request.getRequestUri());
+ Assert.assertEquals(target, new HttpHost(request.getAuthority(), request.getScheme()));
+ }
+
+ @Test(expected=ExecutionException.class)
+ public void testMaxRedirectCheck() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new CircularRedirectService();
+ }
+
+ });
+ final HttpHost target = start();
+
+ final RequestConfig config = RequestConfig.custom()
+ .setCircularRedirectsAllowed(true)
+ .setMaxRedirects(5).build();
+ try {
+ final Future<SimpleHttpResponse> future = httpclient.execute(
+ new SimpleRequestProducer(
+ SimpleHttpRequest.get(target, "/circular-oldlocation/"), config),
+ new SimpleResponseConsumer(), null);
+ future.get();
+ } catch (final ExecutionException e) {
+ Assert.assertTrue(e.getCause() instanceof RedirectException);
+ throw e;
+ }
+ }
+
+ @Test(expected=ExecutionException.class)
+ public void testCircularRedirect() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new CircularRedirectService();
+ }
+
+ });
+ final HttpHost target = start();
+
+ final RequestConfig config = RequestConfig.custom()
+ .setCircularRedirectsAllowed(false)
+ .build();
+ try {
+ final Future<SimpleHttpResponse> future = httpclient.execute(
+ new SimpleRequestProducer(
+ SimpleHttpRequest.get(target, "/circular-oldlocation/"), config),
+ new SimpleResponseConsumer(), null);
+ future.get();
+ } catch (final ExecutionException e) {
+ Assert.assertTrue(e.getCause() instanceof CircularRedirectException);
+ throw e;
+ }
+ }
+
+ @Test
+ public void testPostRedirectSeeOther() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new BasicRedirectService(HttpStatus.SC_SEE_OTHER);
+ }
+
+ });
+ final HttpHost target = start();
+
+ final HttpClientContext context = HttpClientContext.create();
+
+ final Future<SimpleHttpResponse> future = httpclient.execute(
+ SimpleHttpRequest.post(target, "/oldlocation/", "stuff", ContentType.TEXT_PLAIN), context, null);
+ final HttpResponse response = future.get();
+ Assert.assertNotNull(response);
+
+ final HttpRequest request = context.getRequest();
+
+ Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+ Assert.assertEquals("/newlocation/", request.getRequestUri());
+ Assert.assertEquals("GET", request.getMethod());
+ }
+
+ @Test
+ public void testRelativeRedirect() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new RelativeRedirectService();
+ }
+
+ });
+ final HttpHost target = start();
+
+ final HttpClientContext context = HttpClientContext.create();
+
+ final Future<SimpleHttpResponse> future = httpclient.execute(
+ SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+ final HttpResponse response = future.get();
+ Assert.assertNotNull(response);
+
+ final HttpRequest request = context.getRequest();
+
+ Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+ Assert.assertEquals("/relativelocation/", request.getRequestUri());
+ Assert.assertEquals(target, new HttpHost(request.getAuthority(), request.getScheme()));
+ }
+
+ @Test
+ public void testRelativeRedirect2() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new RelativeRedirectService2();
+ }
+
+ });
+ final HttpHost target = start();
+
+ final HttpClientContext context = HttpClientContext.create();
+
+ final Future<SimpleHttpResponse> future = httpclient.execute(
+ SimpleHttpRequest.get(target, "/test/oldlocation"), context, null);
+ final HttpResponse response = future.get();
+ Assert.assertNotNull(response);
+
+ final HttpRequest request = context.getRequest();
+
+ Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+ Assert.assertEquals("/test/relativelocation", request.getRequestUri());
+ Assert.assertEquals(target, new HttpHost(request.getAuthority(), request.getScheme()));
+ }
+
+ static class BogusRedirectService extends AbstractSimpleServerExchangeHandler {
+
+ private final String url;
+
+ public BogusRedirectService(final String url) {
+ super();
+ this.url = url;
+ }
+
+ @Override
+ protected SimpleHttpResponse handle(
+ final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
+ try {
+ final URI requestURI = request.getUri();
+ final String path = requestURI.getPath();
+ if (path.equals("/oldlocation/")) {
+ final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY);
+ response.addHeader(new BasicHeader("Location", url));
+ return response;
+ } else if (path.equals("/relativelocation/")) {
+ return new SimpleHttpResponse(HttpStatus.SC_OK, "Successful redirect", ContentType.TEXT_PLAIN);
+ } else {
+ return new SimpleHttpResponse(HttpStatus.SC_NOT_FOUND);
+ }
+ } catch (final URISyntaxException ex) {
+ throw new ProtocolException(ex.getMessage(), ex);
+ }
+ }
+
+ }
+
+ @Test(expected=ExecutionException.class)
+ public void testRejectBogusRedirectLocation() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new BogusRedirectService("xxx://bogus");
+ }
+
+ });
+ final HttpHost target = start();
+
+ try {
+ final Future<SimpleHttpResponse> future = httpclient.execute(
+ SimpleHttpRequest.get(target, "/oldlocation/"), null);
+ future.get();
+ } catch (final ExecutionException ex) {
+ Assert.assertTrue(ex.getCause() instanceof HttpException);
+ throw ex;
+ }
+ }
+
+ @Test(expected=ExecutionException.class)
+ public void testRejectInvalidRedirectLocation() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new BogusRedirectService("/newlocation/?p=I have spaces");
+ }
+
+ });
+ final HttpHost target = start();
+
+ try {
+ final Future<SimpleHttpResponse> future = httpclient.execute(
+ SimpleHttpRequest.get(target, "/oldlocation/"), null);
+ future.get();
+ } catch (final ExecutionException e) {
+ Assert.assertTrue(e.getCause() instanceof ProtocolException);
+ throw e;
+ }
+ }
+
+ @Test @Ignore
+ public void testRedirectWithCookie() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new BasicRedirectService(HttpStatus.SC_MOVED_TEMPORARILY);
+ }
+
+ });
+ final HttpHost target = start();
+
+ final CookieStore cookieStore = new BasicCookieStore();
+ final HttpClientContext context = HttpClientContext.create();
+ context.setCookieStore(cookieStore);
+
+ final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+ cookie.setDomain(target.getHostName());
+ cookie.setPath("/");
+
+ cookieStore.addCookie(cookie);
+
+ final Future<SimpleHttpResponse> future = httpclient.execute(
+ SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+ final HttpResponse response = future.get();
+ Assert.assertNotNull(response);
+
+ final HttpRequest request = context.getRequest();
+
+ Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+ Assert.assertEquals("/newlocation/", request.getRequestUri());
+
+ final Header[] headers = request.getHeaders("Cookie");
+ Assert.assertEquals("There can only be one (cookie)", 1, headers.length);
+ }
+
+ @Test
+ public void testDefaultHeadersRedirect() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new BasicRedirectService(HttpStatus.SC_MOVED_TEMPORARILY);
+ }
+
+ });
+
+ final List<Header> defaultHeaders = new ArrayList<>(1);
+ defaultHeaders.add(new BasicHeader(HttpHeaders.USER_AGENT, "my-test-client"));
+ clientBuilder.setDefaultHeaders(defaultHeaders);
+
+ final HttpHost target = start();
+
+ final HttpClientContext context = HttpClientContext.create();
+
+ final Future<SimpleHttpResponse> future = httpclient.execute(
+ SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+ final HttpResponse response = future.get();
+ Assert.assertNotNull(response);
+
+ final HttpRequest request = context.getRequest();
+
+ Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+ Assert.assertEquals("/newlocation/", request.getRequestUri());
+
+ final Header header = request.getFirstHeader(HttpHeaders.USER_AGENT);
+ Assert.assertEquals("my-test-client", header.getValue());
+ }
+
+ static class CrossSiteRedirectService extends AbstractSimpleServerExchangeHandler {
+
+ private final HttpHost host;
+
+ public CrossSiteRedirectService(final HttpHost host) {
+ super();
+ this.host = host;
+ }
+
+ @Override
+ protected SimpleHttpResponse handle(
+ final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
+ final String location;
+ try {
+ final URIBuilder uribuilder = new URIBuilder(request.getUri());
+ uribuilder.setScheme(host.getSchemeName());
+ uribuilder.setHost(host.getHostName());
+ uribuilder.setPort(host.getPort());
+ uribuilder.setPath("/random/1024");
+ location = uribuilder.build().toASCIIString();
+ } catch (final URISyntaxException ex) {
+ throw new ProtocolException("Invalid request URI", ex);
+ }
+ final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_TEMPORARY_REDIRECT);
+ response.addHeader(new BasicHeader("Location", location));
+ return response;
+ }
+ }
+
+ @Test
+ public void testCrossSiteRedirect() throws Exception {
+ server.register("/random/*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new AsyncRandomHandler();
+ }
+
+ });
+ final HttpHost redirectTarget = start();
+
+ final Http2TestServer secondServer = new Http2TestServer(IOReactorConfig.DEFAULT,
+ scheme == URIScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null);
+ try {
+ secondServer.register("/redirect/*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new CrossSiteRedirectService(redirectTarget);
+ }
+
+ });
+
+ secondServer.start();
+ final ListenerEndpoint endpoint2 = secondServer.listen(new InetSocketAddress(0));
+ endpoint2.waitFor();
+
+ final InetSocketAddress address2 = (InetSocketAddress) endpoint2.getAddress();
+ final HttpHost initialTarget = new HttpHost("localhost", address2.getPort(), scheme.name());
+
+ final Queue<Future<SimpleHttpResponse>> queue = new ConcurrentLinkedQueue<>();
+ for (int i = 0; i < 1; i++) {
+ queue.add(httpclient.execute(SimpleHttpRequest.get(initialTarget, "/redirect/anywhere"), null));
+ }
+ while (!queue.isEmpty()) {
+ final Future<SimpleHttpResponse> future = queue.remove();
+ final HttpResponse response = future.get();
+ Assert.assertNotNull(response);
+ Assert.assertEquals(200, response.getCode());
+ }
+ } finally {
+ server.shutdown(TimeValue.ofSeconds(5));
+ }
+ }
+
+ private static class RomeRedirectService extends AbstractSimpleServerExchangeHandler {
+
+ @Override
+ protected SimpleHttpResponse handle(
+ final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
+ try {
+ final URI requestURI = request.getUri();
+ final String path = requestURI.getPath();
+ if (path.equals("/rome")) {
+ return new SimpleHttpResponse(HttpStatus.SC_OK, "Successful redirect", ContentType.TEXT_PLAIN);
+ } else {
+ final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY);
+ response.addHeader(new BasicHeader("Location", "/rome"));
+ return response;
+ }
+ } catch (final URISyntaxException ex) {
+ throw new ProtocolException(ex.getMessage(), ex);
+ }
+ }
+
+ }
+
+ @Test
+ public void testRepeatRequest() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new RomeRedirectService();
+ }
+
+ });
+ final HttpHost target = start();
+
+ final HttpClientContext context = HttpClientContext.create();
+
+ final Future<SimpleHttpResponse> future1 = httpclient.execute(
+ SimpleHttpRequest.get(target, "/rome"), context, null);
+ final HttpResponse response1 = future1.get();
+ Assert.assertNotNull(response1);
+
+ final Future<SimpleHttpResponse> future2 = httpclient.execute(
+ SimpleHttpRequest.get(target, "/rome"), context, null);
+ final HttpResponse response2 = future2.get();
+ Assert.assertNotNull(response2);
+
+ final HttpRequest request = context.getRequest();
+
+ Assert.assertEquals(HttpStatus.SC_OK, response2.getCode());
+ Assert.assertEquals("/rome", request.getRequestUri());
+ Assert.assertEquals(target, new HttpHost(request.getAuthority(), request.getScheme()));
+ }
+
+ @Test
+ public void testRepeatRequestRedirect() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new RomeRedirectService();
+ }
+
+ });
+ final HttpHost target = start();
+
+ final HttpClientContext context = HttpClientContext.create();
+
+ final Future<SimpleHttpResponse> future1 = httpclient.execute(
+ SimpleHttpRequest.get(target, "/lille"), context, null);
+ final HttpResponse response1 = future1.get();
+ Assert.assertNotNull(response1);
+
+ final Future<SimpleHttpResponse> future2 = httpclient.execute(
+ SimpleHttpRequest.get(target, "/lille"), context, null);
+ final HttpResponse response2 = future2.get();
+ Assert.assertNotNull(response2);
+
+ final HttpRequest request = context.getRequest();
+
+ Assert.assertEquals(HttpStatus.SC_OK, response2.getCode());
+ Assert.assertEquals("/rome", request.getRequestUri());
+ Assert.assertEquals(target, new HttpHost(request.getAuthority(), request.getScheme()));
+ }
+
+ @Test
+ public void testDifferentRequestSameRedirect() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new RomeRedirectService();
+ }
+
+ });
+ final HttpHost target = start();
+
+ final HttpClientContext context = HttpClientContext.create();
+
+ final Future<SimpleHttpResponse> future1 = httpclient.execute(
+ SimpleHttpRequest.get(target, "/alian"), context, null);
+ final HttpResponse response1 = future1.get();
+ Assert.assertNotNull(response1);
+
+ final Future<SimpleHttpResponse> future2 = httpclient.execute(
+ SimpleHttpRequest.get(target, "/lille"), context, null);
+ final HttpResponse response2 = future2.get();
+ Assert.assertNotNull(response2);
+
+
+ final HttpRequest request = context.getRequest();
+
+ Assert.assertEquals(HttpStatus.SC_OK, response2.getCode());
+ Assert.assertEquals("/rome", request.getRequestUri());
+ Assert.assertEquals(target, new HttpHost(request.getAuthority(), request.getScheme()));
+ }
+
+}
\ No newline at end of file
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestAsyncRedirects.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestAsyncRedirects.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestAsyncRedirects.java
------------------------------------------------------------------------------
svn:mime-type = text/plain