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 package org.apache.hc.client5.http.impl.classic;
28
29 import java.io.ByteArrayInputStream;
30 import java.io.ByteArrayOutputStream;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.util.Collections;
34
35 import org.apache.hc.client5.http.AuthenticationStrategy;
36 import org.apache.hc.client5.http.HttpRoute;
37 import org.apache.hc.client5.http.auth.AuthExchange;
38 import org.apache.hc.client5.http.auth.AuthScope;
39 import org.apache.hc.client5.http.auth.ChallengeType;
40 import org.apache.hc.client5.http.auth.StandardAuthScheme;
41 import org.apache.hc.client5.http.classic.ExecChain;
42 import org.apache.hc.client5.http.classic.ExecRuntime;
43 import org.apache.hc.client5.http.classic.methods.HttpGet;
44 import org.apache.hc.client5.http.classic.methods.HttpPost;
45 import org.apache.hc.client5.http.entity.EntityBuilder;
46 import org.apache.hc.client5.http.impl.auth.BasicScheme;
47 import org.apache.hc.client5.http.impl.auth.CredentialsProviderBuilder;
48 import org.apache.hc.client5.http.protocol.HttpClientContext;
49 import org.apache.hc.core5.http.ClassicHttpRequest;
50 import org.apache.hc.core5.http.ClassicHttpResponse;
51 import org.apache.hc.core5.http.HttpException;
52 import org.apache.hc.core5.http.HttpHeaders;
53 import org.apache.hc.core5.http.HttpHost;
54 import org.apache.hc.core5.http.HttpResponse;
55 import org.apache.hc.core5.http.ProtocolException;
56 import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
57 import org.junit.jupiter.api.Assertions;
58 import org.junit.jupiter.api.BeforeEach;
59 import org.junit.jupiter.api.Test;
60 import org.mockito.Mock;
61 import org.mockito.Mockito;
62 import org.mockito.MockitoAnnotations;
63 import org.mockito.stubbing.Answer;
64
65 @SuppressWarnings({"static-access"})
66 public class TestProtocolExec {
67
68 @Mock
69 private AuthenticationStrategy targetAuthStrategy;
70 @Mock
71 private AuthenticationStrategy proxyAuthStrategy;
72 @Mock
73 private ExecChain chain;
74 @Mock
75 private ExecRuntime execRuntime;
76
77 private ProtocolExec protocolExec;
78 private HttpHost target;
79 private HttpHost proxy;
80
81 @BeforeEach
82 public void setup() throws Exception {
83 MockitoAnnotations.openMocks(this);
84 protocolExec = new ProtocolExec(targetAuthStrategy, proxyAuthStrategy, null, true);
85 target = new HttpHost("foo", 80);
86 proxy = new HttpHost("bar", 8888);
87 }
88
89 @Test
90 public void testUserInfoInRequestURI() throws Exception {
91 final HttpRoute route = new HttpRoute(new HttpHost("somehost", 8080));
92 final ClassicHttpRequest request = new HttpGet("http://somefella:secret@bar/test");
93 final HttpClientContext context = HttpClientContext.create();
94
95 final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
96 Assertions.assertThrows(ProtocolException.class, () -> protocolExec.execute(request, scope, chain));
97 }
98
99 @Test
100 public void testPostProcessHttpException() throws Exception {
101 final HttpRoute route = new HttpRoute(target);
102 final ClassicHttpRequest request = new HttpGet("/test");
103 final HttpClientContext context = HttpClientContext.create();
104
105 final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
106
107 Mockito.when(chain.proceed(
108 Mockito.any(),
109 Mockito.any())).thenReturn(response);
110 Mockito.doThrow(new HttpException("Ooopsie")).when(chain).proceed(Mockito.any(), Mockito.any());
111 final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
112 Assertions.assertThrows(HttpException.class, () ->
113 protocolExec.execute(request, scope, chain));
114 Mockito.verify(execRuntime).discardEndpoint();
115 }
116
117 @Test
118 public void testPostProcessIOException() throws Exception {
119 final HttpRoute route = new HttpRoute(target);
120 final ClassicHttpRequest request = new HttpGet("/test");
121 final HttpClientContext context = HttpClientContext.create();
122
123 final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
124 Mockito.when(chain.proceed(
125 Mockito.any(),
126 Mockito.any())).thenReturn(response);
127 Mockito.doThrow(new IOException("Ooopsie")).when(chain).proceed(Mockito.any(), Mockito.any());
128 final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
129 Assertions.assertThrows(IOException.class, () ->
130 protocolExec.execute(request, scope, chain));
131 Mockito.verify(execRuntime).discardEndpoint();
132 }
133
134 @Test
135 public void testPostProcessRuntimeException() throws Exception {
136 final HttpRoute route = new HttpRoute(target);
137 final ClassicHttpRequest request = new HttpGet("/test");
138 final HttpClientContext context = HttpClientContext.create();
139
140 final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
141 Mockito.when(chain.proceed(
142 Mockito.any(),
143 Mockito.any())).thenReturn(response);
144 Mockito.doThrow(new RuntimeException("Ooopsie")).when(chain).proceed(Mockito.any(), Mockito.any());
145 final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
146 Assertions.assertThrows(RuntimeException.class, () ->
147 protocolExec.execute(request, scope, chain));
148 Mockito.verify(execRuntime).discardEndpoint();
149 }
150
151 @Test
152 public void testExecRequestRetryOnAuthChallenge() throws Exception {
153 final HttpRoute route = new HttpRoute(target);
154 final HttpClientContext context = HttpClientContext.create();
155 final ClassicHttpRequest request = new HttpGet("http://foo/test");
156 final ClassicHttpResponse response1 = new BasicClassicHttpResponse(401, "Huh?");
157 response1.setHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=test");
158 final InputStream inStream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
159 response1.setEntity(EntityBuilder.create()
160 .setStream(inStream1)
161 .build());
162 final ClassicHttpResponse response2 = new BasicClassicHttpResponse(200, "OK");
163 final InputStream inStream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {2, 3, 4}));
164 response2.setEntity(EntityBuilder.create()
165 .setStream(inStream2)
166 .build());
167
168 context.setCredentialsProvider(CredentialsProviderBuilder.create()
169 .add(new AuthScope(target), "user", "pass".toCharArray())
170 .build());
171
172 Mockito.when(chain.proceed(
173 Mockito.same(request),
174 Mockito.any())).thenReturn(response1, response2);
175 Mockito.when(targetAuthStrategy.select(
176 Mockito.eq(ChallengeType.TARGET),
177 Mockito.any(),
178 Mockito.<HttpClientContext>any())).thenReturn(Collections.singletonList(new BasicScheme()));
179 Mockito.when(execRuntime.isConnectionReusable()).thenReturn(true);
180
181 final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
182 final ClassicHttpResponse finalResponse = protocolExec.execute(request, scope, chain);
183 Mockito.verify(chain, Mockito.times(2)).proceed(request, scope);
184 Mockito.verify(inStream1).close();
185 Mockito.verify(inStream2, Mockito.never()).close();
186
187 Assertions.assertNotNull(finalResponse);
188 Assertions.assertEquals(200, finalResponse.getCode());
189 }
190
191 @Test
192 public void testExecEntityEnclosingRequestRetryOnAuthChallenge() throws Exception {
193 final HttpRoute route = new HttpRoute(target);
194 final ClassicHttpRequest request = new HttpGet("http://foo/test");
195 final ClassicHttpResponse response1 = new BasicClassicHttpResponse(401, "Huh?");
196 response1.setHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=test");
197 final InputStream inStream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
198 response1.setEntity(EntityBuilder.create()
199 .setStream(inStream1)
200 .build());
201 final ClassicHttpResponse response2 = new BasicClassicHttpResponse(200, "OK");
202 final InputStream inStream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {2, 3, 4}));
203 response2.setEntity(EntityBuilder.create()
204 .setStream(inStream2)
205 .build());
206
207 final HttpClientContext context = HttpClientContext.create();
208
209 final AuthExchange authExchange = new AuthExchange();
210 authExchange.setState(AuthExchange.State.SUCCESS);
211 authExchange.select(new BasicScheme() {
212
213 @Override
214 public boolean isConnectionBased() {
215 return true;
216 }
217
218 });
219 context.setAuthExchange(target, authExchange);
220
221 context.setCredentialsProvider(CredentialsProviderBuilder.create()
222 .add(new AuthScope(target), "user", "pass".toCharArray())
223 .build());
224
225 Mockito.when(chain.proceed(
226 Mockito.same(request),
227 Mockito.any())).thenReturn(response1, response2);
228
229 Mockito.when(targetAuthStrategy.select(
230 Mockito.eq(ChallengeType.TARGET),
231 Mockito.any(),
232 Mockito.<HttpClientContext>any())).thenReturn(Collections.singletonList(new BasicScheme()));
233
234 final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
235 final ClassicHttpResponse finalResponse = protocolExec.execute(request, scope, chain);
236 Mockito.verify(chain, Mockito.times(2)).proceed(request, scope);
237 Mockito.verify(execRuntime).disconnectEndpoint();
238 Mockito.verify(inStream2, Mockito.never()).close();
239
240 Assertions.assertNotNull(finalResponse);
241 Assertions.assertEquals(200, finalResponse.getCode());
242 Assertions.assertNotNull(authExchange.getAuthScheme());
243 }
244
245 @Test
246 public void testExecEntityEnclosingRequest() throws Exception {
247 final HttpRoute route = new HttpRoute(target);
248 final HttpClientContext context = HttpClientContext.create();
249 final HttpPost request = new HttpPost("http://foo/test");
250 final InputStream inStream0 = new ByteArrayInputStream(new byte[] {1, 2, 3});
251 request.setEntity(EntityBuilder.create()
252 .setStream(inStream0)
253 .build());
254 final ClassicHttpResponse response1 = new BasicClassicHttpResponse(401, "Huh?");
255 response1.setHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=test");
256 final InputStream inStream1 = new ByteArrayInputStream(new byte[] {1, 2, 3});
257 response1.setEntity(EntityBuilder.create()
258 .setStream(inStream1)
259 .build());
260
261 context.setCredentialsProvider(CredentialsProviderBuilder.create()
262 .add(new AuthScope(target), "user", "pass".toCharArray())
263 .build());
264
265 Mockito.when(chain.proceed(
266 Mockito.same(request),
267 Mockito.any())).thenAnswer((Answer<HttpResponse>) invocationOnMock -> {
268 final Object[] args = invocationOnMock.getArguments();
269 final ClassicHttpRequest requestEE = (ClassicHttpRequest) args[0];
270 requestEE.getEntity().writeTo(new ByteArrayOutputStream());
271 return response1;
272 });
273
274 final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
275 final ClassicHttpResponse response = protocolExec.execute(request, scope, chain);
276 Assertions.assertEquals(401, response.getCode());
277 }
278
279 }