1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.core5.testing.nio;
29
30 import java.net.InetSocketAddress;
31 import java.nio.charset.StandardCharsets;
32 import java.util.Arrays;
33 import java.util.Collection;
34 import java.util.Random;
35 import java.util.concurrent.Future;
36
37 import org.apache.hc.core5.function.Supplier;
38 import org.apache.hc.core5.http.ContentType;
39 import org.apache.hc.core5.http.HttpException;
40 import org.apache.hc.core5.http.HttpHeaders;
41 import org.apache.hc.core5.http.HttpHost;
42 import org.apache.hc.core5.http.HttpRequest;
43 import org.apache.hc.core5.http.HttpResponse;
44 import org.apache.hc.core5.http.HttpStatus;
45 import org.apache.hc.core5.http.HttpVersion;
46 import org.apache.hc.core5.http.Message;
47 import org.apache.hc.core5.http.Method;
48 import org.apache.hc.core5.http.URIScheme;
49 import org.apache.hc.core5.http.impl.bootstrap.AsyncRequesterBootstrap;
50 import org.apache.hc.core5.http.impl.bootstrap.AsyncServerBootstrap;
51 import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester;
52 import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer;
53 import org.apache.hc.core5.http.impl.bootstrap.StandardFilter;
54 import org.apache.hc.core5.http.message.BasicHttpRequest;
55 import org.apache.hc.core5.http.nio.AsyncEntityProducer;
56 import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
57 import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers;
58 import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer;
59 import org.apache.hc.core5.http.nio.support.AbstractAsyncServerAuthFilter;
60 import org.apache.hc.core5.http.nio.support.BasicRequestProducer;
61 import org.apache.hc.core5.http.nio.support.BasicResponseConsumer;
62 import org.apache.hc.core5.http.protocol.HttpContext;
63 import org.apache.hc.core5.io.CloseMode;
64 import org.apache.hc.core5.net.URIAuthority;
65 import org.apache.hc.core5.reactor.IOReactorConfig;
66 import org.apache.hc.core5.reactor.ListenerEndpoint;
67 import org.apache.hc.core5.testing.classic.LoggingConnPoolListener;
68 import org.apache.hc.core5.util.Timeout;
69 import org.hamcrest.CoreMatchers;
70 import org.hamcrest.MatcherAssert;
71 import org.junit.Rule;
72 import org.junit.Test;
73 import org.junit.rules.ExternalResource;
74 import org.junit.runner.RunWith;
75 import org.junit.runners.Parameterized;
76 import org.slf4j.Logger;
77 import org.slf4j.LoggerFactory;
78
79 @RunWith(Parameterized.class)
80 public class Http1AuthenticationTest {
81
82 @Parameterized.Parameters(name = "respond immediately on auth failure: {0}")
83 public static Collection<Object[]> data() {
84 return Arrays.asList(new Object[][]{
85 { Boolean.FALSE },
86 { Boolean.TRUE }
87 });
88 }
89
90 private static final Timeout TIMEOUT = Timeout.ofMinutes(1);
91
92 private final Logger log = LoggerFactory.getLogger(getClass());
93
94 private final boolean respondImmediately;
95 private HttpAsyncServer server;
96
97 public Http1AuthenticationTest(final Boolean respondImmediately) {
98 this.respondImmediately = respondImmediately;
99 }
100
101 @Rule
102 public ExternalResource serverResource = new ExternalResource() {
103
104 @Override
105 protected void before() throws Throwable {
106 log.debug("Starting up test server");
107 server = AsyncServerBootstrap.bootstrap()
108 .setLookupRegistry(null)
109 .setIOReactorConfig(
110 IOReactorConfig.custom()
111 .setSoTimeout(TIMEOUT)
112 .build())
113 .register("*", new Supplier<AsyncServerExchangeHandler>() {
114
115 @Override
116 public AsyncServerExchangeHandler get() {
117 return new EchoHandler(2048);
118 }
119
120 })
121 .replaceFilter(StandardFilter.EXPECT_CONTINUE.name(), new AbstractAsyncServerAuthFilter<String>(respondImmediately) {
122
123 @Override
124 protected String parseChallengeResponse(
125 final String challenge, final HttpContext context) throws HttpException {
126 return challenge;
127 }
128
129 @Override
130 protected boolean authenticate(
131 final String challengeResponse,
132 final URIAuthority authority,
133 final String requestUri,
134 final HttpContext context) {
135 return challengeResponse != null && challengeResponse.equals("let me pass");
136 }
137
138 @Override
139 protected String generateChallenge(
140 final String challengeResponse,
141 final URIAuthority authority,
142 final String requestUri,
143 final HttpContext context) {
144 return "who goes there?";
145 }
146
147 @Override
148 protected AsyncEntityProducer generateResponseContent(final HttpResponse unauthorized) {
149 return AsyncEntityProducers.create("You shall not pass!!!");
150 }
151 })
152 .setIOSessionListener(LoggingIOSessionListener.INSTANCE)
153 .setStreamListener(LoggingHttp1StreamListener.INSTANCE_SERVER)
154 .setIOSessionDecorator(LoggingIOSessionDecorator.INSTANCE)
155 .create();
156 }
157
158 @Override
159 protected void after() {
160 log.debug("Shutting down test server");
161 if (server != null) {
162 try {
163 server.close(CloseMode.IMMEDIATE);
164 } catch (final Exception ignore) {
165 }
166 }
167 }
168
169 };
170
171 private HttpAsyncRequester requester;
172
173 @Rule
174 public ExternalResource clientResource = new ExternalResource() {
175
176 @Override
177 protected void before() throws Throwable {
178 log.debug("Starting up test client");
179 requester = AsyncRequesterBootstrap.bootstrap()
180 .setIOReactorConfig(IOReactorConfig.custom()
181 .setSoTimeout(TIMEOUT)
182 .build())
183 .setMaxTotal(2)
184 .setDefaultMaxPerRoute(2)
185 .setIOSessionListener(LoggingIOSessionListener.INSTANCE)
186 .setStreamListener(LoggingHttp1StreamListener.INSTANCE_CLIENT)
187 .setConnPoolListener(LoggingConnPoolListener.INSTANCE)
188 .setIOSessionDecorator(LoggingIOSessionDecorator.INSTANCE)
189 .create();
190 }
191
192 @Override
193 protected void after() {
194 log.debug("Shutting down test client");
195 if (requester != null) {
196 try {
197 requester.close(CloseMode.GRACEFUL);
198 } catch (final Exception ignore) {
199 }
200 }
201 }
202
203 };
204
205 @Test
206 public void testGetRequestAuthentication() throws Exception {
207 server.start();
208 final Future<ListenerEndpoint> future = server.listen(new InetSocketAddress(0), URIScheme.HTTP);
209 final ListenerEndpoint listener = future.get();
210 final InetSocketAddress address = (InetSocketAddress) listener.getAddress();
211 requester.start();
212
213 final HttpHost target = new HttpHost("localhost", address.getPort());
214
215 final HttpRequest request1 = new BasicHttpRequest(Method.GET, target, "/stuff");
216 final Future<Message<HttpResponse, String>> resultFuture1 = requester.execute(
217 new BasicRequestProducer(request1, null),
218 new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null);
219 final Message<HttpResponse, String> message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
220 MatcherAssert.assertThat(message1, CoreMatchers.notNullValue());
221 final HttpResponse response1 = message1.getHead();
222 MatcherAssert.assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_UNAUTHORIZED));
223 final String body1 = message1.getBody();
224 MatcherAssert.assertThat(body1, CoreMatchers.equalTo("You shall not pass!!!"));
225
226 final HttpRequest request2 = new BasicHttpRequest(Method.GET, target, "/stuff");
227 request2.setHeader(HttpHeaders.AUTHORIZATION, "let me pass");
228 final Future<Message<HttpResponse, String>> resultFuture2 = requester.execute(
229 new BasicRequestProducer(request2, null),
230 new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null);
231 final Message<HttpResponse, String> message2 = resultFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
232 MatcherAssert.assertThat(message2, CoreMatchers.notNullValue());
233 final HttpResponse response2 = message2.getHead();
234 MatcherAssert.assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
235 final String body2 = message2.getBody();
236 MatcherAssert.assertThat(body2, CoreMatchers.equalTo(""));
237 }
238
239 @Test
240 public void testPostRequestAuthentication() throws Exception {
241 server.start();
242 final Future<ListenerEndpoint> future = server.listen(new InetSocketAddress(0), URIScheme.HTTP);
243 final ListenerEndpoint listener = future.get();
244 final InetSocketAddress address = (InetSocketAddress) listener.getAddress();
245 requester.start();
246
247 final HttpHost target = new HttpHost("localhost", address.getPort());
248 final Random rnd = new Random();
249 final byte[] stuff = new byte[10240];
250 for (int i = 0; i < stuff.length; i++) {
251 stuff[i] = (byte)('a' + rnd.nextInt(10));
252 }
253 final HttpRequest request1 = new BasicHttpRequest(Method.POST, target, "/stuff");
254 final Future<Message<HttpResponse, String>> resultFuture1 = requester.execute(
255 new BasicRequestProducer(request1, AsyncEntityProducers.create(stuff, ContentType.TEXT_PLAIN)),
256 new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null);
257 final Message<HttpResponse, String> message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
258 MatcherAssert.assertThat(message1, CoreMatchers.notNullValue());
259 final HttpResponse response1 = message1.getHead();
260 MatcherAssert.assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_UNAUTHORIZED));
261 final String body1 = message1.getBody();
262 MatcherAssert.assertThat(body1, CoreMatchers.equalTo("You shall not pass!!!"));
263
264 final HttpRequest request2 = new BasicHttpRequest(Method.POST, target, "/stuff");
265 request2.setHeader(HttpHeaders.AUTHORIZATION, "let me pass");
266 final Future<Message<HttpResponse, String>> resultFuture2 = requester.execute(
267 new BasicRequestProducer(request2, AsyncEntityProducers.create(stuff, ContentType.TEXT_PLAIN)),
268 new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null);
269 final Message<HttpResponse, String> message2 = resultFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
270 MatcherAssert.assertThat(message2, CoreMatchers.notNullValue());
271 final HttpResponse response2 = message2.getHead();
272 MatcherAssert.assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
273 final String body2 = message2.getBody();
274 MatcherAssert.assertThat(body2, CoreMatchers.equalTo(new String(stuff, StandardCharsets.US_ASCII)));
275 }
276
277 @Test
278 public void testPostRequestAuthenticationNoExpectContinue() throws Exception {
279 server.start();
280 final Future<ListenerEndpoint> future = server.listen(new InetSocketAddress(0), URIScheme.HTTP);
281 final ListenerEndpoint listener = future.get();
282 final InetSocketAddress address = (InetSocketAddress) listener.getAddress();
283 requester.start();
284
285 final HttpHost target = new HttpHost("localhost", address.getPort());
286 final Random rnd = new Random();
287 final byte[] stuff = new byte[10240];
288 for (int i = 0; i < stuff.length; i++) {
289 stuff[i] = (byte)('a' + rnd.nextInt(10));
290 }
291
292 final HttpRequest request1 = new BasicHttpRequest(Method.POST, target, "/stuff");
293 request1.setVersion(HttpVersion.HTTP_1_0);
294 final Future<Message<HttpResponse, String>> resultFuture1 = requester.execute(
295 new BasicRequestProducer(request1, AsyncEntityProducers.create(stuff, ContentType.TEXT_PLAIN)),
296 new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null);
297 final Message<HttpResponse, String> message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
298 MatcherAssert.assertThat(message1, CoreMatchers.notNullValue());
299 final HttpResponse response1 = message1.getHead();
300 MatcherAssert.assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_UNAUTHORIZED));
301 final String body1 = message1.getBody();
302 MatcherAssert.assertThat(body1, CoreMatchers.equalTo("You shall not pass!!!"));
303
304 final HttpRequest request2 = new BasicHttpRequest(Method.POST, target, "/stuff");
305 request2.setVersion(HttpVersion.HTTP_1_0);
306 request2.setHeader(HttpHeaders.AUTHORIZATION, "let me pass");
307 final Future<Message<HttpResponse, String>> resultFuture2 = requester.execute(
308 new BasicRequestProducer(request2, AsyncEntityProducers.create(stuff, ContentType.TEXT_PLAIN)),
309 new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null);
310 final Message<HttpResponse, String> message2 = resultFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
311 MatcherAssert.assertThat(message2, CoreMatchers.notNullValue());
312 final HttpResponse response2 = message2.getHead();
313 MatcherAssert.assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
314 final String body2 = message2.getBody();
315 MatcherAssert.assertThat(body2, CoreMatchers.equalTo(new String(stuff, StandardCharsets.US_ASCII)));
316 }
317
318 }