View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.hc.client5.testing.async;
28  
29  import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
30  import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
31  import org.apache.hc.client5.http.config.RequestConfig;
32  import org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy;
33  import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
34  import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
35  import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
36  import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
37  import org.apache.hc.core5.function.Decorator;
38  import org.apache.hc.core5.function.Resolver;
39  import org.apache.hc.core5.http.HttpHost;
40  import org.apache.hc.core5.http.HttpRequest;
41  import org.apache.hc.core5.http.HttpStatus;
42  import org.apache.hc.core5.http.HttpVersion;
43  import org.apache.hc.core5.http.URIScheme;
44  import org.apache.hc.core5.http.config.Http1Config;
45  import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
46  import org.apache.hc.core5.http2.HttpVersionPolicy;
47  import org.apache.hc.core5.http2.config.H2Config;
48  import org.apache.hc.core5.util.TimeValue;
49  import org.hamcrest.CoreMatchers;
50  import org.junit.Assert;
51  import org.junit.Rule;
52  import org.junit.Test;
53  import org.junit.rules.ExternalResource;
54  import org.junit.runner.RunWith;
55  import org.junit.runners.Parameterized;
56  
57  import java.util.Arrays;
58  import java.util.Collection;
59  import java.util.concurrent.Future;
60  import java.util.concurrent.atomic.AtomicInteger;
61  
62  @RunWith(Parameterized.class)
63  public class TestHttp1RequestReExecution extends AbstractIntegrationTestBase<CloseableHttpAsyncClient> {
64  
65      @Parameterized.Parameters(name = "{0}")
66      public static Collection<Object[]> protocolVersions() {
67          return Arrays.asList(new Object[][]{
68                  { HttpVersion.HTTP_1_1 },
69                  { HttpVersion.HTTP_2 }
70          });
71      }
72  
73      private final HttpVersion version;
74  
75      public TestHttp1RequestReExecution(final HttpVersion version) {
76          super(URIScheme.HTTP);
77          this.version = version;
78      }
79  
80      HttpAsyncClientBuilder clientBuilder;
81      PoolingAsyncClientConnectionManager connManager;
82  
83      @Rule
84      public ExternalResource connManagerResource = new ExternalResource() {
85  
86          @Override
87          protected void before() throws Throwable {
88              connManager = PoolingAsyncClientConnectionManagerBuilder.create()
89                      .build();
90          }
91  
92          @Override
93          protected void after() {
94              if (connManager != null) {
95                  connManager.close();
96                  connManager = null;
97              }
98          }
99  
100     };
101 
102     @Rule
103     public ExternalResource clientBuilderResource = new ExternalResource() {
104 
105         @Override
106         protected void before() throws Throwable {
107             clientBuilder = HttpAsyncClientBuilder.create()
108                     .setDefaultRequestConfig(RequestConfig.custom()
109                             .setConnectionRequestTimeout(TIMEOUT)
110                             .setConnectTimeout(TIMEOUT)
111                             .build())
112                     .setConnectionManager(connManager)
113                     .setVersionPolicy(version.greaterEquals(HttpVersion.HTTP_2) ? HttpVersionPolicy.FORCE_HTTP_2 : HttpVersionPolicy.FORCE_HTTP_1);
114         }
115 
116     };
117 
118     @Override
119     public final HttpHost start() throws Exception {
120 
121         final Resolver<HttpRequest, TimeValue> serviceAvailabilityResolver = new Resolver<HttpRequest, TimeValue>() {
122 
123             private final AtomicInteger count = new AtomicInteger(0);
124 
125             @Override
126             public TimeValue resolve(final HttpRequest request) {
127                 final int n = count.incrementAndGet();
128                 return n <= 3 ? TimeValue.ofSeconds(1) : null;
129             }
130 
131         };
132 
133         if (version.greaterEquals(HttpVersion.HTTP_2)) {
134             return super.start(null, new Decorator<AsyncServerExchangeHandler>() {
135 
136                 @Override
137                 public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler handler) {
138                     return new ServiceUnavailableAsyncDecorator(handler, serviceAvailabilityResolver);
139                 }
140 
141             }, H2Config.DEFAULT);
142         } else {
143             return super.start(null, new Decorator<AsyncServerExchangeHandler>() {
144 
145                 @Override
146                 public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler handler) {
147                     return new ServiceUnavailableAsyncDecorator(handler, serviceAvailabilityResolver);
148                 }
149 
150             }, Http1Config.DEFAULT);
151         }
152     }
153 
154     @Override
155     protected CloseableHttpAsyncClient createClient() throws Exception {
156         return clientBuilder.build();
157     }
158 
159     @Test
160     public void testGiveUpAfterOneRetry() throws Exception {
161         clientBuilder.setRetryStrategy(new DefaultHttpRequestRetryStrategy(1, TimeValue.ofSeconds(1)));
162         final HttpHost target = start();
163         final Future<SimpleHttpResponse> future = httpclient.execute(
164                 SimpleRequestBuilder.get()
165                         .setHttpHost(target)
166                         .setPath("/random/2048")
167                         .build(), null);
168         final SimpleHttpResponse response = future.get();
169         Assert.assertThat(response, CoreMatchers.notNullValue());
170         Assert.assertThat(response.getCode(), CoreMatchers.equalTo(HttpStatus.SC_SERVICE_UNAVAILABLE));
171     }
172 
173     @Test
174     public void testDoNotGiveUpEasily() throws Exception {
175         clientBuilder.setRetryStrategy(new DefaultHttpRequestRetryStrategy(5, TimeValue.ofSeconds(1)));
176         final HttpHost target = start();
177         final Future<SimpleHttpResponse> future = httpclient.execute(
178                 SimpleRequestBuilder.get()
179                         .setHttpHost(target)
180                         .setPath("/random/2048")
181                         .build(), null);
182         final SimpleHttpResponse response = future.get();
183         Assert.assertThat(response, CoreMatchers.notNullValue());
184         Assert.assertThat(response.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
185     }
186 
187 }