Properly set the request method in HTTPS connections

This commit is contained in:
Ignasi Barrera 2013-12-12 16:25:16 +01:00 committed by Everett Toews
parent 828d8790c2
commit cec0981bb9
1 changed files with 52 additions and 13 deletions

View File

@ -78,7 +78,6 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
private final Supplier<SSLContext> untrustedSSLContextProvider; private final Supplier<SSLContext> untrustedSSLContextProvider;
private final Function<URI, Proxy> proxyForURI; private final Function<URI, Proxy> proxyForURI;
private final HostnameVerifier verifier; private final HostnameVerifier verifier;
private final Field methodField;
@Inject(optional = true) @Inject(optional = true)
Supplier<SSLContext> sslContextSupplier; Supplier<SSLContext> sslContextSupplier;
@ -95,8 +94,6 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
this.untrustedSSLContextProvider = checkNotNull(untrustedSSLContextProvider, "untrustedSSLContextProvider"); this.untrustedSSLContextProvider = checkNotNull(untrustedSSLContextProvider, "untrustedSSLContextProvider");
this.verifier = checkNotNull(verifier, "verifier"); this.verifier = checkNotNull(verifier, "verifier");
this.proxyForURI = checkNotNull(proxyForURI, "proxyForURI"); this.proxyForURI = checkNotNull(proxyForURI, "proxyForURI");
this.methodField = HttpURLConnection.class.getDeclaredField("method");
this.methodField.setAccessible(true);
} }
@Override @Override
@ -174,16 +171,8 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
// ex. Caused by: java.io.IOException: HTTPS hostname wrong: should be // ex. Caused by: java.io.IOException: HTTPS hostname wrong: should be
// <adriancole.s3int0.s3-external-3.amazonaws.com> // <adriancole.s3int0.s3-external-3.amazonaws.com>
connection.setInstanceFollowRedirects(false); connection.setInstanceFollowRedirects(false);
try {
connection.setRequestMethod(request.getMethod()); setRequestMethodBypassingJREMethodLimitation(connection, request.getMethod());
} catch (ProtocolException e) {
try {
methodField.set(connection, request.getMethod());
} catch (IllegalAccessException e1) {
logger.error(e, "could not set request method: ", request.getMethod());
propagate(e1);
}
}
for (Map.Entry<String, String> entry : request.getHeaders().entries()) { for (Map.Entry<String, String> entry : request.getHeaders().entries()) {
connection.setRequestProperty(entry.getKey(), entry.getValue()); connection.setRequestProperty(entry.getKey(), entry.getValue());
@ -227,6 +216,56 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
return connection; return connection;
} }
/**
* Workaround for a bug in <code>HttpURLConnection.setRequestMethod(String)</code>
* The implementation of Sun Microsystems is throwing a <code>ProtocolException</code>
* when the method is other than the HTTP/1.1 default methods. So
* to use PATCH and others, we must apply this workaround.
*
* See issue http://java.net/jira/browse/JERSEY-639
*/
private void setRequestMethodBypassingJREMethodLimitation(final HttpURLConnection httpURLConnection, final String method) {
try {
httpURLConnection.setRequestMethod(method);
// If the JRE does not support the given method, set it using reflection
} catch (final ProtocolException pe) {
Class<?> connectionClass = httpURLConnection.getClass();
Field delegateField = null;
try {
// SSL connections may have the HttpURLConnection wrapped inside
delegateField = connectionClass.getDeclaredField("delegate");
delegateField.setAccessible(true);
HttpURLConnection delegateConnection = (HttpURLConnection) delegateField.get(httpURLConnection);
setRequestMethodBypassingJREMethodLimitation(delegateConnection, method);
} catch (NoSuchFieldException e) {
// Ignore for now, keep going
} catch (IllegalArgumentException e) {
logger.error(e, "could not set request method: ", method);
propagate(e);
} catch (IllegalAccessException e) {
logger.error(e, "could not set request method: ", method);
propagate(e);
}
try {
Field methodField = null;
while (connectionClass != null) {
try {
methodField = connectionClass.getDeclaredField("method");
} catch (NoSuchFieldException e) {
connectionClass = connectionClass.getSuperclass();
continue;
}
methodField.setAccessible(true);
methodField.set(httpURLConnection, method);
break;
}
} catch (final Exception e) {
logger.error(e, "could not set request method: ", method);
propagate(e);
}
}
}
protected void writeNothing(HttpURLConnection connection) { protected void writeNothing(HttpURLConnection connection) {
if (!HttpRequest.NON_PAYLOAD_METHODS.contains(connection.getRequestMethod())) { if (!HttpRequest.NON_PAYLOAD_METHODS.contains(connection.getRequestMethod())) {
connection.setRequestProperty(CONTENT_LENGTH, "0"); connection.setRequestProperty(CONTENT_LENGTH, "0");