1 package org.robolectric.shadows.httpclient;
2 
3 import static com.google.common.truth.Truth.assertThat;
4 import static java.nio.charset.StandardCharsets.UTF_8;
5 import static org.junit.Assert.assertFalse;
6 import static org.junit.Assert.assertNotNull;
7 import static org.junit.Assert.assertNull;
8 import static org.junit.Assert.assertSame;
9 import static org.junit.Assert.assertThrows;
10 import static org.junit.Assert.fail;
11 import static org.robolectric.shadows.httpclient.Shadows.shadowOf;
12 
13 import androidx.test.ext.junit.runners.AndroidJUnit4;
14 import com.google.common.io.CharStreams;
15 import java.io.BufferedReader;
16 import java.io.ByteArrayInputStream;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.InputStreamReader;
20 import java.net.URI;
21 import org.apache.http.HttpResponse;
22 import org.apache.http.client.methods.HttpGet;
23 import org.apache.http.client.methods.HttpPost;
24 import org.apache.http.client.methods.HttpUriRequest;
25 import org.apache.http.conn.ConnectTimeoutException;
26 import org.apache.http.conn.ConnectionKeepAliveStrategy;
27 import org.apache.http.impl.client.BasicResponseHandler;
28 import org.apache.http.impl.client.DefaultHttpClient;
29 import org.apache.http.impl.client.DefaultRequestDirector;
30 import org.apache.http.message.BasicHeader;
31 import org.apache.http.params.HttpConnectionParams;
32 import org.apache.http.params.HttpParams;
33 import org.junit.After;
34 import org.junit.Assert;
35 import org.junit.Before;
36 import org.junit.Test;
37 import org.junit.runner.RunWith;
38 
39 /** Tests for {@link DefaultRequestDirector} */
40 @RunWith(AndroidJUnit4.class)
41 public class ShadowDefaultRequestDirectorTest {
42 
43   private DefaultRequestDirector requestDirector;
44   private ConnectionKeepAliveStrategy connectionKeepAliveStrategy;
45 
46   @Before
setUp_EnsureStaticStateIsReset()47   public void setUp_EnsureStaticStateIsReset() {
48     FakeHttpLayer fakeHttpLayer = FakeHttp.getFakeHttpLayer();
49     assertFalse(fakeHttpLayer.hasPendingResponses());
50     assertFalse(fakeHttpLayer.hasRequestInfos());
51     assertFalse(fakeHttpLayer.hasResponseRules());
52 
53     connectionKeepAliveStrategy = (httpResponse, httpContext) -> 0;
54     requestDirector =
55         new DefaultRequestDirector(
56             null,
57             null,
58             null,
59             connectionKeepAliveStrategy,
60             null,
61             null,
62             null,
63             null,
64             null,
65             null,
66             null,
67             null);
68   }
69 
70   @After
tearDown_EnsureStaticStateIsReset()71   public void tearDown_EnsureStaticStateIsReset() {
72     FakeHttp.addPendingHttpResponse(200, "a happy response body");
73   }
74 
75   @Test
shouldGetHttpResponseFromExecute()76   public void shouldGetHttpResponseFromExecute() throws Exception {
77     FakeHttp.addPendingHttpResponse(new TestHttpResponse(200, "a happy response body"));
78     HttpResponse response = requestDirector.execute(null, new HttpGet("http://example.com"), null);
79 
80     assertNotNull(response);
81     assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200);
82     assertThat(getStringContent(response)).isEqualTo("a happy response body");
83   }
84 
85   @Test
shouldPreferPendingResponses()86   public void shouldPreferPendingResponses() throws Exception {
87     FakeHttp.addPendingHttpResponse(new TestHttpResponse(200, "a happy response body"));
88 
89     FakeHttp.addHttpResponseRule(
90         HttpGet.METHOD_NAME,
91         "http://some.uri",
92         new TestHttpResponse(200, "a cheery response body"));
93 
94     HttpResponse response = requestDirector.execute(null, new HttpGet("http://some.uri"), null);
95 
96     assertNotNull(response);
97     assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200);
98     assertThat(getStringContent(response)).isEqualTo("a happy response body");
99   }
100 
101   @Test
shouldReturnRequestsByRule()102   public void shouldReturnRequestsByRule() throws Exception {
103     FakeHttp.addHttpResponseRule(
104         HttpGet.METHOD_NAME,
105         "http://some.uri",
106         new TestHttpResponse(200, "a cheery response body"));
107 
108     HttpResponse response = requestDirector.execute(null, new HttpGet("http://some.uri"), null);
109 
110     assertNotNull(response);
111     assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200);
112     assertThat(getStringContent(response)).isEqualTo("a cheery response body");
113   }
114 
115   @Test
shouldReturnRequestsByRule_MatchingMethod()116   public void shouldReturnRequestsByRule_MatchingMethod() throws Exception {
117     FakeHttp.setDefaultHttpResponse(404, "no such page");
118     FakeHttp.addHttpResponseRule(
119         HttpPost.METHOD_NAME,
120         "http://some.uri",
121         new TestHttpResponse(200, "a cheery response body"));
122 
123     HttpResponse response = requestDirector.execute(null, new HttpGet("http://some.uri"), null);
124 
125     assertNotNull(response);
126     assertThat(response.getStatusLine().getStatusCode()).isEqualTo(404);
127   }
128 
129   @Test
shouldReturnRequestsByRule_AnyMethod()130   public void shouldReturnRequestsByRule_AnyMethod() throws Exception {
131     FakeHttp.addHttpResponseRule(
132         "http://some.uri", new TestHttpResponse(200, "a cheery response body"));
133 
134     HttpResponse getResponse = requestDirector.execute(null, new HttpGet("http://some.uri"), null);
135     assertNotNull(getResponse);
136     assertThat(getResponse.getStatusLine().getStatusCode()).isEqualTo(200);
137     assertThat(getStringContent(getResponse)).isEqualTo("a cheery response body");
138 
139     HttpResponse postResponse =
140         requestDirector.execute(null, new HttpPost("http://some.uri"), null);
141     assertNotNull(postResponse);
142     assertThat(postResponse.getStatusLine().getStatusCode()).isEqualTo(200);
143     assertThat(getStringContent(postResponse)).isEqualTo("a cheery response body");
144   }
145 
146   @Test
shouldReturnRequestsByRule_KeepsTrackOfOpenContentStreams()147   public void shouldReturnRequestsByRule_KeepsTrackOfOpenContentStreams() throws Exception {
148     TestHttpResponse testHttpResponse = new TestHttpResponse(200, "a cheery response body");
149     FakeHttp.addHttpResponseRule("http://some.uri", testHttpResponse);
150 
151     assertThat(testHttpResponse.entityContentStreamsHaveBeenClosed()).isTrue();
152 
153     HttpResponse getResponse = requestDirector.execute(null, new HttpGet("http://some.uri"), null);
154     InputStream getResponseStream = getResponse.getEntity().getContent();
155     assertThat(CharStreams.toString(new InputStreamReader(getResponseStream, UTF_8)))
156         .isEqualTo("a cheery response body");
157     assertThat(testHttpResponse.entityContentStreamsHaveBeenClosed()).isFalse();
158 
159     HttpResponse postResponse =
160         requestDirector.execute(null, new HttpPost("http://some.uri"), null);
161     InputStream postResponseStream = postResponse.getEntity().getContent();
162     assertThat(CharStreams.toString(new InputStreamReader(postResponseStream, UTF_8)))
163         .isEqualTo("a cheery response body");
164     assertThat(testHttpResponse.entityContentStreamsHaveBeenClosed()).isFalse();
165 
166     getResponseStream.close();
167     assertThat(testHttpResponse.entityContentStreamsHaveBeenClosed()).isFalse();
168 
169     postResponseStream.close();
170     assertThat(testHttpResponse.entityContentStreamsHaveBeenClosed()).isTrue();
171   }
172 
173   @Test
shouldReturnRequestsByRule_WithTextResponse()174   public void shouldReturnRequestsByRule_WithTextResponse() throws Exception {
175     FakeHttp.addHttpResponseRule("http://some.uri", "a cheery response body");
176 
177     HttpResponse response = requestDirector.execute(null, new HttpGet("http://some.uri"), null);
178 
179     assertNotNull(response);
180     assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200);
181     assertThat(getStringContent(response)).isEqualTo("a cheery response body");
182   }
183 
184   @Test
clearHttpResponseRules_shouldRemoveAllRules()185   public void clearHttpResponseRules_shouldRemoveAllRules() throws Exception {
186     FakeHttp.addHttpResponseRule("http://some.uri", "a cheery response body");
187     FakeHttp.clearHttpResponseRules();
188     FakeHttp.addHttpResponseRule("http://some.uri", "a gloomy response body");
189 
190     HttpResponse response = requestDirector.execute(null, new HttpGet("http://some.uri"), null);
191 
192     assertNotNull(response);
193     assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200);
194     assertThat(getStringContent(response)).isEqualTo("a gloomy response body");
195   }
196 
197   @Test
clearPendingHttpResponses()198   public void clearPendingHttpResponses() throws Exception {
199     FakeHttp.addPendingHttpResponse(200, "earlier");
200     FakeHttp.clearPendingHttpResponses();
201     FakeHttp.addPendingHttpResponse(500, "later");
202 
203     HttpResponse response = requestDirector.execute(null, new HttpGet("http://some.uri"), null);
204 
205     assertNotNull(response);
206     assertThat(response.getStatusLine().getStatusCode()).isEqualTo(500);
207     assertThat(getStringContent(response)).isEqualTo("later");
208   }
209 
210   @Test
shouldReturnRequestsByRule_WithCustomRequestMatcher()211   public void shouldReturnRequestsByRule_WithCustomRequestMatcher() throws Exception {
212     FakeHttp.setDefaultHttpResponse(404, "no such page");
213 
214     FakeHttp.addHttpResponseRule(
215         request -> request.getRequestLine().getUri().equals("http://matching.uri"),
216         new TestHttpResponse(200, "a cheery response body"));
217 
218     HttpResponse response = requestDirector.execute(null, new HttpGet("http://matching.uri"), null);
219     assertNotNull(response);
220     assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200);
221     assertThat(getStringContent(response)).isEqualTo("a cheery response body");
222 
223     response = requestDirector.execute(null, new HttpGet("http://non-matching.uri"), null);
224     assertNotNull(response);
225     assertThat(response.getStatusLine().getStatusCode()).isEqualTo(404);
226     assertThat(getStringContent(response)).isEqualTo("no such page");
227   }
228 
229   @Test
shouldGetHttpResponseFromExecuteSimpleApi()230   public void shouldGetHttpResponseFromExecuteSimpleApi() throws Exception {
231     FakeHttp.addPendingHttpResponse(200, "a happy response body");
232     HttpResponse response = requestDirector.execute(null, new HttpGet("http://example.com"), null);
233 
234     assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200);
235     assertThat(getStringContent(response)).isEqualTo("a happy response body");
236   }
237 
238   @Test
shouldHandleMultipleInvocations()239   public void shouldHandleMultipleInvocations() throws Exception {
240     FakeHttp.addPendingHttpResponse(200, "a happy response body");
241     FakeHttp.addPendingHttpResponse(201, "another happy response body");
242 
243     HttpResponse response1 = requestDirector.execute(null, new HttpGet("http://example.com"), null);
244     HttpResponse response2 = requestDirector.execute(null, new HttpGet("www.example.com"), null);
245 
246     assertThat(response1.getStatusLine().getStatusCode()).isEqualTo(200);
247     assertThat(getStringContent(response1)).isEqualTo("a happy response body");
248 
249     assertThat(response2.getStatusLine().getStatusCode()).isEqualTo(201);
250     assertThat(getStringContent(response2)).isEqualTo("another happy response body");
251   }
252 
253   @Test
shouldHandleMultipleInvocationsOfExecute()254   public void shouldHandleMultipleInvocationsOfExecute() throws Exception {
255     FakeHttp.addPendingHttpResponse(200, "a happy response body");
256     FakeHttp.addPendingHttpResponse(201, "another happy response body");
257 
258     requestDirector.execute(null, new HttpGet("http://example.com"), null);
259     requestDirector.execute(null, new HttpGet("www.example.com"), null);
260 
261     HttpUriRequest request1 = (HttpUriRequest) FakeHttp.getSentHttpRequest(0);
262     assertThat(request1.getMethod()).isEqualTo(HttpGet.METHOD_NAME);
263     assertThat(request1.getURI()).isEqualTo(URI.create("http://example.com"));
264 
265     HttpUriRequest request2 = (HttpUriRequest) FakeHttp.getSentHttpRequest(1);
266     assertThat(request2.getMethod()).isEqualTo(HttpGet.METHOD_NAME);
267     assertThat(request2.getURI()).isEqualTo(URI.create("www.example.com"));
268   }
269 
270   @Test
shouldRejectUnexpectedCallsToExecute()271   public void shouldRejectUnexpectedCallsToExecute() throws Exception {
272     try {
273       requestDirector.execute(null, new HttpGet("http://example.com"), null);
274       fail();
275     } catch (RuntimeException expected) {
276       assertThat(expected.getMessage())
277           .isEqualTo(
278               "Unexpected call to execute, no pending responses are available. "
279                   + "See Robolectric.addPendingResponse(). Request was: GET http://example.com");
280     }
281   }
282 
283   @Test
shouldRecordExtendedRequestData()284   public void shouldRecordExtendedRequestData() throws Exception {
285     FakeHttp.addPendingHttpResponse(200, "a happy response body");
286     HttpGet httpGet = new HttpGet("http://example.com");
287     requestDirector.execute(null, httpGet, null);
288 
289     assertSame(FakeHttp.getSentHttpRequestInfo(0).getHttpRequest(), httpGet);
290     ConnectionKeepAliveStrategy strategy =
291         shadowOf((DefaultRequestDirector) FakeHttp.getSentHttpRequestInfo(0).getRequestDirector())
292             .getConnectionKeepAliveStrategy();
293     assertSame(strategy, connectionKeepAliveStrategy);
294   }
295 
296   @Test
getNextSentHttpRequestInfo_shouldRemoveHttpRequestInfos()297   public void getNextSentHttpRequestInfo_shouldRemoveHttpRequestInfos() throws Exception {
298     FakeHttp.addPendingHttpResponse(200, "a happy response body");
299     HttpGet httpGet = new HttpGet("http://example.com");
300     requestDirector.execute(null, httpGet, null);
301 
302     assertSame(FakeHttp.getNextSentHttpRequestInfo().getHttpRequest(), httpGet);
303     assertNull(FakeHttp.getNextSentHttpRequestInfo());
304   }
305 
306   @Test
getNextSentHttpRequest_shouldRemoveHttpRequests()307   public void getNextSentHttpRequest_shouldRemoveHttpRequests() throws Exception {
308     FakeHttp.addPendingHttpResponse(200, "a happy response body");
309     HttpGet httpGet = new HttpGet("http://example.com");
310     requestDirector.execute(null, httpGet, null);
311 
312     assertSame(FakeHttp.getNextSentHttpRequest(), httpGet);
313     assertNull(FakeHttp.getNextSentHttpRequest());
314   }
315 
316   @Test
shouldSupportBasicResponseHandlerHandleResponse()317   public void shouldSupportBasicResponseHandlerHandleResponse() throws Exception {
318     FakeHttp.addPendingHttpResponse(200, "OK", new BasicHeader("Content-Type", "text/plain"));
319 
320     DefaultHttpClient client = new DefaultHttpClient();
321     HttpResponse response = client.execute(new HttpGet("http://www.nowhere.org"));
322 
323     assertThat(((HttpUriRequest) FakeHttp.getSentHttpRequest(0)).getURI())
324         .isEqualTo(URI.create("http://www.nowhere.org"));
325 
326     Assert.assertNotNull(response);
327     String responseStr = new BasicResponseHandler().handleResponse(response);
328     Assert.assertEquals("OK", responseStr);
329   }
330 
331   @Test
shouldFindLastRequestMade()332   public void shouldFindLastRequestMade() throws Exception {
333     FakeHttp.addPendingHttpResponse(200, "a happy response body");
334     FakeHttp.addPendingHttpResponse(200, "a happy response body");
335     FakeHttp.addPendingHttpResponse(200, "a happy response body");
336 
337     DefaultHttpClient client = new DefaultHttpClient();
338     client.execute(new HttpGet("http://www.first.org"));
339     client.execute(new HttpGet("http://www.second.org"));
340     client.execute(new HttpGet("http://www.third.org"));
341 
342     assertThat(((HttpUriRequest) FakeHttp.getLatestSentHttpRequest()).getURI())
343         .isEqualTo(URI.create("http://www.third.org"));
344   }
345 
346   @Test
shouldSupportConnectionTimeoutWithExceptions()347   public void shouldSupportConnectionTimeoutWithExceptions() throws Exception {
348     FakeHttp.setDefaultHttpResponse(
349         new TestHttpResponse() {
350           @Override
351           public HttpParams getParams() {
352             HttpParams httpParams = super.getParams();
353             HttpConnectionParams.setConnectionTimeout(httpParams, -1);
354             return httpParams;
355           }
356         });
357 
358     DefaultHttpClient client = new DefaultHttpClient();
359     try {
360       client.execute(new HttpGet("http://www.nowhere.org"));
361     } catch (ConnectTimeoutException x) {
362       return;
363     }
364 
365     fail("Exception should have been thrown");
366   }
367 
368   @Test
shouldSupportSocketTimeoutWithExceptions()369   public void shouldSupportSocketTimeoutWithExceptions() throws Exception {
370     FakeHttp.setDefaultHttpResponse(
371         new TestHttpResponse() {
372           @Override
373           public HttpParams getParams() {
374             HttpParams httpParams = super.getParams();
375             HttpConnectionParams.setSoTimeout(httpParams, -1);
376             return httpParams;
377           }
378         });
379 
380     DefaultHttpClient client = new DefaultHttpClient();
381     try {
382       client.execute(new HttpGet("http://www.nowhere.org"));
383     } catch (ConnectTimeoutException x) {
384       return;
385     }
386 
387     fail("Exception should have been thrown");
388   }
389 
390   @Test
shouldSupportRealHttpRequests()391   public void shouldSupportRealHttpRequests() {
392     FakeHttp.getFakeHttpLayer().interceptHttpRequests(false);
393     DefaultHttpClient client = new DefaultHttpClient();
394 
395     assertThrows(
396         IOException.class,
397         () ->
398             client.execute(new HttpGet("http://www.this-host-should-not-exist-123456790.org:999")));
399   }
400 
401   @Test
shouldSupportRealHttpRequestsAddingRequestInfo()402   public void shouldSupportRealHttpRequestsAddingRequestInfo() throws Exception {
403     FakeHttp.getFakeHttpLayer().interceptHttpRequests(false);
404     DefaultHttpClient client = new DefaultHttpClient();
405 
406     // it's really bad to depend on an external server in order to get a test pass,
407     // but this test is about making sure that we can intercept calls to external servers
408     // so, I think that in this specific case, it's appropriate...
409     client.execute(new HttpGet("http://google.com"));
410 
411     assertNotNull(FakeHttp.getFakeHttpLayer().getLastSentHttpRequestInfo());
412     assertNotNull(FakeHttp.getFakeHttpLayer().getLastHttpResponse());
413   }
414 
415   @Test
realHttpRequestsShouldMakeContentDataAvailable()416   public void realHttpRequestsShouldMakeContentDataAvailable() throws Exception {
417     FakeHttp.getFakeHttpLayer().interceptHttpRequests(false);
418     FakeHttp.getFakeHttpLayer().interceptResponseContent(true);
419     DefaultHttpClient client = new DefaultHttpClient();
420 
421     client.execute(new HttpGet("http://google.com"));
422 
423     byte[] cachedContent = FakeHttp.getFakeHttpLayer().getHttpResposeContentList().get(0);
424     assertThat(cachedContent.length).isNotEqualTo(0);
425 
426     InputStream content =
427         FakeHttp.getFakeHttpLayer().getLastHttpResponse().getEntity().getContent();
428     BufferedReader contentReader = new BufferedReader(new InputStreamReader(content, UTF_8));
429     String firstLineOfContent = contentReader.readLine();
430     assertThat(firstLineOfContent).contains("Google");
431 
432     BufferedReader cacheReader =
433         new BufferedReader(new InputStreamReader(new ByteArrayInputStream(cachedContent), UTF_8));
434     String firstLineOfCachedContent = cacheReader.readLine();
435     assertThat(firstLineOfCachedContent).isEqualTo(firstLineOfContent);
436   }
437 
438   @Test
shouldReturnResponseFromHttpResponseGenerator()439   public void shouldReturnResponseFromHttpResponseGenerator() throws Exception {
440     FakeHttp.addPendingHttpResponse(request -> new TestHttpResponse(200, "a happy response body"));
441     HttpResponse response = requestDirector.execute(null, new HttpGet("http://example.com"), null);
442 
443     assertNotNull(response);
444     assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200);
445     assertThat(getStringContent(response)).isEqualTo("a happy response body");
446   }
447 
getStringContent(HttpResponse response)448   private static String getStringContent(HttpResponse response) throws IOException {
449     return CharStreams.toString(new InputStreamReader(response.getEntity().getContent(), UTF_8));
450   }
451 }
452