Skip to content

Commit 9d922ad

Browse files
committed
Fix for https://github.com/sonatype/async-http-client/issues/59 (Following redirects, AHC uses the original request method).
1 parent 4f92393 commit 9d922ad

File tree

7 files changed

+350
-12
lines changed

7 files changed

+350
-12
lines changed

src/main/java/com/ning/http/client/AsyncHttpClientConfig.java

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ public class AsyncHttpClientConfig {
8383
protected boolean removeQueryParamOnRedirect;
8484
protected HostnameVerifier hostnameVerifier;
8585
protected int ioThreadMultiplier;
86+
protected boolean strict302Handling;
8687

8788
protected AsyncHttpClientConfig() {
8889
}
@@ -115,7 +116,8 @@ private AsyncHttpClientConfig(int maxTotalConnections,
115116
boolean useRawUrl,
116117
boolean removeQueryParamOnRedirect,
117118
HostnameVerifier hostnameVerifier,
118-
int ioThreadMultiplier) {
119+
int ioThreadMultiplier,
120+
boolean strict302Handling) {
119121

120122
this.maxTotalConnections = maxTotalConnections;
121123
this.maxConnectionPerHost = maxConnectionPerHost;
@@ -144,6 +146,7 @@ private AsyncHttpClientConfig(int maxTotalConnections,
144146
this.removeQueryParamOnRedirect = removeQueryParamOnRedirect;
145147
this.hostnameVerifier = hostnameVerifier;
146148
this.ioThreadMultiplier = ioThreadMultiplier;
149+
this.strict302Handling = strict302Handling;
147150

148151
if (applicationThreadPool == null) {
149152
this.applicationThreadPool = Executors.newCachedThreadPool();
@@ -456,6 +459,23 @@ public int getIoThreadMultiplier() {
456459
return ioThreadMultiplier;
457460
}
458461

462+
/**
463+
* <p>
464+
* In the case of a POST/Redirect/Get scenario where the server uses a 302
465+
* for the redirect, should AHC respond to the redirect with a GET or
466+
* whatever the original method was. Unless configured otherwise,
467+
* for a 302, AHC, will use a GET for this case.
468+
* </p>
469+
*
470+
* @return <code>true</code> if string 302 handling is to be used,
471+
* otherwise <code>false</code>.
472+
*
473+
* @since 1.7.2
474+
*/
475+
public boolean isStrict302Handling() {
476+
return strict302Handling;
477+
}
478+
459479
/**
460480
* Builder for an {@link AsyncHttpClient}
461481
*/
@@ -503,6 +523,7 @@ public Thread newThread(Runnable r) {
503523
private boolean removeQueryParamOnRedirect = true;
504524
private HostnameVerifier hostnameVerifier = new AllowAllHostnameVerifier();
505525
private int ioThreadMultiplier = 2;
526+
private boolean strict302Handling;
506527

507528
public Builder() {
508529
}
@@ -920,6 +941,21 @@ public Builder setHostnameVerifier(HostnameVerifier hostnameVerifier) {
920941
return this;
921942
}
922943

944+
/**
945+
* Configures this AHC instance to be strict in it's handling of 302 redirects
946+
* in a POST/Redirect/GET situation.
947+
*
948+
* @param strict302Handling strict handling
949+
*
950+
* @return this
951+
*
952+
* @since 1.7.2
953+
*/
954+
public Builder setStrict302Handling(final boolean strict302Handling) {
955+
this.strict302Handling = strict302Handling;
956+
return this;
957+
}
958+
923959
/**
924960
* Create a config builder with values taken from the given prototype configuration.
925961
*
@@ -961,6 +997,7 @@ public Builder(AsyncHttpClientConfig prototype) {
961997
allowSslConnectionPool = prototype.getAllowPoolingConnection();
962998
removeQueryParamOnRedirect = prototype.isRemoveQueryParamOnRedirect();
963999
hostnameVerifier = prototype.getHostnameVerifier();
1000+
strict302Handling = prototype.isStrict302Handling();
9641001
}
9651002

9661003
/**
@@ -1007,7 +1044,8 @@ public AsyncHttpClientConfig build() {
10071044
useRawUrl,
10081045
removeQueryParamOnRedirect,
10091046
hostnameVerifier,
1010-
ioThreadMultiplier);
1047+
ioThreadMultiplier,
1048+
strict302Handling);
10111049
}
10121050
}
10131051
}

src/main/java/com/ning/http/client/providers/grizzly/GrizzlyAsyncHttpProvider.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,6 +1406,7 @@ private static boolean isRedirect(final int status) {
14061406

14071407
return HttpStatus.MOVED_PERMANENTLY_301.statusMatches(status)
14081408
|| HttpStatus.FOUND_302.statusMatches(status)
1409+
|| HttpStatus.SEE_OTHER_303.statusMatches(status)
14091410
|| HttpStatus.TEMPORARY_REDIRECT_307.statusMatches(status);
14101411

14111412
}
@@ -1538,7 +1539,8 @@ public boolean handleStatus(final HttpResponsePacket responsePacket,
15381539
if (!uri.toString().equalsIgnoreCase(orig.toString())) {
15391540
requestToSend = newRequest(uri,
15401541
responsePacket,
1541-
httpTransactionContext);
1542+
httpTransactionContext,
1543+
sendAsGet(responsePacket, httpTransactionContext));
15421544
} else {
15431545
httpTransactionContext.statusHandler = null;
15441546
httpTransactionContext.invocationStatus = InvocationStatus.CONTINUE;
@@ -1585,6 +1587,14 @@ public boolean handleStatus(final HttpResponsePacket responsePacket,
15851587

15861588
// ------------------------------------------------- Private Methods
15871589

1590+
private boolean sendAsGet(final HttpResponsePacket response,
1591+
final HttpTransactionContext ctx) {
1592+
final int statusCode = response.getStatus();
1593+
return !(statusCode < 302 || statusCode > 303)
1594+
&& !(statusCode == 302
1595+
&& ctx.provider.clientConfig.isStrict302Handling());
1596+
}
1597+
15881598

15891599
private boolean switchingSchemes(final URI oldUri,
15901600
final URI newUri) {
@@ -1609,9 +1619,13 @@ private void notifySchemeSwitch(final FilterChainContext ctx,
16091619

16101620
private static Request newRequest(final URI uri,
16111621
final HttpResponsePacket response,
1612-
final HttpTransactionContext ctx) {
1622+
final HttpTransactionContext ctx,
1623+
boolean asGet) {
16131624

16141625
final RequestBuilder builder = new RequestBuilder(ctx.request);
1626+
if (asGet) {
1627+
builder.setMethod("GET");
1628+
}
16151629
builder.setUrl(uri.toString());
16161630

16171631
if (ctx.provider.clientConfig.isRemoveQueryParamOnRedirect()) {

src/main/java/com/ning/http/client/providers/grizzly/GrizzlyResponseStatus.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public GrizzlyResponseStatus(final HttpResponsePacket response,
4949

5050

5151
/**
52-
* @{inheritDoc}
52+
* {@inheritDoc}
5353
*/
5454
@Override
5555
public int getStatusCode() {
@@ -60,7 +60,7 @@ public int getStatusCode() {
6060

6161

6262
/**
63-
* @{inheritDoc}
63+
* {@inheritDoc}
6464
*/
6565
@Override
6666
public String getStatusText() {
@@ -71,7 +71,7 @@ public String getStatusText() {
7171

7272

7373
/**
74-
* @{inheritDoc}
74+
* {@inheritDoc}
7575
*/
7676
@Override
7777
public String getProtocolName() {
@@ -82,7 +82,7 @@ public String getProtocolName() {
8282

8383

8484
/**
85-
* @{inheritDoc}
85+
* {@inheritDoc}
8686
*/
8787
@Override
8888
public int getProtocolMajorVersion() {
@@ -93,7 +93,7 @@ public int getProtocolMajorVersion() {
9393

9494

9595
/**
96-
* @{inheritDoc}
96+
* {@inheritDoc}
9797
*/
9898
@Override
9999
public int getProtocolMinorVersion() {
@@ -104,7 +104,7 @@ public int getProtocolMinorVersion() {
104104

105105

106106
/**
107-
* @{inheritDoc}
107+
* {@inheritDoc}
108108
*/
109109
@Override
110110
public String getProtocolText() {

src/main/java/com/ning/http/client/providers/netty/NettyAsyncHttpProvider.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2107,7 +2107,10 @@ public Object call() throws Exception {
21072107
}
21082108

21092109
boolean redirectEnabled = request.isRedirectOverrideSet() ? request.isRedirectEnabled() : config.isRedirectEnabled();
2110-
if (redirectEnabled && (statusCode == 302 || statusCode == 301 || statusCode == 307)) {
2110+
if (redirectEnabled && (statusCode == 302
2111+
|| statusCode == 301
2112+
|| statusCode == 303
2113+
|| statusCode == 307)) {
21112114

21122115
if (future.incrementAndGetCurrentRedirectCount() < config.getMaxRedirects()) {
21132116
// We must allow 401 handling again.
@@ -2116,12 +2119,16 @@ public Object call() throws Exception {
21162119
String location = response.getHeader(HttpHeaders.Names.LOCATION);
21172120
URI uri = AsyncHttpProviderUtils.getRedirectUri(future.getURI(), location);
21182121
boolean stripQueryString = config.isRemoveQueryParamOnRedirect();
2119-
21202122
if (!uri.toString().equalsIgnoreCase(future.getURI().toString())) {
21212123
final RequestBuilder nBuilder = stripQueryString ?
21222124
new RequestBuilder(future.getRequest()).setQueryParameters(null)
21232125
: new RequestBuilder(future.getRequest());
21242126

2127+
if (!(statusCode < 302 || statusCode > 303)
2128+
&& !(statusCode == 302
2129+
&& config.isStrict302Handling())) {
2130+
nBuilder.setMethod("GET");
2131+
}
21252132
final URI initialConnectionUri = future.getURI();
21262133
final boolean initialConnectionKeepAlive = future.getKeepAlive();
21272134
future.setURI(uri);

0 commit comments

Comments
 (0)