Skip to content

Commit 0e6d7ff

Browse files
committed
Added extended control over redirect behavior
1 parent 0212639 commit 0e6d7ff

File tree

4 files changed

+190
-10
lines changed

4 files changed

+190
-10
lines changed

library/src/main/java/com/loopj/android/http/AsyncHttpClient.java

+41-9
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,15 @@
3939
import org.apache.http.client.CookieStore;
4040
import org.apache.http.client.CredentialsProvider;
4141
import org.apache.http.client.HttpClient;
42+
import org.apache.http.client.RedirectHandler;
4243
import org.apache.http.client.methods.HttpDelete;
4344
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
4445
import org.apache.http.client.methods.HttpGet;
4546
import org.apache.http.client.methods.HttpHead;
4647
import org.apache.http.client.methods.HttpPost;
4748
import org.apache.http.client.methods.HttpPut;
4849
import org.apache.http.client.methods.HttpUriRequest;
50+
import org.apache.http.client.params.ClientPNames;
4951
import org.apache.http.client.protocol.ClientContext;
5052
import org.apache.http.conn.params.ConnManagerParams;
5153
import org.apache.http.conn.params.ConnPerRouteBean;
@@ -57,7 +59,6 @@
5759
import org.apache.http.entity.HttpEntityWrapper;
5860
import org.apache.http.impl.auth.BasicScheme;
5961
import org.apache.http.impl.client.DefaultHttpClient;
60-
import org.apache.http.impl.client.DefaultRedirectHandler;
6162
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
6263
import org.apache.http.params.BasicHttpParams;
6364
import org.apache.http.params.HttpConnectionParams;
@@ -217,8 +218,8 @@ public AsyncHttpClient(SchemeRegistry schemeRegistry) {
217218
ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(httpParams, schemeRegistry);
218219

219220
threadPool = getDefaultThreadPool();
220-
requestMap = new WeakHashMap();
221-
clientHeaderMap = new HashMap();
221+
requestMap = new WeakHashMap<Context, List<RequestHandle>>();
222+
clientHeaderMap = new HashMap<String, String>();
222223

223224
httpContext = new SyncBasicHttpContext(new BasicHttpContext());
224225
httpClient = new DefaultHttpClient(cm, httpParams);
@@ -359,16 +360,47 @@ protected ExecutorService getDefaultThreadPool() {
359360
/**
360361
* Simple interface method, to enable or disable redirects. If you set manually RedirectHandler
361362
* on underlying HttpClient, effects of this method will be canceled.
363+
* <p/>
364+
* Default setting is to disallow redirects.
362365
*
366+
* @param enableRedirects boolean
367+
* @param enableRelativeRedirects boolean
368+
* @param enableCircularRedirects boolean
369+
*/
370+
public void setEnableRedirects(final boolean enableRedirects, final boolean enableRelativeRedirects, final boolean enableCircularRedirects) {
371+
httpClient.getParams().setBooleanParameter(ClientPNames.REJECT_RELATIVE_REDIRECT, !enableRelativeRedirects);
372+
httpClient.getParams().setBooleanParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, enableCircularRedirects);
373+
httpClient.setRedirectHandler(new MyRedirectHandler(enableRedirects));
374+
}
375+
376+
/**
377+
* Circular redirects are enabled by default
378+
*
379+
* @param enableRedirects boolean
380+
* @param enableRelativeRedirects boolean
381+
* @see #setEnableRedirects(boolean, boolean, boolean)
382+
*/
383+
public void setEnableRedirects(final boolean enableRedirects, final boolean enableRelativeRedirects) {
384+
setEnableRedirects(enableRedirects, enableRelativeRedirects, true);
385+
}
386+
387+
/**
363388
* @param enableRedirects boolean
389+
* @see #setEnableRedirects(boolean, boolean, boolean)
364390
*/
365391
public void setEnableRedirects(final boolean enableRedirects) {
366-
httpClient.setRedirectHandler(new DefaultRedirectHandler() {
367-
@Override
368-
public boolean isRedirectRequested(HttpResponse response, HttpContext context) {
369-
return enableRedirects;
370-
}
371-
});
392+
setEnableRedirects(enableRedirects, enableRedirects, enableRedirects);
393+
}
394+
395+
/**
396+
* Allows you to set custom RedirectHandler implementation, if the default provided doesn't suit
397+
* your needs
398+
*
399+
* @param customRedirectHandler RedirectHandler instance
400+
* @see com.loopj.android.http.MyRedirectHandler
401+
*/
402+
public void setRedirectHandler(final RedirectHandler customRedirectHandler) {
403+
httpClient.setRedirectHandler(customRedirectHandler);
372404
}
373405

374406
/**

library/src/main/java/com/loopj/android/http/AsyncHttpResponseHandler.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ public AsyncHttpResponseHandler() {
189189
* @param totalSize total size of file
190190
*/
191191
public void onProgress(int bytesWritten, int totalSize) {
192-
Log.v(LOG_TAG, String.format("Progress %d from %d (%2.0f%%)", bytesWritten, totalSize, (totalSize > 0) ? (bytesWritten*1.0 / totalSize) * 100 : -1));
192+
Log.v(LOG_TAG, String.format("Progress %d from %d (%2.0f%%)", bytesWritten, totalSize, (totalSize > 0) ? (bytesWritten * 1.0 / totalSize) * 100 : -1));
193193
}
194194

195195
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package com.loopj.android.http;
2+
3+
import org.apache.http.Header;
4+
import org.apache.http.HttpHost;
5+
import org.apache.http.HttpRequest;
6+
import org.apache.http.HttpResponse;
7+
import org.apache.http.HttpStatus;
8+
import org.apache.http.ProtocolException;
9+
import org.apache.http.client.CircularRedirectException;
10+
import org.apache.http.client.params.ClientPNames;
11+
import org.apache.http.client.utils.URIUtils;
12+
import org.apache.http.impl.client.DefaultRedirectHandler;
13+
import org.apache.http.impl.client.RedirectLocations;
14+
import org.apache.http.params.HttpParams;
15+
import org.apache.http.protocol.ExecutionContext;
16+
import org.apache.http.protocol.HttpContext;
17+
18+
import java.net.URI;
19+
import java.net.URISyntaxException;
20+
21+
// taken from: https://stackoverflow.com/questions/3420767/httpclient-redirecting-to-url-with-spaces-throwing-exception
22+
class MyRedirectHandler extends DefaultRedirectHandler {
23+
24+
private static final String REDIRECT_LOCATIONS = "http.protocol.redirect-locations";
25+
private final boolean enableRedirects;
26+
27+
public MyRedirectHandler(final boolean allowRedirects) {
28+
super();
29+
this.enableRedirects = allowRedirects;
30+
}
31+
32+
public boolean isRedirectRequested(
33+
final HttpResponse response,
34+
final HttpContext context) {
35+
if (!enableRedirects) {
36+
return false;
37+
}
38+
if (response == null) {
39+
throw new IllegalArgumentException("HTTP response may not be null");
40+
}
41+
int statusCode = response.getStatusLine().getStatusCode();
42+
switch (statusCode) {
43+
case HttpStatus.SC_MOVED_TEMPORARILY:
44+
case HttpStatus.SC_MOVED_PERMANENTLY:
45+
case HttpStatus.SC_SEE_OTHER:
46+
case HttpStatus.SC_TEMPORARY_REDIRECT:
47+
return true;
48+
default:
49+
return false;
50+
} //end of switch
51+
}
52+
53+
public URI getLocationURI(
54+
final HttpResponse response,
55+
final HttpContext context) throws ProtocolException {
56+
if (response == null) {
57+
throw new IllegalArgumentException("HTTP response may not be null");
58+
}
59+
//get the location header to find out where to redirect to
60+
Header locationHeader = response.getFirstHeader("location");
61+
if (locationHeader == null) {
62+
// got a redirect response, but no location header
63+
throw new ProtocolException(
64+
"Received redirect response " + response.getStatusLine()
65+
+ " but no location header"
66+
);
67+
}
68+
//HERE IS THE MODIFIED LINE OF CODE
69+
String location = locationHeader.getValue().replaceAll(" ", "%20");
70+
71+
URI uri;
72+
try {
73+
uri = new URI(location);
74+
} catch (URISyntaxException ex) {
75+
throw new ProtocolException("Invalid redirect URI: " + location, ex);
76+
}
77+
78+
HttpParams params = response.getParams();
79+
// rfc2616 demands the location value be a complete URI
80+
// Location = "Location" ":" absoluteURI
81+
if (!uri.isAbsolute()) {
82+
if (params.isParameterTrue(ClientPNames.REJECT_RELATIVE_REDIRECT)) {
83+
throw new ProtocolException("Relative redirect location '"
84+
+ uri + "' not allowed");
85+
}
86+
// Adjust location URI
87+
HttpHost target = (HttpHost) context.getAttribute(
88+
ExecutionContext.HTTP_TARGET_HOST);
89+
if (target == null) {
90+
throw new IllegalStateException("Target host not available " +
91+
"in the HTTP context");
92+
}
93+
94+
HttpRequest request = (HttpRequest) context.getAttribute(
95+
ExecutionContext.HTTP_REQUEST);
96+
97+
try {
98+
URI requestURI = new URI(request.getRequestLine().getUri());
99+
URI absoluteRequestURI = URIUtils.rewriteURI(requestURI, target, true);
100+
uri = URIUtils.resolve(absoluteRequestURI, uri);
101+
} catch (URISyntaxException ex) {
102+
throw new ProtocolException(ex.getMessage(), ex);
103+
}
104+
}
105+
106+
if (params.isParameterFalse(ClientPNames.ALLOW_CIRCULAR_REDIRECTS)) {
107+
108+
RedirectLocations redirectLocations = (RedirectLocations) context.getAttribute(
109+
REDIRECT_LOCATIONS);
110+
111+
if (redirectLocations == null) {
112+
redirectLocations = new RedirectLocations();
113+
context.setAttribute(REDIRECT_LOCATIONS, redirectLocations);
114+
}
115+
116+
URI redirectURI;
117+
if (uri.getFragment() != null) {
118+
try {
119+
HttpHost target = new HttpHost(
120+
uri.getHost(),
121+
uri.getPort(),
122+
uri.getScheme());
123+
redirectURI = URIUtils.rewriteURI(uri, target, true);
124+
} catch (URISyntaxException ex) {
125+
throw new ProtocolException(ex.getMessage(), ex);
126+
}
127+
} else {
128+
redirectURI = uri;
129+
}
130+
131+
if (redirectLocations.contains(redirectURI)) {
132+
throw new CircularRedirectException("Circular redirect to '" +
133+
redirectURI + "'");
134+
} else {
135+
redirectLocations.add(redirectURI);
136+
}
137+
}
138+
139+
return uri;
140+
}
141+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.loopj.android.http.sample;
2+
3+
/**
4+
* Created by msebera on 21/04/14.
5+
*/
6+
public class Redirect302Sample {
7+
}

0 commit comments

Comments
 (0)