1 package org.robolectric.shadows.httpclient;
2 
3 import com.google.errorprone.annotations.InlineMe;
4 import java.io.ByteArrayInputStream;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.lang.reflect.Field;
8 import org.apache.commons.logging.Log;
9 import org.apache.commons.logging.LogFactory;
10 import org.apache.http.ConnectionReuseStrategy;
11 import org.apache.http.HttpEntity;
12 import org.apache.http.HttpException;
13 import org.apache.http.HttpHost;
14 import org.apache.http.HttpRequest;
15 import org.apache.http.HttpResponse;
16 import org.apache.http.client.AuthenticationHandler;
17 import org.apache.http.client.HttpRequestRetryHandler;
18 import org.apache.http.client.RedirectHandler;
19 import org.apache.http.client.UserTokenHandler;
20 import org.apache.http.conn.ClientConnectionManager;
21 import org.apache.http.conn.ConnectionKeepAliveStrategy;
22 import org.apache.http.conn.routing.HttpRoutePlanner;
23 import org.apache.http.entity.BasicHttpEntity;
24 import org.apache.http.entity.HttpEntityWrapper;
25 import org.apache.http.impl.client.DefaultRequestDirector;
26 import org.apache.http.params.HttpParams;
27 import org.apache.http.protocol.HttpContext;
28 import org.apache.http.protocol.HttpProcessor;
29 import org.apache.http.protocol.HttpRequestExecutor;
30 import org.robolectric.annotation.Implementation;
31 import org.robolectric.annotation.Implements;
32 import org.robolectric.annotation.RealObject;
33 import org.robolectric.annotation.Resetter;
34 import org.robolectric.util.Util;
35 
36 @SuppressWarnings({"UnusedDeclaration"})
37 @Implements(DefaultRequestDirector.class)
38 public class ShadowDefaultRequestDirector {
39   @RealObject DefaultRequestDirector realObject;
40 
41   protected Log log;
42   protected ClientConnectionManager connectionManager;
43   protected HttpRoutePlanner httpRoutePlanner;
44   protected ConnectionReuseStrategy connectionReuseStrategy;
45   protected ConnectionKeepAliveStrategy connectionKeepAliveStrategy;
46   protected HttpRequestExecutor httpRequestExecutor;
47   protected HttpProcessor httpProcessor;
48   protected HttpRequestRetryHandler httpRequestRetryHandler;
49   protected RedirectHandler redirectHandler;
50   protected AuthenticationHandler targetAuthenticationHandler;
51   protected AuthenticationHandler proxyAuthenticationHandler;
52   protected UserTokenHandler userTokenHandler;
53   protected HttpParams httpParams;
54 
55   org.robolectric.shadows.httpclient.DefaultRequestDirector redirector;
56 
57   @Implementation
__constructor__( Log log, HttpRequestExecutor requestExec, ClientConnectionManager conman, ConnectionReuseStrategy reustrat, ConnectionKeepAliveStrategy kastrat, HttpRoutePlanner rouplan, HttpProcessor httpProcessor, HttpRequestRetryHandler retryHandler, RedirectHandler redirectHandler, AuthenticationHandler targetAuthHandler, AuthenticationHandler proxyAuthHandler, UserTokenHandler userTokenHandler, HttpParams params)58   protected void __constructor__(
59       Log log,
60       HttpRequestExecutor requestExec,
61       ClientConnectionManager conman,
62       ConnectionReuseStrategy reustrat,
63       ConnectionKeepAliveStrategy kastrat,
64       HttpRoutePlanner rouplan,
65       HttpProcessor httpProcessor,
66       HttpRequestRetryHandler retryHandler,
67       RedirectHandler redirectHandler,
68       AuthenticationHandler targetAuthHandler,
69       AuthenticationHandler proxyAuthHandler,
70       UserTokenHandler userTokenHandler,
71       HttpParams params) {
72     this.log = log;
73     this.httpRequestExecutor = requestExec;
74     this.connectionManager = conman;
75     this.connectionReuseStrategy = reustrat;
76     this.connectionKeepAliveStrategy = kastrat;
77     this.httpRoutePlanner = rouplan;
78     this.httpProcessor = httpProcessor;
79     this.httpRequestRetryHandler = retryHandler;
80     this.redirectHandler = redirectHandler;
81     this.targetAuthenticationHandler = targetAuthHandler;
82     this.proxyAuthenticationHandler = proxyAuthHandler;
83     this.userTokenHandler = userTokenHandler;
84     this.httpParams = params;
85 
86     try {
87       redirector =
88           new org.robolectric.shadows.httpclient.DefaultRequestDirector(
89               log,
90               requestExec,
91               conman,
92               reustrat,
93               kastrat,
94               rouplan,
95               httpProcessor,
96               retryHandler,
97               redirectHandler,
98               targetAuthHandler,
99               proxyAuthHandler,
100               userTokenHandler,
101               params);
102     } catch (IllegalArgumentException ignored) {
103       FakeHttp.getFakeHttpLayer().interceptHttpRequests(true);
104     }
105   }
106 
107   @Implementation
__constructor__( HttpRequestExecutor requestExec, ClientConnectionManager conman, ConnectionReuseStrategy reustrat, ConnectionKeepAliveStrategy kastrat, HttpRoutePlanner rouplan, HttpProcessor httpProcessor, HttpRequestRetryHandler retryHandler, RedirectHandler redirectHandler, AuthenticationHandler targetAuthHandler, AuthenticationHandler proxyAuthHandler, UserTokenHandler userTokenHandler, HttpParams params)108   protected void __constructor__(
109       HttpRequestExecutor requestExec,
110       ClientConnectionManager conman,
111       ConnectionReuseStrategy reustrat,
112       ConnectionKeepAliveStrategy kastrat,
113       HttpRoutePlanner rouplan,
114       HttpProcessor httpProcessor,
115       HttpRequestRetryHandler retryHandler,
116       RedirectHandler redirectHandler,
117       AuthenticationHandler targetAuthHandler,
118       AuthenticationHandler proxyAuthHandler,
119       UserTokenHandler userTokenHandler,
120       HttpParams params) {
121     __constructor__(
122         LogFactory.getLog(DefaultRequestDirector.class),
123         requestExec,
124         conman,
125         reustrat,
126         kastrat,
127         rouplan,
128         httpProcessor,
129         retryHandler,
130         redirectHandler,
131         targetAuthHandler,
132         proxyAuthHandler,
133         userTokenHandler,
134         params);
135   }
136 
137   /**
138    * Get the sent {@link HttpRequest} for the given index.
139    *
140    * @param index The index
141    * @deprecated Use {@link FakeHttp#getSentHttpRequestInfo(int)} instead. This method will be
142    *     removed in Robolectric 4.13.
143    * @return HttpRequest
144    */
145   @Deprecated
146   @InlineMe(
147       replacement = "FakeHttp.getFakeHttpLayer().getSentHttpRequestInfo(index).getHttpRequest()",
148       imports = "org.robolectric.shadows.httpclient.FakeHttp")
getSentHttpRequest(int index)149   public static HttpRequest getSentHttpRequest(int index) {
150     return FakeHttp.getFakeHttpLayer().getSentHttpRequestInfo(index).getHttpRequest();
151   }
152 
getLatestSentHttpRequest()153   public static HttpRequest getLatestSentHttpRequest() {
154     return getLatestSentHttpRequestInfo().getHttpRequest();
155   }
156 
getLatestSentHttpRequestInfo()157   public static HttpRequestInfo getLatestSentHttpRequestInfo() {
158     int requestCount = FakeHttp.getFakeHttpLayer().getSentHttpRequestInfos().size();
159     return FakeHttp.getFakeHttpLayer().getSentHttpRequestInfo(requestCount - 1);
160   }
161 
162   /**
163    * Get the sent {@link HttpRequestInfo} for the given index.
164    *
165    * @param index The index
166    * @deprecated Use {@link FakeHttp#getSentHttpRequest(int)} instead. This method will be removed
167    *     in Robolectric 4.13.
168    * @return HttpRequestInfo
169    */
170   @Deprecated
171   @InlineMe(
172       replacement = "FakeHttp.getFakeHttpLayer().getSentHttpRequestInfo(index)",
173       imports = "org.robolectric.shadows.httpclient.FakeHttp")
getSentHttpRequestInfo(int index)174   public static HttpRequestInfo getSentHttpRequestInfo(int index) {
175     return FakeHttp.getFakeHttpLayer().getSentHttpRequestInfo(index);
176   }
177 
178   @Implementation
execute( HttpHost httpHost, HttpRequest httpRequest, HttpContext httpContext)179   protected HttpResponse execute(
180       HttpHost httpHost, HttpRequest httpRequest, HttpContext httpContext)
181       throws HttpException, IOException {
182     if (FakeHttp.getFakeHttpLayer().isInterceptingHttpRequests()) {
183       return FakeHttp.getFakeHttpLayer()
184           .emulateRequest(httpHost, httpRequest, httpContext, realObject);
185     } else {
186       FakeHttp.getFakeHttpLayer()
187           .addRequestInfo(new HttpRequestInfo(httpRequest, httpHost, httpContext, redirector));
188       HttpResponse response = redirector.execute(httpHost, httpRequest, httpContext);
189 
190       if (FakeHttp.getFakeHttpLayer().isInterceptingResponseContent()) {
191         interceptResponseContent(response);
192       }
193 
194       FakeHttp.getFakeHttpLayer().addHttpResponse(response);
195       return response;
196     }
197   }
198 
getLog()199   public Log getLog() {
200     return log;
201   }
202 
getConnectionManager()203   public ClientConnectionManager getConnectionManager() {
204     return connectionManager;
205   }
206 
getHttpRoutePlanner()207   public HttpRoutePlanner getHttpRoutePlanner() {
208     return httpRoutePlanner;
209   }
210 
getConnectionReuseStrategy()211   public ConnectionReuseStrategy getConnectionReuseStrategy() {
212     return connectionReuseStrategy;
213   }
214 
getConnectionKeepAliveStrategy()215   public ConnectionKeepAliveStrategy getConnectionKeepAliveStrategy() {
216     return connectionKeepAliveStrategy;
217   }
218 
getHttpRequestExecutor()219   public HttpRequestExecutor getHttpRequestExecutor() {
220     return httpRequestExecutor;
221   }
222 
getHttpProcessor()223   public HttpProcessor getHttpProcessor() {
224     return httpProcessor;
225   }
226 
getHttpRequestRetryHandler()227   public HttpRequestRetryHandler getHttpRequestRetryHandler() {
228     return httpRequestRetryHandler;
229   }
230 
getRedirectHandler()231   public RedirectHandler getRedirectHandler() {
232     return redirectHandler;
233   }
234 
getTargetAuthenticationHandler()235   public AuthenticationHandler getTargetAuthenticationHandler() {
236     return targetAuthenticationHandler;
237   }
238 
getProxyAuthenticationHandler()239   public AuthenticationHandler getProxyAuthenticationHandler() {
240     return proxyAuthenticationHandler;
241   }
242 
getUserTokenHandler()243   public UserTokenHandler getUserTokenHandler() {
244     return userTokenHandler;
245   }
246 
getHttpParams()247   public HttpParams getHttpParams() {
248     return httpParams;
249   }
250 
251   @Resetter
reset()252   public static void reset() {
253     FakeHttp.reset();
254   }
255 
interceptResponseContent(HttpResponse response)256   private void interceptResponseContent(HttpResponse response) {
257     HttpEntity entity = response.getEntity();
258     if (entity instanceof HttpEntityWrapper) {
259       HttpEntityWrapper entityWrapper = (HttpEntityWrapper) entity;
260       try {
261         Field wrappedEntity = HttpEntityWrapper.class.getDeclaredField("wrappedEntity");
262         wrappedEntity.setAccessible(true);
263         entity = (HttpEntity) wrappedEntity.get(entityWrapper);
264       } catch (Exception e) {
265         // fail to record
266       }
267     }
268     if (entity instanceof BasicHttpEntity) {
269       BasicHttpEntity basicEntity = (BasicHttpEntity) entity;
270       try {
271         Field contentField = BasicHttpEntity.class.getDeclaredField("content");
272         contentField.setAccessible(true);
273         InputStream content = (InputStream) contentField.get(basicEntity);
274 
275         byte[] buffer = Util.readBytes(content);
276 
277         FakeHttp.getFakeHttpLayer().addHttpResponseContent(buffer);
278         contentField.set(basicEntity, new ByteArrayInputStream(buffer));
279       } catch (Exception e) {
280         // fail to record
281       }
282     }
283   }
284 }
285