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  
28  package org.apache.hc.core5.testing.nio;
29  
30  import static org.hamcrest.MatcherAssert.assertThat;
31  
32  import java.net.InetSocketAddress;
33  import java.nio.charset.StandardCharsets;
34  import java.util.Random;
35  import java.util.concurrent.Future;
36  
37  import org.apache.hc.core5.http.ContentType;
38  import org.apache.hc.core5.http.HttpException;
39  import org.apache.hc.core5.http.HttpHeaders;
40  import org.apache.hc.core5.http.HttpHost;
41  import org.apache.hc.core5.http.HttpRequest;
42  import org.apache.hc.core5.http.HttpResponse;
43  import org.apache.hc.core5.http.HttpStatus;
44  import org.apache.hc.core5.http.HttpVersion;
45  import org.apache.hc.core5.http.Message;
46  import org.apache.hc.core5.http.Method;
47  import org.apache.hc.core5.http.URIScheme;
48  import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester;
49  import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer;
50  import org.apache.hc.core5.http.impl.bootstrap.StandardFilter;
51  import org.apache.hc.core5.http.message.BasicHttpRequest;
52  import org.apache.hc.core5.http.nio.AsyncEntityProducer;
53  import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers;
54  import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer;
55  import org.apache.hc.core5.http.nio.support.AbstractAsyncServerAuthFilter;
56  import org.apache.hc.core5.http.nio.support.BasicRequestProducer;
57  import org.apache.hc.core5.http.nio.support.BasicResponseConsumer;
58  import org.apache.hc.core5.http.protocol.HttpContext;
59  import org.apache.hc.core5.net.URIAuthority;
60  import org.apache.hc.core5.reactor.IOReactorConfig;
61  import org.apache.hc.core5.reactor.ListenerEndpoint;
62  import org.apache.hc.core5.testing.nio.extension.HttpAsyncRequesterResource;
63  import org.apache.hc.core5.testing.nio.extension.HttpAsyncServerResource;
64  import org.apache.hc.core5.util.Timeout;
65  import org.hamcrest.CoreMatchers;
66  import org.junit.jupiter.api.Test;
67  import org.junit.jupiter.api.extension.RegisterExtension;
68  
69  public abstract class Http1AuthenticationTest {
70  
71      private static final Timeout TIMEOUT = Timeout.ofMinutes(1);
72  
73      @RegisterExtension
74      private final HttpAsyncServerResource serverResource;
75      @RegisterExtension
76      private final HttpAsyncRequesterResource clientResource;
77  
78      public Http1AuthenticationTest(final boolean respondImmediately) {
79          this.serverResource = new HttpAsyncServerResource(bootstrap -> bootstrap
80                  .setIOReactorConfig(
81                          IOReactorConfig.custom()
82                                  .setSoTimeout(TIMEOUT)
83                                  .build())
84                  .setLookupRegistry(null) // same as the default
85                  .register("*", () -> new EchoHandler(2048))
86                  .replaceFilter(StandardFilter.EXPECT_CONTINUE.name(), new AbstractAsyncServerAuthFilter<String>(respondImmediately) {
87  
88                      @Override
89                      protected String parseChallengeResponse(
90                              final String challenge, final HttpContext context) throws HttpException {
91                          return challenge;
92                      }
93  
94                      @Override
95                      protected boolean authenticate(
96                              final String challengeResponse,
97                              final URIAuthority authority,
98                              final String requestUri,
99                              final HttpContext context) {
100                         return challengeResponse != null && challengeResponse.equals("let me pass");
101                     }
102 
103                     @Override
104                     protected String generateChallenge(
105                             final String challengeResponse,
106                             final URIAuthority authority,
107                             final String requestUri,
108                             final HttpContext context) {
109                         return "who goes there?";
110                     }
111 
112                     @Override
113                     protected AsyncEntityProducer generateResponseContent(final HttpResponse unauthorized) {
114                         return AsyncEntityProducers.create("You shall not pass!!!");
115                     }
116                 })
117         );
118         this.clientResource = new HttpAsyncRequesterResource(bootstrap -> bootstrap
119                 .setIOReactorConfig(IOReactorConfig.custom()
120                         .setSoTimeout(TIMEOUT)
121                         .build())
122         );
123     }
124 
125     @Test
126     public void testGetRequestAuthentication() throws Exception {
127         final HttpAsyncServer server = serverResource.start();
128         final Future<ListenerEndpoint> future = server.listen(new InetSocketAddress(0), URIScheme.HTTP);
129         final ListenerEndpoint listener = future.get();
130         final InetSocketAddress address = (InetSocketAddress) listener.getAddress();
131         final HttpAsyncRequester requester = clientResource.start();
132 
133         final HttpHost target = new HttpHost("localhost", address.getPort());
134 
135         final HttpRequest request1 = new BasicHttpRequest(Method.GET, target, "/stuff");
136         final Future<Message<HttpResponse, String>> resultFuture1 = requester.execute(
137                 new BasicRequestProducer(request1, null),
138                 new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null);
139         final Message<HttpResponse, String> message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
140         assertThat(message1, CoreMatchers.notNullValue());
141         final HttpResponse response1 = message1.getHead();
142         assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_UNAUTHORIZED));
143         final String body1 = message1.getBody();
144         assertThat(body1, CoreMatchers.equalTo("You shall not pass!!!"));
145 
146         final HttpRequest request2 = new BasicHttpRequest(Method.GET, target, "/stuff");
147         request2.setHeader(HttpHeaders.AUTHORIZATION, "let me pass");
148         final Future<Message<HttpResponse, String>> resultFuture2 = requester.execute(
149                 new BasicRequestProducer(request2, null),
150                 new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null);
151         final Message<HttpResponse, String> message2 = resultFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
152         assertThat(message2, CoreMatchers.notNullValue());
153         final HttpResponse response2 = message2.getHead();
154         assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
155         final String body2 = message2.getBody();
156         assertThat(body2, CoreMatchers.equalTo(""));
157     }
158 
159     @Test
160     public void testPostRequestAuthentication() throws Exception {
161         final HttpAsyncServer server = serverResource.start();
162         final Future<ListenerEndpoint> future = server.listen(new InetSocketAddress(0), URIScheme.HTTP);
163         final ListenerEndpoint listener = future.get();
164         final InetSocketAddress address = (InetSocketAddress) listener.getAddress();
165         final HttpAsyncRequester requester = clientResource.start();
166 
167         final HttpHost target = new HttpHost("localhost", address.getPort());
168         final Random rnd = new Random();
169         final byte[] stuff = new byte[10240];
170         for (int i = 0; i < stuff.length; i++) {
171             stuff[i] = (byte) ('a' + rnd.nextInt(10));
172         }
173         final HttpRequest request1 = new BasicHttpRequest(Method.POST, target, "/stuff");
174         final Future<Message<HttpResponse, String>> resultFuture1 = requester.execute(
175                 new BasicRequestProducer(request1, AsyncEntityProducers.create(stuff, ContentType.TEXT_PLAIN)),
176                 new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null);
177         final Message<HttpResponse, String> message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
178         assertThat(message1, CoreMatchers.notNullValue());
179         final HttpResponse response1 = message1.getHead();
180         assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_UNAUTHORIZED));
181         final String body1 = message1.getBody();
182         assertThat(body1, CoreMatchers.equalTo("You shall not pass!!!"));
183 
184         final HttpRequest request2 = new BasicHttpRequest(Method.POST, target, "/stuff");
185         request2.setHeader(HttpHeaders.AUTHORIZATION, "let me pass");
186         final Future<Message<HttpResponse, String>> resultFuture2 = requester.execute(
187                 new BasicRequestProducer(request2, AsyncEntityProducers.create(stuff, ContentType.TEXT_PLAIN)),
188                 new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null);
189         final Message<HttpResponse, String> message2 = resultFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
190         assertThat(message2, CoreMatchers.notNullValue());
191         final HttpResponse response2 = message2.getHead();
192         assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
193         final String body2 = message2.getBody();
194         assertThat(body2, CoreMatchers.equalTo(new String(stuff, StandardCharsets.US_ASCII)));
195     }
196 
197     @Test
198     public void testPostRequestAuthenticationNoExpectContinue() throws Exception {
199         final HttpAsyncServer server = serverResource.start();
200         final Future<ListenerEndpoint> future = server.listen(new InetSocketAddress(0), URIScheme.HTTP);
201         final ListenerEndpoint listener = future.get();
202         final InetSocketAddress address = (InetSocketAddress) listener.getAddress();
203         final HttpAsyncRequester requester = clientResource.start();
204 
205         final HttpHost target = new HttpHost("localhost", address.getPort());
206         final Random rnd = new Random();
207         final byte[] stuff = new byte[10240];
208         for (int i = 0; i < stuff.length; i++) {
209             stuff[i] = (byte) ('a' + rnd.nextInt(10));
210         }
211 
212         final HttpRequest request1 = new BasicHttpRequest(Method.POST, target, "/stuff");
213         request1.setVersion(HttpVersion.HTTP_1_0);
214         final Future<Message<HttpResponse, String>> resultFuture1 = requester.execute(
215                 new BasicRequestProducer(request1, AsyncEntityProducers.create(stuff, ContentType.TEXT_PLAIN)),
216                 new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null);
217         final Message<HttpResponse, String> message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
218         assertThat(message1, CoreMatchers.notNullValue());
219         final HttpResponse response1 = message1.getHead();
220         assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_UNAUTHORIZED));
221         final String body1 = message1.getBody();
222         assertThat(body1, CoreMatchers.equalTo("You shall not pass!!!"));
223 
224         final HttpRequest request2 = new BasicHttpRequest(Method.POST, target, "/stuff");
225         request2.setVersion(HttpVersion.HTTP_1_0);
226         request2.setHeader(HttpHeaders.AUTHORIZATION, "let me pass");
227         final Future<Message<HttpResponse, String>> resultFuture2 = requester.execute(
228                 new BasicRequestProducer(request2, AsyncEntityProducers.create(stuff, ContentType.TEXT_PLAIN)),
229                 new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null);
230         final Message<HttpResponse, String> message2 = resultFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
231         assertThat(message2, CoreMatchers.notNullValue());
232         final HttpResponse response2 = message2.getHead();
233         assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
234         final String body2 = message2.getBody();
235         assertThat(body2, CoreMatchers.equalTo(new String(stuff, StandardCharsets.US_ASCII)));
236     }
237 
238 }