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.classic;
29
30 import java.nio.charset.StandardCharsets;
31 import java.util.Arrays;
32 import java.util.Collection;
33 import java.util.Random;
34
35 import org.apache.hc.core5.http.ClassicHttpRequest;
36 import org.apache.hc.core5.http.ClassicHttpResponse;
37 import org.apache.hc.core5.http.ContentType;
38 import org.apache.hc.core5.http.HttpEntity;
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.HttpResponse;
43 import org.apache.hc.core5.http.HttpStatus;
44 import org.apache.hc.core5.http.HttpVersion;
45 import org.apache.hc.core5.http.Method;
46 import org.apache.hc.core5.http.impl.bootstrap.HttpRequester;
47 import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
48 import org.apache.hc.core5.http.impl.bootstrap.RequesterBootstrap;
49 import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap;
50 import org.apache.hc.core5.http.impl.bootstrap.StandardFilter;
51 import org.apache.hc.core5.http.io.SocketConfig;
52 import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
53 import org.apache.hc.core5.http.io.entity.EntityUtils;
54 import org.apache.hc.core5.http.io.entity.StringEntity;
55 import org.apache.hc.core5.http.io.support.AbstractHttpServerAuthFilter;
56 import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
57 import org.apache.hc.core5.http.protocol.HttpContext;
58 import org.apache.hc.core5.http.protocol.HttpCoreContext;
59 import org.apache.hc.core5.io.CloseMode;
60 import org.apache.hc.core5.net.URIAuthority;
61 import org.apache.hc.core5.util.Timeout;
62 import org.hamcrest.CoreMatchers;
63 import org.hamcrest.MatcherAssert;
64 import org.junit.Rule;
65 import org.junit.Test;
66 import org.junit.rules.ExternalResource;
67 import org.junit.runner.RunWith;
68 import org.junit.runners.Parameterized;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
71
72 @RunWith(Parameterized.class)
73 public class ClassicAuthenticationTest {
74
75 @Parameterized.Parameters(name = "respond immediately on auth failure: {0}")
76 public static Collection<Object[]> data() {
77 return Arrays.asList(new Object[][]{
78 { Boolean.FALSE },
79 { Boolean.TRUE }
80 });
81 }
82
83 private static final Timeout TIMEOUT = Timeout.ofSeconds(30);
84
85 private final Logger log = LoggerFactory.getLogger(getClass());
86
87 private final boolean respondImmediately;
88 private HttpServer server;
89
90 public ClassicAuthenticationTest(final Boolean respondImmediately) {
91 this.respondImmediately = respondImmediately;
92 }
93
94 @Rule
95 public ExternalResource serverResource = new ExternalResource() {
96
97 @Override
98 protected void before() throws Throwable {
99 log.debug("Starting up test server");
100 server = ServerBootstrap.bootstrap()
101 .setSocketConfig(
102 SocketConfig.custom()
103 .setSoTimeout(TIMEOUT)
104 .build())
105 .register("*", new EchoHandler())
106 .replaceFilter(StandardFilter.EXPECT_CONTINUE.name(), new AbstractHttpServerAuthFilter<String>(respondImmediately) {
107
108 @Override
109 protected String parseChallengeResponse(
110 final String challenge, final HttpContext context) throws HttpException {
111 return challenge;
112 }
113
114 @Override
115 protected boolean authenticate(
116 final String challengeResponse,
117 final URIAuthority authority,
118 final String requestUri,
119 final HttpContext context) {
120 return challengeResponse != null && challengeResponse.equals("let me pass");
121 }
122
123 @Override
124 protected String generateChallenge(
125 final String challengeResponse,
126 final URIAuthority authority,
127 final String requestUri,
128 final HttpContext context) {
129 return "who goes there?";
130 }
131
132 @Override
133 protected HttpEntity generateResponseContent(final HttpResponse unauthorized) {
134 return new StringEntity("You shall not pass!!!");
135 }
136 })
137 .setConnectionFactory(LoggingBHttpServerConnectionFactory.INSTANCE)
138 .setExceptionListener(LoggingExceptionListener.INSTANCE)
139 .setStreamListener(LoggingHttp1StreamListener.INSTANCE)
140 .create();
141 }
142
143 @Override
144 protected void after() {
145 log.debug("Shutting down test server");
146 if (server != null) {
147 try {
148 server.close(CloseMode.IMMEDIATE);
149 } catch (final Exception ignore) {
150 }
151 }
152 }
153
154 };
155
156 private HttpRequester requester;
157
158 @Rule
159 public ExternalResource clientResource = new ExternalResource() {
160
161 @Override
162 protected void before() throws Throwable {
163 log.debug("Starting up test client");
164 requester = RequesterBootstrap.bootstrap()
165 .setSocketConfig(SocketConfig.custom()
166 .setSoTimeout(TIMEOUT)
167 .build())
168 .setMaxTotal(2)
169 .setDefaultMaxPerRoute(2)
170 .setConnectionFactory(LoggingBHttpClientConnectionFactory.INSTANCE)
171 .setStreamListener(LoggingHttp1StreamListener.INSTANCE)
172 .setConnPoolListener(LoggingConnPoolListener.INSTANCE)
173 .create();
174 }
175
176 @Override
177 protected void after() {
178 log.debug("Shutting down test client");
179 if (requester != null) {
180 try {
181 requester.close(CloseMode.GRACEFUL);
182 } catch (final Exception ignore) {
183 }
184 }
185 }
186
187 };
188
189 @Test
190 public void testGetRequestAuthentication() throws Exception {
191 server.start();
192 final HttpHost target = new HttpHost("localhost", server.getLocalPort());
193 final HttpCoreContext context = HttpCoreContext.create();
194 final ClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.GET, "/stuff");
195 try (final ClassicHttpResponse response1 = requester.execute(target, request1, TIMEOUT, context)) {
196 MatcherAssert.assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_UNAUTHORIZED));
197 final String body1 = EntityUtils.toString(response1.getEntity());
198 MatcherAssert.assertThat(body1, CoreMatchers.equalTo("You shall not pass!!!"));
199 }
200 final ClassicHttpRequest request2 = new BasicClassicHttpRequest(Method.GET, "/stuff");
201 request2.setHeader(HttpHeaders.AUTHORIZATION, "let me pass");
202 try (final ClassicHttpResponse response2 = requester.execute(target, request2, TIMEOUT, context)) {
203 MatcherAssert.assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
204 final String body1 = EntityUtils.toString(response2.getEntity());
205 MatcherAssert.assertThat(body1, CoreMatchers.equalTo(""));
206 }
207 }
208
209 @Test
210 public void testPostRequestAuthentication() throws Exception {
211 server.start();
212 final HttpHost target = new HttpHost("localhost", server.getLocalPort());
213 final HttpCoreContext context = HttpCoreContext.create();
214 final Random rnd = new Random();
215 final byte[] stuff = new byte[10240];
216 for (int i = 0; i < stuff.length; i++) {
217 stuff[i] = (byte)('a' + rnd.nextInt(10));
218 }
219 final ClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.POST, "/stuff");
220 request1.setEntity(new ByteArrayEntity(stuff, ContentType.TEXT_PLAIN));
221 try (final ClassicHttpResponse response1 = requester.execute(target, request1, TIMEOUT, context)) {
222 MatcherAssert.assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_UNAUTHORIZED));
223 final String body1 = EntityUtils.toString(response1.getEntity());
224 MatcherAssert.assertThat(body1, CoreMatchers.equalTo("You shall not pass!!!"));
225 }
226 final ClassicHttpRequest request2 = new BasicClassicHttpRequest(Method.POST, "/stuff");
227 request2.setHeader(HttpHeaders.AUTHORIZATION, "let me pass");
228 request2.setEntity(new ByteArrayEntity(stuff, ContentType.TEXT_PLAIN));
229 try (final ClassicHttpResponse response2 = requester.execute(target, request2, TIMEOUT, context)) {
230 MatcherAssert.assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
231 final String body1 = EntityUtils.toString(response2.getEntity());
232 MatcherAssert.assertThat(body1, CoreMatchers.equalTo(new String(stuff, StandardCharsets.US_ASCII)));
233 }
234 }
235
236 @Test
237 public void testPostRequestAuthenticationNoExpectContinue() throws Exception {
238 server.start();
239 final HttpHost target = new HttpHost("localhost", server.getLocalPort());
240 final HttpCoreContext context = HttpCoreContext.create();
241 final Random rnd = new Random();
242 final byte[] stuff = new byte[10240];
243 for (int i = 0; i < stuff.length; i++) {
244 stuff[i] = (byte)('a' + rnd.nextInt(10));
245 }
246 final ClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.POST, "/stuff");
247 request1.setVersion(HttpVersion.HTTP_1_0);
248 request1.setEntity(new ByteArrayEntity(stuff, ContentType.TEXT_PLAIN));
249 try (final ClassicHttpResponse response1 = requester.execute(target, request1, TIMEOUT, context)) {
250 MatcherAssert.assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_UNAUTHORIZED));
251 final String body1 = EntityUtils.toString(response1.getEntity());
252 MatcherAssert.assertThat(body1, CoreMatchers.equalTo("You shall not pass!!!"));
253 }
254 final ClassicHttpRequest request2 = new BasicClassicHttpRequest(Method.POST, "/stuff");
255 request2.setHeader(HttpHeaders.AUTHORIZATION, "let me pass");
256 request2.setVersion(HttpVersion.HTTP_1_0);
257 request2.setEntity(new ByteArrayEntity(stuff, ContentType.TEXT_PLAIN));
258 try (final ClassicHttpResponse response2 = requester.execute(target, request2, TIMEOUT, context)) {
259 MatcherAssert.assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
260 final String body1 = EntityUtils.toString(response2.getEntity());
261 MatcherAssert.assertThat(body1, CoreMatchers.equalTo(new String(stuff, StandardCharsets.US_ASCII)));
262 }
263 }
264
265 }